PHP Extensions Made Eldrich: Hello, World!

This is part 2 of a 4-part tutorial on writing PHP extensions:

  1. Setting Up PHP – compiling PHP for extension development
  2. Hello, world! – your first extension
  3. Working with the API – the PHP C API
  4. Classes – creating PHP objects in C

First we need to think of a name for our extension. I’ve been reading some H.P. Lovecraft, so let’s call it “rlyeh”.

For our first extension, we’ll create a new function, cthulhu(). When we call cthulhu() (tee hee), PHP will print “In his house at R’lyeh dead Cthulhu waits dreaming.”

Cheat Sheet

If you don’t want to copy/paste all of the code, you can clone the Github repo for this tutorial and check out sections as you go.

$ git clone git://github.com/kchodorow/rlyeh.git

This part of the tutorial (Hello, world!) is the master branch. Stating in part 3, each “unit” has a branch: <branchname> at the beginning of the section. You can checkout this branch if you want to see the code example in context.

For example, if you see branch: oop, you’d do:

$ git checkout -b oop origin/oop

Then you can compare what you’re doing to the “ideal” example code.

Setting Up

Create a directory for your PHP extension, named “rlyeh”. This is where all of the source code for your extension will live.

$ mkdir rlyeh
$ cd rlyeh

A PHP extension consists of at least three files:

  1. “config.m4″, which contains compilation instructions for PHP
  2. “php_extname.c”: source code
  3. “php_extname.h”: a header file

Creating a config.m4 file is wholly lacking in interest, so just cut/paste the one below.

dnl lines starting with "dnl" are comments

PHP_ARG_ENABLE(rlyeh, whether to enable Rlyeh extension, [  --enable-rlyeh   Enable Rlyeh extension])

if test "$PHP_RLYEH" != "no"; then

  dnl this defines the extension
  PHP_NEW_EXTENSION(rlyeh, php_rlyeh.c, $ext_shared)

  dnl this is boilerplate to make the extension work on OS X
  case $build_os in
  darwin1*.*.*)
    AC_MSG_CHECKING([whether to compile for recent osx architectures])
    CFLAGS="$CFLAGS -arch i386 -arch x86_64 -mmacosx-version-min=10.5"
    AC_MSG_RESULT([yes])
    ;;
  darwin*)
    AC_MSG_CHECKING([whether to compile for every osx architecture ever])
    CFLAGS="$CFLAGS -arch i386 -arch x86_64 -arch ppc -arch ppc64"
    AC_MSG_RESULT([yes])
    ;;
  esac

fi

If you want to call your extension something else, global replace “rlyeh” with your extension’s name.

Now for the actual extension: create a file called php_rlyeh.c with the following content:

// include PHP API
#include <php.h>
 
// header file we'll create below
#include "php_rlyeh.h"
 
// define the function(s) we want to add
zend_function_entry rlyeh_functions[] = {
  PHP_FE(cthulhu, NULL)
  { NULL, NULL, NULL }
};
 
// "rlyeh_functions" refers to the struct defined above
// we'll be filling in more of this later: you can use this to specify
// globals, php.ini info, startup and teardown functions, etc.
zend_module_entry rlyeh_module_entry = {
  STANDARD_MODULE_HEADER,
  PHP_RLYEH_EXTNAME,
  rlyeh_functions,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  PHP_RLYEH_VERSION,
  STANDARD_MODULE_PROPERTIES
};
 
// install module
ZEND_GET_MODULE(rlyeh)
 
// actual non-template code!
PHP_FUNCTION(cthulhu) {
    // php_printf is PHP's version of printf, it's essentially "echo" from C
    php_printf("In his house at R'lyeh dead Cthulhu waits dreaming.\n");
}

That’s a whole lotta template, but it’ll make more sense as you go along.

Learning PHP extension programming is sort of like learning Java as your first programming language: “type ‘public static void main’.” “Why? What does that even mean?” “It doesn’t matter, you’ll learn about it later.”

You also have to make a header file, to declare the cthulhu function as well as the two extension info macros used in php_rlyeh.c (PHP_RLYEH_EXTNAME and PHP_RLYEH_VERSION).

Create a new file, php_rlyeh.h, and add a couple of lines:

 
#define PHP_RLYEH_EXTNAME "rlyeh"
#define PHP_RLYEH_VERSION "0.01"
 
PHP_FUNCTION(cthulhu);

You can change the version whenever you do a new release. It can be any string. It’s displayed when you do:

$ php --ri rlyeh

(once the extension is installed).

Speaking of, now all that’s left is to compile and install. Make sure that your custom-compiled-PHP is first in your PATH. If it isn’t, put it there before doing the rest of the install.

$ echo $PATH
$PHPDIR/install-debug-zts/bin:/usr/local/bin:/usr/bin
$ phpize
Configuring for:
PHP Api Version:         20090626
Zend Module Api No:      20090626
Zend Extension Api No:   220090626
$
$ ./configure
# lots of checks...
$
$ make
# compile...

Build complete.
Don't forget to run 'make test'.

$ make install
$
Installing shared extensions:     $PHPDIR/install-debug-zts/lib/php/extensions/debug-zts-20090626/

Now, add your extension to your php.ini file. PHP is probably expecting a php.ini file in the lib/ subdirectory of your install directory ($PHPDIR/install-debug-zts/lib/php.ini). It probably doesn’t exist yet, so create a new php.ini file with one line:

extension=rlyeh.so

Now you should be able to use your function from PHP without importing, loading, or requiring anything. Do:

$ php -r 'cthulhu();'
In his house at R'lyeh dead Cthulhu waits dreaming.

Your first PHP extension is working!

