Skip to main content
Created with Avocode.

Secondary Navigation

  • PHP Security Center
  • Blog
  • Store
  • Downloads
    • Downloads
    • Plugins
    • MyZend Account
  • Company
    • About Zend by Perforce
    • Careers at Perforce
    • Customers
    • Partners
    • Press
  • Contact
    • Contact Us
    • Request Pricing
    • Request Support
    • Subscribe
Home
Zend

Main Navigation - Mega Menu

  • Products

    Main Navigation - Mega Menu

    • ZendPHP
      PHP Runtime and Support
    • PHP LTS
      Patches for EOL PHP
    • ZendHQ
      Must-Have Extension for ZendPHP
    • Zend Server
      PHP Application Server
  • Services

    Main Navigation - Mega Menu

    • Service Overview
    • Migration Services
    • Audits
    • CI/CD
    • Custom Consulting
    • Admin as a Service

    Services

    Innovate faster and cut risk with PHP experts from Zend Services.

    Explore Services

  • Solutions

    Main Navigation - Mega Menu

    • PHP Cloud Solutions
    • PHP Container Solutions
    • PHP Security Solutions
    • Windows Solutions
    • Hosting Provider Solutions

    Zend Solutions Cloud

    See How Zend Helps Leading Hosting Providers Keep Their Managed Sites on Secure PHP

    Read More

  • Training

    Main Navigation - Mega Menu

    • Training Overview

    Training

    Learn PHP from PHP experts with free, on-demand, and instructor led courses.

    Explore Training

  • Resources

    Main Navigation - Mega Menu

    • Explore Resources
    • Events & Webinars
    • Papers & Videos
    • Recorded Webinars
    • Blog
    Cloud Orchestration

    Orchestrating Your PHP Applications

    Watch Now
  • Support

    Main Navigation - Mega Menu

    • Explore Support
    • PHP Long-Term Support
    • Knowledgebase
    • Documentation
    • Download Software
    • Download Plugins
    • Request Support

    Support

    Submit support requests and browse self-service resources.

    Explore Support

  • Try Free
  • PHP Security Center
  • Blog
  • Store
  • Downloads

    Main Navigation - Mega Menu

    • Downloads
    • Plugins
    • MyZend Account
    • Downloads
    • Plugins
    • MyZend Account
  • Company

    Main Navigation - Mega Menu

    • About Zend by Perforce
    • Careers at Perforce
    • Customers
    • Partners
    • About Zend by Perforce
    • Careers at Perforce
    • Customers
    • Partners
  • Contact

    Main Navigation - Mega Menu

    • Contact Us
    • Request Support
    • Subscribe

TECHNICAL GUIDE

Writing PHP Extensions

Request PDF Version
Writing PHP Extensions
1. Setting up Your PHP Build Environment on Linux
2. Generating a PHP Extension Skeleton
3. Building and Installing a PHP Extension
4. Rebuilding Extensions for Production
5. Extension Skeleton File Content
6. Running PHP Extension Tests
7. Adding New Functionality
8. Basic PHP Structures
9. PHP Arrays
10. Catching Memory Leaks
11. PHP Memory Management
12. PHP References
13. Copy on Write
14. PHP Classes and Objects
15. Using OOP in our Example Extension
16. Embedding C Data into PHP Objects
17. Overriding Object Handlers
18. Answers to Common Extension Questions

7. Adding New Functionality

At this point you should already know the basic structure of a PHP extension and the building process. Now, we are going to learn how to implement new basic PHP extension features. Starting from this section, I’ll write new code in red and keep existing code black. 

FUNCTIONS 

Functions are the simplest primitives to add new functionality. To implement a new function, we first have to write the function code itself. This is regular C code that starts with the PHP_FUNCTION() macro and name of the function. This code requires a single double number argument and it returns an argument that is scaled by factor 2. I will describe parameter parsing API and most macros for manipulating values later. 

