Writing PHP Extensions1. Setting up Your PHP Build Environment on Linux2. Generating a PHP Extension Skeleton3. Building and Installing a PHP Extension4. Rebuilding Extensions for Production5. Extension Skeleton File Content6. Running PHP Extension Tests7. Adding New Functionality8. Basic PHP Structures9. PHP Arrays10. Catching Memory Leaks11. PHP Memory Management12. PHP References13. Copy on Write14. PHP Classes and Objects15. Using OOP in our Example Extension16. Embedding C Data into PHP Objects17. Overriding Object Handlers18. Answers to Common Extension Questions5. Extension Skeleton File ContentLet’s review the contents of extension skeleton files.“config.m4” is an extension configuration script, used during generation of “configure” script by “phpize” or “buildconf” commands. It’s written in M4 macro-processing language. Very basic knowledge is enough for PHP extension configuration. You can copy-paste blocks from this tutorial or other extension configuration files.PHP_ARG_ENABLE([test], [whether to enable test support], [AS_HELP_STRING([--enable-test], [Enable test support])], [no]) if test "$PHP_TEST" != "no"; then AC_DEFINE(HAVE_TEST, 1, [ Have test support ]) PHP_NEW_EXTENSION(test, test.c, $ext_shared) fi PHP_ARG_ENABLE(...) – macro adds a configuration option “--enable-test”. It may get three values “yes”, “no”, and “shared”. When you run “phpize”, the default value is “shared” which means we are going to build a dynamically loadable PHP extension. However, it is possible to copy the “test” extension directory into the main PHP distribution (“ext/test”) and re-run “./buildconf” and “./configure … –enable-test” to re-build the whole PHP with extension “test”, statically linked in. It’s possible to enable extension by default, replacing “no” to “yes” at line 5. In this case, it’s possible to disable “test” extension by “./configure --disable-test”.Following “if” is just a regular UNIX shell code that tests the value defined by “--enable-test”, “--disable-test”, or “--enable-test=shared”.AC_DEFINE(HAVE_TEST) adds C macro HAVE_TEST into “config.h”, so you can use conditional compilation directives (#ifdef, #ifndef) to skip useless code, if necessary.Finally, the PHP_NEW_EXTENSION (test, test.c, $ext_shared) macro states that we are going to build extension “test” from “test.c” file. It’s possible to specify few files. Depending on the value of $ext_shared variable, the extension could be built as shared object or linked statically. (It’s taken from the same “--enable-test” option.)This file might need to be extended in case you add new source files or need to link some external libraries. I’ll show how to link libraries later. Just, don’t forget to rerun “phpize”/”buildconf” + “configure” after you make any changes in this file.Windows PHP uses a different build system. For Windows, file “config.w32” is a replacement of “config.m4”. The two are almost the same. They use similar macros, just a different language: on Windows PHP build system uses JavaScript instead of M4 and Shell. I won’t repeat the explanation of the macros. You should be able to guess.ARG_ENABLE('test', 'test support', 'no'); if (PHP_TEST != 'no') { AC_DEFINE('HAVE_TEST', 1, 'test support enabled'); EXTENSION('test', 'test.c', null, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); } “php_test.h” is a C header file with common definitions. In our very basic case, it defines:test_module_entry — an extension description structure. (It’s an entry point to the extension.)PHP_TEST_VERSION — a version of the extension.ZEND_TSRMLS_CACHE_EXTERN — a thread-local storage cache entry, if the extension was built for a thread-safe build (ZTS) and compiled as shared object ( COMPILE_DL_TEST)./* test extension for PHP */ #ifndef PHP_TEST_H # define PHP_TEST_H extern zend_module_entry test_module_entry; # define phpext_test_ptr &test_module_entry # define PHP_TEST_VERSION "0.1.0" # if defined(ZTS) && defined(COMPILE_DL_TEST) ZEND_TSRMLS_CACHE_EXTERN() # endif #endif /* PHP_TEST_H */ “test.c” is the main (and in our case, single) extension source file. It’s too big to fit into one page/screen, so I’ll split it into small parts and explain each part separately./* test extension for PHP */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "php.h" #include "ext/standard/info.h" #include "php_test.h" Include necessary C header files. You may add additional “#include” directives if necessary./* For compatibility with older PHP versions */ #ifndef ZEND_PARSE_PARAMETERS_NONE #define ZEND_PARSE_PARAMETERS_NONE() \ ZEND_PARSCE_PARAMETERS_START(0, 0) \ ZEND_PARSE_PARAMETERS_END() #endif Some forward compatibility macro, to make it possible to compile the extension for older PHP-7 versions./* {{{ void test_test1() */ PHP_FUNCTION(test_test1) { ZEND_PARSE_PARAMETERS_NONE(); php_printf("The extension %s is loaded and working!\r\n", "test"); } /* }}} */ A C code for function test_test1() provided by our PHP extension. The argument of PHP_FUNCTION() macro is the function name. ZEND_PARSE_PARAMETERS_NONE() tells that this function doesn’t require any arguments. php_printf(...) is just a C function call that prints the string into the output stream, similar to PHP printf() function./* {{{ string test_test2( [ string $var ] ) */ PHP_FUNCTION(test_test2) { char *var = "World"; size_t var_len = sizeof("World") – 1; zend_string *retval; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_STRING(var, var_len) ZEND_PARSE_PARAMETERS_END(); retval = strpprintf(0, "Hello %s", var); RETURN_STR(retval); } /* }}}*/ Another, more complex function uses “Fast Parameter Parsing API” to describe its arguments. ZEND_PARSE_PARAMETERS_START(0, 1) starts the parameter description section. Its first argument (0) defines the number of required arguments. The second argument (1) defines the maximum number of arguments. So, our function may be called without arguments, or with a single argument. Inside this section, we should define all parameters, their types, and where they will be copied. For our case:Z_PARAM_OPTIONAL separates required parameters from optional ones. Z_PARAM_STRING() defines a string parameter that value is going to be copied to variable “var” and the length into variable “var_len.” Note that our argument is optional and therefore may be omitted. In this case a default value “World” is used. See initializers for variables “var” and “var_len” above ZEND_PARSE_PARAMETERS_START.The code creates a “zend_string” value and returns it though macro RETURN_STR() similar to PHP sprintf() function:/* {{{ PHP_RINIT_FUNCTION */ PHP_RINIT_FUNCTION(test) { #if defined(ZTS) && defined(COMPILE_DL_TEST) ZEND_TSRMLS_CACHE_UPDATE(); #endif return SUCCESS; } /* }}} */ PHP_RINIT_FUNCTION() defines a callback function that is going to be called at each request start-up. In our case, it only initializes thread-local storage cache. It would be much better to do this early (in MINIT or GINIT callbacks). I predict this will be fixed in the PHP 8 extension skeleton./* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(test) { php_info_print_table_start(); php_info_print_table_header(2, "test support", "enabled"); php_info_print_table_end(); } /* }}} */ PHP_MINFO_FUNCTION() defines a callback function that is going to be called from PHP phpinfo() function, to print information about the extension./* {{{ arginfo */ ZEND_BEGIN_ARG_INFO(arginfo_test_test1, 0) ZEND_END_ARG_INFO() Information about arguments of the first function. There are no arguments.ZEND_BEGIN_ARG_INFO(arginfo_test_test2, 0) ZEND_ARG_INFO(0, str) ZEND_END_ARG_INFO() /* }}} */ Information about arguments of the second function. The single optional argument with name “str” is passed by value./* {{{ test_functions[] */ static const zend_function_entry test_functions[] = { PHP_FE(test_test1, arginfo_test_test1) PHP_FE(test_test2, arginfo_test_test2) PHP_FE_END }; /* }}} */ “test_functions” is a list of all extension functions with information about their arguments. The list is terminated by PHP_FE_END macro./* {{{ test_module_entry */ zend_module_entry test_module_entry = { STANDARD_MODULE_HEADER, "test", /* Extension name */ test_functions, /* zend_function_entry */ NULL, /* PHP_MINIT - Module initialization */ NULL, /* PHP_MSHUTDOWN - Module shutdown */ PHP_RINIT(test), /* PHP_RINIT - Request initialization */ NULL, /* PHP_RSHUTDOWN - Request shutdown */ PHP_MINFO(test), /* PHP_MINFO - Module info */ PHP_TEST_VERSION, /* Version */ STANDARD_MODULE_PROPERTIES }; /* }}} */ test_module_entry is the main extension entry structure. PHP core takes all information about extensions from such structures. It defines:Extension name (“test”).A list of declared PHP functions (“test_functions”).A few callback functions and extension version (PHP_TEST_VERSION - defined in the header file). The callbacks occur when PHP started (MINIT), on PHP termination (MSHUTDOWN), at start of each request processing (RINIT), at the end of each request processing (RSHUTDOWN) and from phpinfo() (MINFO).#ifdef COMPILE_DL_TEST # ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() # endif ZEND_GET_MODULE(test) #endif Finally, a couple of definitions for dynamic linking.Request PDF VersionBook traversal links for 5. Extension Skeleton File Content‹ 4. Rebuilding Extensions for ProductionWriting PHP Extensions6. Running PHP Extension Tests ›