Next up: a deep dive into the PHP API.

  • Huarong

    $ php -r ‘cthulhu();’
    Finally I got segment fault….How to debug it ?

  • Huarong

    I  change “STANDARD_MODULE_PROPERTIES_EX” to “STANDARD_MODULE_PROPERTIES”, and it works! no segment fault !

    In this article, http://devzone.zend.com/article/1021 , they also use STANDARD_MODULE_PROPERTIES.

  • Anonymous

    It depends on what version of PHP you are using, sounds like you’re not using 5.3.  You’re going to have problems with other parts of the tutorial, too, if you’re using an earlier version.

  • Anonymous

    It’s handy to get familiar with GDB if you’re going to do C development.  For the lightening-est of quickstarts, run “gdb –args php -r ‘cthulhu();'” and then “r” to run your program at the gdb> prompt.  It’ll print SEGV and stop when it segfaults, type “bt” to see what line it segfaulted on.

  • Huarong

    I am using php 5.3.7
    [root@localhost rlyeh]# /home/modify/php537/install-debug-zts/bin/php –versionPHP 5.3.7 (cli) (built: Aug 19 2011 11:18:35) (DEBUG)Copyright (c) 1997-2011 The PHP GroupZend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies

    =-======================================

    (gdb) r
    Starting program: /home/modify/php537/install-debug-zts/bin/php -r cthulhu()
    [Thread debugging using libthread_db enabled]
    [New Thread 0xb7f206c0 (LWP 3242)]

    Program received signal SIGSEGV, Segmentation fault.
    0x083233f2 in php_load_extension (filename=0xb7ee10e4 “rlyeh.so”, type=1, start_now=0, tsrm_ls=0xa14e050) at /home/modify/php537/src/ext/standard/dl.c:226
    226             if(strcmp(module_entry->build_id, ZEND_MODULE_BUILD_ID)) {

    (gdb) bt#0  0x083233f2 in php_load_extension (filename=0xb7ee10e4 “rlyeh.so”, type=1, start_now=0, tsrm_ls=0xa14e050) at /home/modify/php537/src/ext/standard/dl.c:226#1  0x083bf777 in php_load_php_extension_cb (arg=0xa159c10, tsrm_ls=0xa14e050) at /home/modify/php537/src/main/php_ini.c:351#2  0x08432b80 in zend_llist_apply (l=0x880511c, func=0x83bf74d , tsrm_ls=0xa14e050) at /home/modify/php537/src/Zend/zend_llist.c:193#3  0x083c074d in php_ini_register_extensions (tsrm_ls=0xa14e050) at /home/modify/php537/src/main/php_ini.c:751#4  0x083b7ca0 in php_module_startup (sf=0x87f0f00, additional_modules=0x0, num_additional_modules=0) at /home/modify/php537/src/main/main.c:2041#5  0x0852268c in php_cli_startup (sapi_module=0x87f0f00) at /home/modify/php537/src/sapi/cli/php_cli.c:398#6  0x08523421 in main (argc=3, argv=0xbfc5da24) at /home/modify/php537/src/sapi/cli/php_cli.c:770

  • Huarong

    I add some debug code to ext/standard/dl.c:
    php_printf(“module_entry_name:%sn”, module_entry->name);
    php_printf(“php Module compiled with build ID =%sn”, module_entry->build_id);
    php_printf(“ZEND_MODULE_BUILD_ID: %sn”, ZEND_MODULE_BUILD_ID);
    before 
    if(strcmp(module_entry->build_id, ZEND_MODULE_BUILD_ID)) {

    make && make install

    [root@localhost rlyeh]# /home/modify/php537/install-debug-zts/bin/php –version                        
    module_entry_name:rlyeh
    php Module compiled with build ID =(null)
    ZEND_MODULE_BUILD_ID: API20090626,NTS,debug

    module_entry->build_id   is null ……

  • Anonymous

    This means that your zend_module_entry isn’t being set up correctly.  Take a look at the struct’s definition (https://github.com/php/php-src/blob/master/Zend/zend_modules.h#L73) and make sure you’re initializing all of the fields!  build_id is the last field in the struct, so you’re probably missing an earlier field.  

    STANDARD_MODULE_PROPERTIES contains STANDARD_MODULE_PROPERTIES_EX, plus a couple of other fields.  Again, take a look at the file on Github I linked to.  It’s basically a way of automatically populating most of the fields of zend_module_entry, but you have to make sure you have the right number of fields before it.  Also, pay attention to compiler warnings, those can tip you off if something is misaligned and being assigned to the wrong type.

  • Kevin

    I have the same problem with php 5.3.6 on Ubuntu 10.04 LTS. Taking your example code gives me a segfault on the strcmp because module_entry->build_id == NULL whereas replacing with STANDARD_MODULE_PROPERTIES works fine.

    Just for info, if you generate an extension with ./ext_skel, it uses the same number of fields as your code for initializing the struct, but uses STANDARD_MODULE_PROPERTIES.

    Anyway, thanks for the articles, I have two c++ functions I need to publish through a webservice by Friday, and hopefully with this, I think I’m going to be able to make it on time.

  • Anonymous

    Aha! I’ve found the problem.  In the “cheat sheet” repository the code is correct, but I copied it incorrectly to the example code in the post!  I’ve fixed it now, so it should work better for people in the future.  Sorry guys!

  • Shabeer Ali M

    Nice tutorial.. is there any option to create php extension for windows(.dll) using linux ? if not what i have to do ? or is any option to convert the .so file to .dll .. ?

  • Anonymous

    Thanks!  You can make a .dll, but you have to compile it on Windows.  Check out https://wiki.php.net/internals/windows/stepbystepbuild for info on building and extension with Windows. 

  • am

    where should ‘rlyeh’ dir be created? or it doesn’t matter?

  • Anonymous

    Anywhere you want is fine.

  • Adi_mutu06

    thanks kristina1 !

  • am

    what is the role of phpize?

  • Anonymous

    It generates the configure script.  There’s a lot of standard boilerplate you need in the “configure”, so phpize just lets you write a small part that affects your extension.

  • Pingback: PHP扩展开发笔记 – MoXie's blog

kristina chodorow's blog