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 

// 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.

Join the Conversation

19 Comments

    1. 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.

      Like

      1. 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

        Like

      2. 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 ……

        Like

      3. 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.

        Like

      4. 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.

        Like

    1. 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.

      Like

  1. 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 .. ?

    Like

    1. 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.

      Like

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: