PHP Extensions – How and Why?

Standard

In this short post we will quickly see:

  1. How to write PHP extensions?
  2. Why to write PHP extensions?

However before you could understand what we are going to disucss, I will recommend you to read one of  my previous post How does PHP echo’s a “Hello World”? – Behind the scene . In this post I discussed in brief the backend architecture of PHP.

Assuming you have read the previous post, lets discuss on how to build our first PHP extension:

  1. Every PHP extension is built out of minimum of 2 files.
  2. Configuration file (config.m4) which tells us what files to build and what external libraries are needed.
  3. Source File(s) which will contain the actual functionalities provided by the extension.

Building a sample extension skeleton
Lets start with building and understanding a sample extension skeleton. Then we will move ahead with building our first PHP extension:

config.m4

PHP_ARG_ENABLE(sample,
        [Whether to enable the "sample" extension],
        [-enable-sample  Enable "sample" extension support])
if test $PHP_SAMPLE != "no"; then
        PHP_SUBST(SAMPLE_SHARED_LIBADD)
        PHP_NEW_EXTENSION(sample,sample.c,$ext_shared)  // 1st argument declares the module
                                                        // 2nd tells what all files to compile
                                                        // $ext_shared is counterpart of PHP_SUBST()
fi

I found a number of articles on internet which gives you code for your first PHP extension but none of them go ahead and explain each and every word in those codes. Lets give an attempt in understanding every bit of this strange config file.

  1. This is a minimalistic config file which is required for an extension
  2. The first parameter to PHP_ARG_ENABLE(), sets up a ./configure option called -enable-sample
  3. The second parameter to PHP_ARG_ENABLE() will be displayed during the ./configure process as it reaches this configuration file
  4. Third parameter will be displayed as an option if end user issues ./configure -help

For Newbies: Wondering what is this ./configure option? Kindly read PHP: Installation on Unix System for details.

Lets understand the remaining part of the config.m4 file:

  1. To compile an extension we follow 3 steps: (i) phpize (ii) ./configure -enable-sample (iii) make
  2. When we call ./configure -enable-sample in step (ii), a local environmental variable $PHP_SAMPLE is set to yes. (PS: If our extension name was Hello, then $PHP_HELLO would have been set to yes)
  3. PHP_SUBST() is a MACRO similar to AC_SUBST() in C and is necessary to build the extension as a shared module
  4. PHP_NEW_EXTENSION() declares the module and tell source files that must be compiled as part of the extension. $ext_shared is a counterpart of PHP_SUBST() and is necessary for buildin an extension as a shared module

(PS: We only have a single source file i.e. sample.c for this extension. If in case we had more than a single source file then last line of config.m4 would have been something like this: PHP_NEW_EXTENSION(sample,sample1.c sample2.c sample3.c,$ext_shared) and so on)

Now lets build our source file skeleton. Let’s segregate certain type of data in a header file, which we will finally include in sample.c file. This is generally a good practice rather than maintaining a single source file.

php_sample.h

#ifndef PHP_SAMPLE_H
  #define PHP_SAMPLE_H
  #define PHP_SAMPLE_EXTNAME "sample"
  #define PHP_SAMPLE_EXTVER "1.0"

  #ifdef HAVE_CONFIG_H
    #include "config.h"
  #endif

  #include "php.h"
  extern zend_module_entry sample_module_entry;
  #define phpext_sample_ptr &sample_module_entry
#endif

Do not leave this page on seeing this code. It’s all very simple if you have ever written some code in C.
All that this file wants to do is:

  1. config.h file is included when compiled using phpize tool
  2. It also includes php.h from the PHP source tree. With inclusion of php.h, many other .h files also gets included and hence making available a lot of PHP API’s, which can be used by this extension
  3. The zend_module_entry struct is defined as extern so that it can be picked up by ZEND engine using dlopen() and dlsym() functions, when the module loads.

sample.c

#include "php_sample.h"