PHP_FUNCTION(test_scale)
{
    double x;

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_DOUBLE(x)
    ZEND_PARSE_PARAMETERS_END();

    RETURN_DOUBLE(x * 2);
}

We also have to define the arguments’ description block. This block is started from ZEND_BEGIN_ARG_INFO() macro (or its variant) and terminated by ZEND_END_ARG_INFO() macro. 

The first argument of ZEND_BEGIN_ARG_INFO() is the name of the arg_info structure. The same name should be reused in PHP_FE() macro. 

The second argument is ignored. (In PHP 5, it meant pass rest of arguments by reference.) Each argument is defined by the ZEND_ARG_ INFO() macro that takes the “pass by reference” value and the argument name. 

ZEND_BEGIN_ARG_INFO(arginfo_test_scale, 0)
    ZEND_ARG_INFO(0, x)
ZEND_END_ARG_INFO()

It’s possible to use extended variants of ARG_INFO macros to specify additional arguments and return type hints, null-ability, returning by reference, number of required arguments, functions with variable number of arguments, etc. 

Finally, we have to add our new function into the list of extension functions: 

static const zend_function_entry test_functions[] = {
    PHP_FE(test_test1, arginfo_test_test1)
    PHP_FE(test_test2, arginfo_test_test2)
    PHP_FE(test_scale, arginfo_test_scale)
    PHP_FE_END
};

After extension rebuild and installation, the new function should start working. 

$ php -r ‘var_dump(test_scale(5));’
float(10)

To declare functions inside a namespace, it is possible to use ZEND_NS_FUNCTION(ns, name) instead of PHP_FUNCTION(name) — and ZEND_NS_FE(ns, name, arg_info) instead of PHP_FE(name, arg_info). 

It’s also possible to add some function flags (e.g. deprecate function adding ZEND_ACC_DEPRECATED flag), using ZEND_FENTRY() instead if PHP_FE(). See Zend/zend_API.h for the main extension API. 

EXTENSION CALLBACKS 

Only PHP extension functions may be implemented in a pure declarative manner. All other extension features may be implemented calling special API functions during PHP start-up. To do this, the extension should implement MINIT() callback. This is, again, a regular C function, starting with PHP_MINIT_FUNCTION() macro and the extension name as an argument. The function should return SUCCESS to link the PHP extension into the core and enable all its functions and other features. Our MINIT function just initializes thread-local storage cache. Previously, this code was called from RINIT, but in case you execute something in MINIT, and directly or indirectly access module global variables or common global variables, it’s better to move this code into the beginning of MINIT. 

PHP_MINIT_FUNCTION(test)
{
#if defined(ZTS) && defined(COMPILE_DL_TEST)
    ZEND_TSRMLS_CACHE_UPDATE();
#endif

    return SUCCESS;
}

The MINIT callback address should be added into the module entry structure. You can also remove RINIT callback if it was used only for thread-local storage and is now empty.

zend_module_entry test_module_entry = {
    STANDARD_MODULE_HEADER,
    “test”,                    /* Extension name */
    test_functions,            /* zend_function_entry */
    PHP_MINIT(test),           /* PHP_MINIT - Module initialization */
    NULL,                      /* PHP_MSHUTDOWN - Module shutdown */
    NULL,                      /* PHP_RINIT - Request initialization */
    NULL,                      /* PHP_RSHUTDOWN - Request shutdown */
    PHP_MINFO(test),           /* PHP_MINFO - Module info */
    PHP_TEST_VERSION,          /* Version */
    STANDARD_MODULE_PROPERTIES
};

Similar to MINIT, you may implement other callbacks like MSHUTDOWN, when you need to free up some resources before PHP termination.

CONSTANTS 

MINIT callback is suitable to add various extension entities like a new internal constant. This is done using REGISTER_LONG_CONSTANT() macro, where the first argument is the constant name, the second is the constant value, and the third is the constant flags: 

  • CONST_CS refers to the case sensitive constant name. 
  • CONST_PERSISTENT refers to the persistent constant. (All internal extension constants should be persistent.) 
