One, foreword

1. The source code version used in this article is PHP-7.1.19

2. The PHP version installed in this article is 7.1.19

3. If the computer is A Mac, the operating system information is as follows

Darwin Kernel Version 18.0.0: root:xnu-4903.201.2~1/RELEASE_X86_64 x86_64
Copy the code

4. The extension development mentioned in this article is a PHP extension, not a Zend extension

5, the original article from the blog, pay attention to get the latest articles

PHP extensions and Zend extensions

1. What are PHP extensions

The _zend_module_entry structure is defined in the /php-src/Zend/zend_modules.h header. This structure is the PHP extension structure called module, which provides the following hook functions in addition to some basic information

  • MINT: is called when the module is initialized
  • MSHUTDOWN: is called when the module terminates
  • RINT: is called every time a request is made
  • RSHUTDOWN: Is invoked after each request ends
struct _zend_module_entry {
    // The basic information is normally populated with the STANDARD_MODULE_HEADER constant
    unsigned short size;
    unsigned int zend_api;
    const struct _zend_ini_entry *ini_entry;
    int module_started;
    int module_number;
    
    // Name of the extension
    const char *name;
    
    // Extended function pointer, used to get the list of extended functions
    const struct _zend_function_entry *functions;
    
    // MINT hook function
    int (*module_startup_func)(INIT_FUNC_ARGS);
    
    // MSHUTDOWN hook function
    int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
    
    // RINT hook function
    int (*request_startup_func)(INIT_FUNC_ARGS);
    
    // RSHUTDOWN hook function
    int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
	
    // Prints extended information when phpInfo () is called
    void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);
};
Copy the code

What is the Zend extension

The _zend_extension structure is defined in the /php-src/Zend/zend_extensions.h header. This structure is the structure of the Zend extension, called extension. More low-level hook functions are provided than PHP extensions, as shown below

struct _zend_extension {
    // Some basic information
    char *name;
    char *version;
    char *author;
    char *URL;
    char *copyright;

    /* Some hook functions within the Zend lifecycle */
    startup_func_t startup;
    shutdown_func_t shutdown;
    activate_func_t activate;
    deactivate_func_t deactivate;

    message_handler_func_t message_handler;

    op_array_handler_func_t op_array_handler;

    statement_handler_func_t statement_handler;
    fcall_begin_handler_func_t fcall_begin_handler;
    fcall_end_handler_func_t fcall_end_handler;

    op_array_ctor_func_t op_array_ctor;
    op_array_dtor_func_t op_array_dtor;
    /* Some hook functions within the Zend lifecycle */
};
Copy the code

3, for example,

(1) the Json extension

The Json extension defines the structure as zend_module_entry, which indicates that it is a PHP extension

(2) Opcache extension

The Opcache extension defines zend_extension as a Zend extension

(3) the Xdebug extension

The Xdebug extension must Hook within the Zend lifecycle to debug code, so Xdebug is a Zend extension

4, summarize

Extensions distinguish between PHP extensions and Zend extensions, and there is a strict distinction between module and extension in PHP source code

  • modulesaidphp extension, which is a PHP extension loaded with extension=*
  • extensionsaidzend extension, the Zend extension, loaded with zend_extension=*

Third, the structure of the extension

1. Directory structure

The php-src/ext directory in the source code is the extension directory, such as JSON, mysqli, pDO and other commonly used extensions, each of which is mainly composed of the following files

  • tests: Unit test directory
  • config.m4: Extended build configuration file (Unix)
  • config.w32: Extended build configuration file (Windows)
  • php_{$module}.h: Extension header file
  • {$module}.c: Extends the source file
  • {$module}.php: extended test file

2. Code structure

(1) Unit test

After the extension is successfully compiled, a run-test.php script file is generated in the extension directory, which automatically performs all unit tests in the Tests directory. In addition, in the extension directory will automatically generate a {$module}.php extension test file, you can easily test whether the extension can be loaded and used normally

(2) Compile the configuration file

So extension files can be used by PHP. Config. m4 and config.w32 are the configuration files that will be used in the subsequent execution of PHPize. M4 is a macro processing file consisting of PHP_ARG_WITH and PHP_ARG_ENABLE. The second part is used to enable the specified extension. At compile time, the $PHP_PHP_HELLO variable is determined not to be no, and the compilation of this extension is performed. If you want to compile the php_hello extension, remove all the DNL macros at the beginning of the PHP_ARG_ENABLE section

dnl If your extension references something external, use with:

dnl PHP_ARG_WITH(php_hello.for php_hello support.dnl Make sure that the comment is aligned:
dnl[-with-php_hello             Include php_hello support])

dnl Otherwise use enable:

dnl PHP_ARG_ENABLE(php_hello.whether to enable php_hello support.dnl Make sure that the comment is aligned:
dnl[-enable-php_hello           Enable php_hello support])