zend_module_entry sample_module_entry = {
  #if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,        // Roughly means if PHP Version > 4.2.0
  #endif
    PHP_SAMPLE_EXTNAME,        // Define PHP extension name
    NULL,        /* Functions */
    NULL,        /* MINIT */
    NULL,        /* MSHUTDOWN */
    NULL,        /* RINIT */
    NULL,        /* RSHUTDOWN */
    NULL,        /* MINFO */
  #if ZEND_MODULE_API_NO >= 20010901
    PHP_SAMPLE_EXTVER,        // Roughly means if PHP Version > 4.2.0
  #endif
    STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_SAMPLE
  ZEND_GET_MODULE(sample)      // Common for all PHP extensions which are build as shared modules
#endif


Thats it! We have our first PHP extension ready. Compile this module as discussed above i.e.
(i) phpize
(ii) ./configure -enable-sample
(iii) make
check your phpinfo() and see if you have an extension called “sample” loaded successfully or not.

Though this extension is capable of doing nothing, but the skeleton here is the base for every PHP extension. Lets recap in short what has happened till now:





RECAP

  1. config.m4 file is the configuration file for extension
  2. It declared the extension, tells what all files are required for the extension to build, add a few ./configure -help options too
  3. On the other hand sample.c and php_sample.h are the main source files.
  4. The header file includes the config.h and php.h header files from PHP source tree, which additionally provides a number of PHP API’s which can be used
  5. As discussed in last blog post, every extension have the following modules: MINIT, RINIT, RSHUTDOWN, MSHUTDOWN. sample.c helps in telling PHP, which part of the code corresponds to the above module
  6. For our extension “sample” we have defined NULL as MINIT, RINIT, RSHUTDOWN and MSHUTDOWN and hence this module isn’t capable of doing anything

Building a Hello World Extension
To build an extension which actually do something, we will need to just tweak the abiove skeleton. Here we are trying to build an extension which will provide us with a function called sample_hello_world(), which we can use directly in our php codes to output Hello World!

Quickest link between userspace and extension code is the PHP_FUNCTION(). Start by adding the following code block near the top of sample.c file just after
#include “php_sample.h”

PHP_FUNCTION(sample_hello_world) {
  php_printf("Hello World!\n");
}

PHP_FUNCTION() is basically a MACRO which expands internally. (I will skip this expansion as of now to keep this post as simple as possible)

But simply declaring the function isn’t enough. The ZE needs to know the address of the function as well as how the function name should be exported to the userspace. Place the following block of code immediately after PHP_FUNCTION() block:

static function_entry php_sample_functions[] = {
    PHP_FE(sample_hello_world,NULL)
    {NULL,NULL,NULL}
};

The php_sample_functions vector is a NULL terminated vector that will grow as we continue to add more functionality to sample extension. Every function we export will appear as an item in this vector.

PHP_FE(sample_hello_world,NULL) expands to {sample_hello_world, zif_sample_hello_world, NULL} and hence providing a name and an address to implement it.

Finally simply go to the sample_module_entry struct and replace:
NULL /* functions */ with
php_sample_functions /* functions */

Now simply rebuild the extension and then try this on command line:
$ php -r ‘sample_hello_world();’

If everything was done perfectly, you would see “Hello World!” output on the shell.

PS: In this tutorial I have tried to explain each and every line which is involved in making a hello world php extension. However I am sure that many questions are still un-answered. Feel free to ask any doubt or correct me in case I have made a blunder while penning this down.

  • Pingback: JohnPuPu » Blog Archive » links for 2008-12-10

  • Pingback: Useful Links for PHP « Narendra Dhami

  • Pingback: Squirrel Hacker » links for 2008-12-12

  • Zach

    Great explanation on the how, but why would I want to create an extension?

    I’m getting into deeper PHP coding recently and love learning about all of this low-level stuff. I just don’t understand the why.

    Thanks again for a great article.

  • http://abhinavsingh.com admin

    Sorry for missing out the why part towards the end. Lemme summarize it in 2-3 lines. You may want to write PHP extensions for following reasons:

    1. PHP extensions are written in C, and pre-compiled. Hence you get faster response for your functions. For eg. We have mysql extension enabled which allows us to connect to mysql database. Since we know that connecting to a mysql db is a common thing, hence we wrote an extension for it. To save time.
    2. Secondly you may be interested in writing an extension because you don’t want your code to be read by others. Suppose you developed some algorithm or implemented a great algo. Now you want the world to use it but you are still not prepared to open it up. Simple enough, just create an extension out of your php code and distribute the .so file across the world.
    3. Finally you may want to create PHP extensions if you are mad like me, for no reasons ofcourse :P

    Regards,
    Abhi

  • razvantim

    do you have some books recommendations to continue the study of php extensions ?

  • razvantim

    almost forgot, great article :)

  • http://abhinavsingh.com admin

    Thanks. Well there must be some great books on this issue. However since I mostly read from my internal office network twiki, I am not aware of any names.

    Try to search on safari and you will find plenty. All the best.

  • Jacob Santos

    I think the barrier for entry for me is figuring out how to debug memory crashes. Several tutorials have pointed out linking to the PHP executable in VS, but I’m unsure how this will work.

  • Jacob Santos

    Oh yeah, how do you debug PHP extensions on Windows? I think that would be an awesome blog post. Preferably, using Visual Studio Express or 2005/2008.

  • http://abhinavsingh.com admin

    @Jacob Santos
    Hi Jacob, I know there is a lot more to writing PHP extensions. One need to have all internals knowledge including ZVAL etc for writing a good extension. I am planning one of my next posts on the same topic.

    Regarding debugging PHP extensions on windows, well I wasn’t able to setup the environment for that on my local windows :( I remember I needed a few stuffs and couldn’t find them. Will appreciate if someone else can write one on that :)

    Belated Merry Christmas and Happy New Year :)

  • Jacob Santos

    I plan on working on my PHP extension in February to see if I can get it debugged. If I can get it working by linking against the PHP executable, then I’ll be sure to write a blog post.

    It is super annoying not being able to step through code.

  • http://abhinavsingh.com admin

    Yeah I did tried coming up with extension.dll on windows system but for some reasons I wasn’t able to crack through. Will appreciate a step through tutorial for the same.

  • asha

    I need more explanation.

  • http://abhinavsingh.com admin

    Can you kindly be more specific as to what are you referring to. More explaination on what?

  • Ramya

    i wanna to make it work for me. where should i store these files to run??? inside PHP?

  • http://abhinavsingh.com admin

    Hi Ramya,

    You can place these files anywhere on your system, inside a folder. Build the extension and then you will have to keep generated .so (for unix) or .dll (for windows) in your php’s extension directory. Then enable the same in your php.ini script, and voila!
    Hope you get it working :D
  • Pingback: PHP tokens and opcodes : 3 useful extensions for understanding the working of Zend Engine | Abhi's Weblog

  • saimhe

    COMPILE_DL_* is defined by the PHP_NEW_EXTENSION macro and supposedly written to config.h so that the code being compiled can see it – and can export the _get_module symbol correctly which is absolutely required for a shared module.

    However I even built PHP 5.3.0 and a few standard modules (as shared, of course) successfully under Fedora 9 but still there was no config.h in the source tree. After a few days of hacking I found everything in php_config.h. Which is included automatically as php.h – php_compat.h – php_config.h.

    Conclusion: the whole HAVE_CONFIG_H mechanism is outdated and not used any more.

  • http://abhinavsingh.com Abhinav Singh

    Hi Saimhe,

    Absolutely right.
    There are a lot of changes being made to the PHP API’s for building extensions.

    – Abhi

  • Pingback: PHP tokens and opcodes : 3 useful extensions for understanding the working of Zend Engine | 鸭嘴的blog

  • Hamza

    i need to make this function include a file from the server when it’s called

    like include a class and return the class instance

    include(“/path/class.php”);
    return new ClassName();

    instead of
    php_printf(“Hello World!\n”);

    is there a way to do that ?

  • http://abhinavsingh.com Abhinav Singh

    Hi Hamza,

    Do you want to include a PHP file inside your extension and then return it’s instance?

  • http://code.google.com/p/ztag/ Ruben Zevallos Jr.

    Very nice to know it… I want in a near future to transfer my zTag project to a extension… the idea is to reduce the execution time.

  • Jorge Arreaza

    I need to place the .so extension on godaddy web server where I cannot edit php.ini how do I call the extension to work?

    $tnx=’Thank you very much';
    echo $tnx;

  • Jorge Arreaza

    I hope you can help me.
    I have linux mint 9.
    Downloaded php source version 5.2.17 (this is the one used at godaddy)
    I changed api no. to 20090626
    I was asked to apt-get phpize5
    What follows is the result of a compilation intent with no luck
    Please guide me on what could be missing.
    Thank you.

    copy from the terminal follows
    =====
    /ext/sample $ phpize5
    Configuring for:
    PHP Api Version: 20090626
    Zend Module Api No: 20090626
    Zend Extension Api No: 220090626
    jorgearr@compusis5 ~/Downloads/php-5.2.17/ext/sample $ ./configure –enable-sample
    checking for grep that handles long lines and -e… /bin/grep
    checking for egrep… /bin/grep -E
    checking for a sed that does not truncate output… /bin/sed
    checking for cc… cc
    checking whether the C compiler works… yes
    checking for C compiler default output file name… a.out
    checking for suffix of executables…
    checking whether we are cross compiling… no
    checking for suffix of object files… o
    checking whether we are using the GNU C compiler… yes
    checking whether cc accepts -g… yes
    checking for cc option to accept ISO C89… none needed
    checking how to run the C preprocessor… cc -E
    checking for icc… no
    checking for suncc… no
    checking whether cc understands -c and -o together… yes
    checking for system library directory… lib
    checking if compiler supports -R… no
    checking if compiler supports -Wl,-rpath,… yes
    checking build system type… i686-pc-linux-gnu
    checking host system type… i686-pc-linux-gnu
    checking target system type… i686-pc-linux-gnu
    checking for PHP prefix… /usr
    checking for PHP includes… -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
    checking for PHP extension directory… /usr/lib/php5/20090626+lfs
    checking for PHP installed headers prefix… /usr/include/php5
    checking if debug is enabled… no
    checking if zts is enabled… no
    checking for re2c… no
    configure: WARNING: You will need re2c 0.13.4 or later if you want to regenerate PHP parsers.
    checking for gawk… gawk
    ./configure: line 3953: 1.: command not found
    checking 2. Whether to enable the sample extension… yes, shared
    ./configure: line 4003: syntax error near unexpected token `then’
    ./configure: line 4003: ` 4. if test “$PHP_SAMPLE” != “no”; then’
    jorgearr@compusis5 ~/Downloads/php-5.2.17/ext/sample $ make
    make: *** No targets specified and no makefile found. Stop.
    jorgearr@compusis5 ~/Downloads/php-5.2.17/ext/sample $

  • Jorge Arreaza

    OK. I compiled it and finally got the .so file. Now I found a wall my problem is that php 5.2.17 at godaddy expects me to place the .so extensions only in the extensions directory and I do not have access to it, I can only edit the php5.ini and place the files in my html directory. And this is the question: Is there a trick to make it work given the conditions mentioned?

    Thanks in advance.

  • Peter

    Hi,

    Thanks for the tutorial. This was very helpful and I’ve got it working. Now, I am trying to call a C function from a c source file without converting the C file into an extension. Basically, I guess I would like to wrap C functions in a PHP extension.

    Could you please show some examples on that?

    Thanks,
    Peter

  • Pingback: error: php.h no such file or directory | SeekPHP.com

  • http://www.flagrantsystemerror.com Jay K.

    There is a pretty good book for developing PHP extensions. It’s “Extending and Embedding PHP” by Sara Golemon.

    It walks you through the primary data structures and functions for interfacing with the PHP engine. It’s a little out-of-date, but it’s really the only starting point that I’ve seen on the subject.

    Of course, there’s nothing like following by example. Find an extension that does something similar to what you want, and use it as a template.

  • Pingback: Learning to write PHP Extensions | Huazai

  • Antrikssh

    Hello,

    I have one doubt regarding PHP extension the entire information. i got from various site but still i am unable to print ‘Hello World’ on my browser or command prompt.I have created .so file by using the procedure that you mention or mentioned by the different site. I have also configured it. but when I execute the command php -r ‘sample_hello_world();’ the error has been occured that is Fatal error: Call to undefined function sample_hello_world() in Command line code on line 1. so can you solve my problem.

    Thanks & Regards,
    Antrikssh.

  • Antrikssh

    Hi Abhinav,

    Yesterday I send one post here but today post is not here why? I have post my problem related to this topic and i am facing that problem last one week. I thought i will get the answer from this site but today i am little disappointed.

    Regards,

    Antrikssh

  • Satish Bhasin

    Sir, please tell how can we compile the php_sample.h and php.c files? and can this be done in the windows environment and what tools do we need?

  • mak ashtekar

    really intresting topic..
    It is very neat and clean topic.. Anyone can understand quickly.. Thank u so much..

  • Pingback: PHP调用C++动态链接库

  • jhuebel

    Note that the above code is broken for PHP 5.4 and above. Replace this line:

    static function_entry php_sample_functions[] = {

    with:

    static zend_function_entry php_sample_functions[] = {