PHP_MINIT_FUNCTION(test)
{
#if defined(ZTS) && defined(COMPILE_DL_TEST)
    ZEND_TSRMLS_CACHE_UPDATE();
#endif

    REGISTER_LONG_CONSTANT(“TEST_SCALE_FACTOR”, 2,
        CONST_CS | CONST_PERSISTENT);
    return SUCCESS;
}

You can access a new constant after the extension rebuild and reinstallation. 

$ php -r ‘var_dump(TEST_SCALE_FACTOR);’
int(2)

Of course, there are various other API macros to declare constants of different value types: 

  • REGISTER_NULL_CONSTANT(name, flags): Constant with value NULL. 
  • REGISTER_BOOL_CONSTANT(name, bval, flags): FALSE or TRUE. 
  • REGISTER_LONG_CONSTANT(name, lval, flags): Any long number. 
  • REGISTER_DOUBLE_CONSTANT(name, dval, flags): A double number. 
  • REGISTER_STRING_CONSTANT(name, str, flags): A zero terminated string. 
  • REGISTER_STRINGL_CONSTANT(name, str, len, flags): A string (with length). 

It’s also possible to use the similar REGISER_NS_...() group of macros to declare constants in some namespaces. See Zend/zend_constants.h for complete PHP constants API. 

MODULE GLOBAL VARIABLES 

For now, our new scale() function is purely functional. It doesn’t depend on any internal state and it always returns a value that’s only based on input arguments. However, some real-world functions access or update global state. In C, it’s usually stored in global variables, but multi-threaded software has to use some tricks to make a distinction between states of different threads. 

PHP is usually built to work in context of single thread, but it also may be configured for multi-threading. In any case, to be portable across different configurations, PHP recommends declaring global variables as a specially declared “module globals” structure. 

The following code should be added at the end of “php_test.h” file: 

ZEND_BEGIN_MODULE_GLOBALS(test)
    zend_long scale;
ZEND_END_MODULE_GLOBALS(test)

ZEND_EXTERN_MODULE_GLOBALS(test)

#define TEST_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(test, v)

The module global variables are declared between ZEND_BEGIN_MODULE_GLOBALS and ZEND_END_MODULE_GLOBALS macros. These are just plain C declarations. Actually, the C preprocessor converts them into “zend_tests_globals” C structure definition. ZEND_ EXTERN_MODULE_GLOBALS() defines an external C name to access the structure and TEST_G() macro provides a way to access our module global variables. So instead of global “scale”, we will use TEST_G(scale). 

In the “test.c” file we should declare the real module global variable: 

ZEND_DECLARE_MODULE_GLOBALS(test)

We will also define a GINIT callback to initialize this structure. This callback is called before MINIT, so we have to move the thread-local storage cache initialization code here: 

static PHP_GINIT_FUNCTION(test)
{
#if defined(COMPILE_DL_BCMATH) && defined(ZTS)
    ZEND_TSRMLS_CACHE_UPDATE();
#endif
    test_globals->scale= 1;
}

We should also add information about module global variables and their initialization callback into the extension entry structure: 

zend_module_entry test_module_entry = {
    STANDARD_MODULE_HEADER,
    “test”,                    /* Extension name */
    test_functions,            /* zend_function_entry */
    PHP_MINIT(test),           /* PHP_MINIT - Module initialization */
    NULL,                      /* PHP_MSHUTDOWN - Module shutdown */
    NULL,                      /* PHP_RINIT - Request initialization */
    NULL,                      /* PHP_RSHUTDOWN - Request shutdown */
    PHP_MINFO(test),           /* PHP_MINFO - Module info */
    PHP_TEST_VERSION,          /* Version */
    PHP_MODULE_GLOBALS(test),  /* Module globals */
    PHP_GINIT(test),           /* PHP_GINIT – Globals initialization */
    NULL,                      /* PHP_GSHUTDOWN – Globals shutdown */
    NULL,
    STANDARD_MODULE_PROPERTIES_EX
};