if test "$PHP_PHP_HELLO! "" ="no"; then
  PHP_NEW_EXTENSION(php_hello, php_hello.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi

Copy the code

(3) Extension header file

The extension header, usually named php_{$module}. H, is used to define constants, functions, etc

(4) Expand the source file

The extension source file, typically named {$module}.c, consists of the following sections

  • zend_module_entry: Defines the extended structure
  • PHP_FUNCTION: Defines the extended function
  • Hook_FUNCTION, such as:PHP_MINIT_FUNCTION

The loading process of the extension

(1) Source annotations

In the php-src/main/main.c file, the php_module_startup() function performs the load and initialization of the extension.

int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules)
{
    
    // Zend engine initialization
    zend_startup(&zuf, NULL);
    
    // Register constants
    php_output_register_constants();
    php_rfc1867_register_constants();
    
    // Register the INI configuration
    if (php_init_config() == FAILURE) {
        return FAILURE;
    }
    REGISTER_INI_ENTRIES();
    zend_register_standard_ini_entries();

    // register global variables like $_GET/$_POST/$_SERVER/$_REQUEST
    php_startup_auto_globals()

    // Load statically compiled extensions included in main/internal_functions.c
    if (php_register_internal_extensions_func(TSRMLS_C) == FAILURE) {
        php_printf("Unable to start builtin modules\n");
        return FAILURE;
    }

    // Register the SAPI extension module, i.e. Additional_modules
    php_register_extensions_bc(additional_modules, num_additional_modules TSRMLS_CC);

    // According to ini configuration, load Zend extension first, then load PHP extension
    php_ini_register_extensions(TSRMLS_C);
    
    // Extension initialization triggers MINT() hook
    zend_startup_modules(TSRMLS_C);
    zend_startup_extensions();
}
Copy the code

(2) php_ini_register_extensions function

In this function, extension_lists is a linked list of all extensions obtained after parsing ini configurations (including PHP extensions and Zend extensions), Get PHP extended lists and Zend extended lists by using &extension_lists. Engine and &extension_lists. Functions. The different types of extensions are then loaded by php_load_zend_extension_cb() or php_load_PHp_extension_CB (), respectively

void php_ini_register_extensions(void)
{
	// Register the Zend extension
    zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb);
	// Register the PHP extension
    zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);

    zend_llist_destroy(&extension_lists.engine);
    zend_llist_destroy(&extension_lists.functions);
}
Copy the code

(3) Extended life cycle

As you can see in the PHP Extensions and Zend extensions section, the two extensions provide different hook functions, which are called in the order shown in the following figure throughout the PHP life cycle

Five, extended development tutorial

1. Get the PHP source code

After obtaining the PHP source code, switch to version 7.1.19 and follow the following command

git clonehttps://github.com/php/php-src git checkout remotes/origin/PHP - 7.1.19Copy the code

2. Generate the base file for the extension

Switch to the ext extension directory, where you have an ext_skel script that can be used to automatically generate the base files for the extension. For example, to create an extension to print_hello, do the following

cd php-src/ext/
./ext_skel --extname=print_hello
Copy the code

After the command is executed successfully, the following message is displayed

The print_hello directory has been successfully generated and contains the following files

  • tests: Unit test directory
  • config.m4: Extended build configuration file (Unix)
  • config.w32: Extended build configuration file (Windows)
  • php_print_hello.h: Extension header file
  • print_hello.c: Extends the source file
  • print_hello.php: extended test file

3. Modify the compilation configuration file

Open the config.m4 configuration file, as shown below

Find the PHP_ARG_ENABLE code and delete the DNL macro, as shown below

# change before
dnl PHP_ARG_ENABLE(print_hello, whether to enable print_hello support,
dnl Make sure that the comment is aligned:
dnl [  --enable-print_hello           Enable print_hello support])

# modified
PHP_ARG_ENABLE(print_hello, whether to enable print_hello support,
Make sure that the comment is aligned:
[  --enable-print_hello           Enable print_hello support])
Copy the code

4. Modify the print_hello.c file

Find PHP_FUNCTION (which represents the defined extension function) and add one line of code that prints Hello World to the confirm_print_hello_compiled function as follows

PHP_FUNCTION(confirm_print_hello_compiled)
{
	char *arg = NULL;
	size_t arg_len, len;
	zend_string *strg;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
		return;
	}

	strg = strpprintf(0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP."."print_hello", arg); // Add php_printf()"hello world! \n");

  RETURN_STR(strg);
}
Copy the code

5. Build the extension

Compile processing of the extension by executing the following command

cd print_hello
phpize
./configure --with-php-config=/usr/bin/php-config
make
sudo make
Copy the code

The following information is displayed after the make command is executed successfully

The following information is displayed after the sudo make install command is executed successfully

6. Execute the extended test script

The test script dynamically loads the print_hello extension and prints all the functions provided in the extension. Finally, the confirm_print_hello_compiled function defined in the print_hello.c source file is executed, indicating that the extension is loaded and executed successfully

$br = (php_sapi_name() == "cli")? "":"<br>";

// Check whether the extension is loaded
if(! extension_loaded('print_hello')) {
    // Load the extension library dynamically at runtime
    // If the loading fails, you need to modify the php.ini configuration file. Directly enable the option enable_dl = On to dynamically load the extension
    dl('print_hello.' . PHP_SHLIB_SUFFIX);
}
$module = 'print_hello';

// Outputs all functions provided by the extension in turn
$functions = get_extension_funcs($module);
echo "Functions available in the test extension:$br\n";
foreach($functions as $func) {
    echo $func."$br\n";
}
echo "$br\n";

If the extension loads successfully, the confirm_print_hello_compiled function is executed
$function = 'confirm_' . $module . '_compiled';
if (extension_loaded($module)) {
	$str = $function($module);
} else {
	$str = "Module $module is not compiled into PHP";
}

echo "$str\n";

Copy the code

The following information is displayed after the script is successfully executed

In July and end

So far, a simple print_Hello extension has been developed, and of course you can define more extension functions in the print_hello.c source file and do more fun things! For advanced use of the extension, please check out the blog for the latest articles!