And now we can update our “scale” function to use the module global variable: 

PHP_FUNCTION(test_scale)
{
    double x;

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_DOUBLE(x)
    ZEND_PARSE_PARAMETERS_END();

    RETURN_DOUBLE(x * TEST_G(scale));
}

It works after extension recompilation and reinstallation: 

$ php -r ‘var_dump(test_scale(5));’
float(5)

CONFIGURATION DIRECTIVES 

What else can we do? We may implement a way to define the value of our “scale” factor through configuration directive in php.ini. This is done by two additional pieces of code in “test.c.” 

The first defines configuration directives, their names, default values, types, and storage locations: 

PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY(“test.scale”, “1”, PHP_INI_ALL, OnUpdateLong, scale,
        zend_test_globals, test_globals)
PHP_INI_END()

STD_PHP_INI_ENTRY() declares a configuration directive named “test.scale,” with default value “1.” PHP_INI_ALL indicates that it may be modified at any time (in php.ini, in per-directory configuration files and by ini_set() function during script excution). PHP_INI_SYSTEM (instead) would allow modification only during PHP startup (in php.ini). PHP_INI_PERDIR would allow modification only in php.ini and per-directory configuration files. 

OnUpdateLong is a common callback that sets the integer value of the directive. (There are few other common callbacks like OnUpdateString.) “scale” is the module global variable name. “zend_test_globals” is the name of the structure (C type name) that keeps module global variables. “test_globals” is the global variable that keeps module global variables for a non-thread-safe build. 

The complete PHP.ini API is defined at Zend/zend_ini.h and main/php_ini.h. 

The second piece calls an API function that registers the directives declared in the previous block:

PHP_MINIT_FUNCTION(test)
{
    REGISTER_INI_ENTRIES();

    return SUCCESS;
}

PHP configuration directives may be set through a “-d” command line argument: 

$ php -d test.scale=4 -r ‘var_dump(test_scale(5));’
float(20)

COMMON PHP GLOBALS 

Except for our own global variables, we may also need to get some values from common PHP global variables that are wrapped into similar module global structures and may be accessed through similar macros: 

  • CG(name): Compiler global variables. 
  • EG(name): Executor global variables. 

All declarations can be viewed at Zend/zend_globals.h. 

Request PDF Version

Book traversal links for 7. Adding New Functionality

  • ‹ 6. Running PHP Extension Tests
  • Writing PHP Extensions
  • 8. Basic PHP Structures ›

Footer menu

  • Products
    • ZendPHP
    • PHP Long-Term Support
    • ZendHQ
    • Zend Server
    • Laminas Enterprise Support
    • PHP Development Tools
  • Services
    • PHP Long-Term Support
    • Migration Services
    • Audits
    • CI/CD Services
    • Custom Consulting
    • Admin as a Service
  • Free Trials and Demos
    • ZendPHP Trial
    • Zend Server Trial
    • ZendHQ Demo
  • Training
  • Resources
    • PHP Security Center
    • Papers & Videos
    • Events & Webinars
    • Recorded Webinars
    • Blog
    • Case Studies
  • Hubs
    • PHP Versions Guide
    • Developing Web Apps With PHP
    • Guide to PHP and IBM i
  • Store
  • Downloads
    • MyZend Account
    • Plugins
    • Container Registry
  • Support
    • PHP Long-term Support
    • Knowledgebase
    • Documentation
  • Contact
    • Request Pricing
    • Request Support
    • Subscribe
  • Company
    • About Zend by Perforce
    • Careers at Perforce
    • Customers
    • Partners
    • Press
Home

Zend by Perforce © Perforce Software, Inc.
Terms of Use  |  Privacy Policy | Sitemap

Social Menu

  • Facebook
  • LinkedIn
  • Twitter
  • YouTube
  • RSS
Send Feedback