Brief introduction to the development of PHP Extension the basic part

  • 2020-03-31 21:24:54
  • OfStack

Abstract & introduction
PHP is currently widely used in a language, from foreign Facebook, Twitter to the domestic taobao, tencent, baidu and the Internet to a variety of large and medium-sized websites can see it. PHP's success is largely due to its open Extension API mechanism and rich PHP Extension components, which enable PHP to do everything from database operations to XML, JSON, encryption, file processing, graphics processing, sockets, etc. Sometimes developers may need to develop their own PHP extensions, and the current extension mechanism for PHP5 is based on the Zend API, which provides a rich set of interfaces and macro definitions, plus utilities that make PHP extensions not particularly difficult to develop. This article introduces you to the basics of PHP extension component development and shows you the basic process of developing PHP extensions through an example.

The development process for PHP extension components is different in Unix and Windows environments, but is basically interoperable, and this article will be based on Unix environments (specifically using Linux). Reading this article requires a brief understanding of some of the basics of Unix environments, PHP, and C. I'll try not to get too specific about operating systems and language features, and explain them where necessary for the reader.

The specific development environment of this article is Ubuntu 10.04 + PHP 5.3.3.

Download PHP source code
To develop a PHP extension, the first step is to download the PHP source code, because it has the tools you need to develop the extension. I downloaded the latest version of PHP, 5.3.3, in a tar.bz2 zip. Download address is: http://cn.php.net/get/php-5.3.3.tar.bz2/from/a/mirror.

After downloading, move the source code to the appropriate directory and unzip it. Unzip command:
tar -jxvf  Source package name  

If the download is tar.gz compression package, unzip the command as
tar -zxvf  Source package name  

After unzipping, there is an ext directory in the source directory, which is where the PHP extension is concerned. When you go into the directory and look at it with ls, you can see many existing extensions. The following is the result viewed in my environment:

At this time, if you use ls to view, you will find that there is an additional directory of "say_hello". If you enter this directory, you will find that ext_skel has established the basic framework of say_hello for us, as shown in the following figure:

(link: https://www.jb51.net/upload/201012/20101209214719849.png)

If you're too lazy to figure out the entire contents of the PHP extensions directory structure, there are three files you should be aware of:

Config.m4: this is the Build System configuration file for the Unix environment, through which configuration and installation will be generated later.

Php_say_hello.h: this file is the header of the extension module. Follow the C language style, this can place some custom structure, global variables, and so on.

Say_hello.c: this is the main program file for the extension module, where the final extension module function entries are located. Of course, you can cram all the code into it, or you can follow the idea of modularity and put the functional modules into different files.

The following content mainly revolves around these three files.

Unix Build System configuration

The first step in developing a PHP extension is not to write the implementation code, but to configure the Build System option. Since we are developing under Linux, the configuration here is mainly related to config.m4.

On the Build System configuration of this piece, if you write to write a lot of, and associated with the Unix System a lot of things, even if I'm interested in writing estimate you also not interested to see, so here we shortage, only pick key areas, about the config. M4 can (link: http://www.php.net/manual/en/internals2.buildsys.configunix.php) for more details.

Open the generated config.m4 file, and the contents are roughly as follows:

 
dnl $Id$ 
dnl config.m4 for extension say_hello 
dnl Comments in this file start with the string 'dnl'. 
dnl Remove where necessary. This file will not work 
dnl without editing. 
dnl If your extension references something external, use with: 
dnl PHP_ARG_WITH(say_hello, for say_hello support, 
dnl Make sure that the comment is aligned: 
dnl [ --with-say_hello Include say_hello support]) 
dnl Otherwise use enable: 
dnl PHP_ARG_ENABLE(say_hello, whether to enable say_hello support, 
dnl Make sure that the comment is aligned: 
dnl [ --enable-say_hello Enable say_hello support]) 
if test "$PHP_SAY_HELLO" != "no"; then 
dnl Write more examples of tests here... 
dnl # --with-say_hello -> check with-path 
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 
dnl SEARCH_FOR="/include/say_hello.h" # you most likely want to change this 
dnl if test -r $PHP_SAY_HELLO/$SEARCH_FOR; then # path given as parameter 
dnl SAY_HELLO_DIR=$PHP_SAY_HELLO 
dnl else # search default path list 
dnl AC_MSG_CHECKING([for say_hello files in default path]) 
dnl for i in $SEARCH_PATH ; do 
dnl if test -r $i/$SEARCH_FOR; then 
dnl SAY_HELLO_DIR=$i 
dnl AC_MSG_RESULT(found in $i) 
dnl fi 
dnl done 
dnl fi 
dnl 
dnl if test -z "$SAY_HELLO_DIR"; then 
dnl AC_MSG_RESULT([not found]) 
dnl AC_MSG_ERROR([Please reinstall the say_hello distribution]) 
dnl fi 
dnl # --with-say_hello -> add include path 
dnl PHP_ADD_INCLUDE($SAY_HELLO_DIR/include) 
dnl # --with-say_hello -> check for lib and symbol presence 
dnl LIBNAME=say_hello # you may want to change this 
dnl LIBSYMBOL=say_hello # you most likely want to change this 
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 
dnl [ 
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SAY_HELLO_DIR/lib, SAY_HELLO_SHARED_LIBADD) 
dnl AC_DEFINE(HAVE_SAY_HELLOLIB,1,[ ]) 
dnl ],[ 
dnl AC_MSG_ERROR([wrong say_hello lib version or lib not found]) 
dnl ],[ 
dnl -L$SAY_HELLO_DIR/lib -lm 
dnl ]) 
dnl 
dnl PHP_SUBST(SAY_HELLO_SHARED_LIBADD) 
PHP_NEW_EXTENSION(say_hello, say_hello.c, $ext_shared) 
fi 

This structure might look like a bit of a headache, but I'm going to explain what's in it. Because this is the prototype for PHP Extension, you can't develop PHP Extension without understanding it. Of course, I won't explain each field in pairs, just the key fields that will be used in this article, because many fields don't need to be filled in manually, but can be filled in with some predefined macros.

The seventh field, "name," is the name of this PHP Extension, which in this case is "say_hello."

The eighth field, "functions", will hold a reference to the function we defined in this extension. The specific structure will not be analyzed. Those interested can read the source code of _zend_function_entry. You'll have macros here when you write the code.

The fields 9-12 are four function Pointers that are called at the appropriate time: "when the extension module is loaded", "when the extension module is unloaded", "when each request begins", and "when each request ends". These four functions can be regarded as an interception mechanism, which is mainly used for resource allocation, release and other related operations at the corresponding time.

The 13th field "info_func" is also a function pointer to a function that is called when phpinfo() is executed to display custom module information.

The 14th field "version" is the version of the module.

(about zend_module_entry more detailed description please (link: http://www.php.net/manual/en/internals2.structure.modstruct.php))

With the above fields covered, we can look at the "say_hello_module_entry" framework code generated automatically in "say_hello_module_entry".

 
 
zend_module_entry say_hello_module_entry = { 
#if ZEND_MODULE_API_NO >= 20010901 
STANDARD_MODULE_HEADER, 
#endif 
"say_hello", 
say_hello_functions, 
PHP_MINIT(say_hello), 
PHP_MSHUTDOWN(say_hello), 
PHP_RINIT(say_hello),  
PHP_RSHUTDOWN(say_hello),  
PHP_MINFO(say_hello), 
#if ZEND_MODULE_API_NO >= 20010901 
"0.1",  
#endif 
STANDARD_MODULE_PROPERTIES 
}; 
 

First, the macro "STANDARD_MODULE_HEADER" generates the first six fields, and "STANDARD_MODULE_PROPERTIES" generates the field after the "version," so we don't have to worry about that yet. The fields we care about are filled out or generated by macros, and the frames of several functions are generated in the corresponding positions of "say_hello.c". Note that the arguments to several macros are "say_hello", but this does not mean that the names of several functions are all "say_hello", and there is no possibility of function name overloading in C. In fact, in the process of developing PHP Extension, almost everywhere are used in the Zend predefined macro, from the definition of a global variable to the function return value, even can't in accordance with the "naked" way to write the C language, this is because PHP running mechanism may lead to problems such as naming conflicts, and elements such as these macros will function transformation into an internal name, but these for programmers is transparent (unless you read the macro code), we through a variety of macro programming, while macro for us to deal with a lot of things inside.

At this point, our task becomes clear: first, if we need to process something at the appropriate time, we need to populate the contents of the interceptor functions; Second, write a functional function for say_hello and add the reference to say_hello_functions.

Write the phpinfo() callback function

Because the say_hello extension doesn't need to do anything during each lifecycle phase, we'll just write the contents of info_func, which, as mentioned above, will be called automatically when phpinfo() executes to display the extension's information. Four functions are used to write this function:

Php_info_print_table_start () -- start the phpinfo table. No parameters.

Php_info_print_table_header () -- output table header. The first parameter is an int, indicating the number of columns in the header, and the next parameter is a char* type parameter with the same number of columns used to specify the text to be displayed.

Php_info_print_table_row () -- output the table contents. The first argument is an int, indicating the number of columns in the row, and the next argument is a char* type argument with the same number of columns to specify the text to be displayed.

Php_info_print_table_end () -- ends the phpinfo table. No parameters.


The following is the specific code of info_func to be written in "say_hello.c" :

 
 
PHP_MINFO_FUNCTION(say_hello) 
{ 
php_info_print_table_start(); 
php_info_print_table_header(2, "say_hello support", "enabled"); 
php_info_print_table_row(2, "author", "Zhang Yang");  
php_info_print_table_end(); 

 
} 
 

You can see that we've written two lines of content, whether the component is available, and author information.

Writing core functions
Write the core function, there are three steps: 1, using the macro PHP_FUNCTION to define the function body; 2. Use the macros ZEND_BEGIN_ARG_INFO and ZEND_END_ARG_INFO to define parameter information; Use the macro PHP_FE to add functions to say_hello_functions. Step by step.

Define the body of the function using the macro PHP_FUNCTION
 
PHP_FUNCTION(say_hello_func) 
{ 
char *name; 
int name_len; 

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) 
{ 
return; 
} 
php_printf("Hello %s!", name); 

RETURN_TRUE; 
} 

As mentioned above, when you write PHP extensions, you can't write almost anything naked. You must use macros instead. You can see this clearly from the code above. In general, the core function code consists of the following parts:

Define the function, this step through the macro PHP_FUNCTION, the external name of the function is the name behind the macro in parentheses.

Declare and define local variables.

This step is accomplished by the zend_parse_parameters function, which reads the data from the input stack of the function user and then converts it into the corresponding function parameter to fill in the variable for use by the following core function code. The first parameter of zend_parse_parameters is the number of parameters passed in by the user, which can be generated by the macro "ZEND_NUM_ARGS() TSRMLS_CC". The second parameter is a string, where each letter represents a variable type, we only have a string variable, so the second parameter is "s"; Finally, some necessary local variable Pointers are needed for each parameter to store data. The following table shows the letter representatives of different variable types and their required local variable Pointers.

(link: https://www.jb51.net/upload/201012/20101209214719870.png)

After the parameter parsing is completed, it is the core function code. We only output one line of characters here. Php_printf is the Zend version of printf.

The final return value is also implemented through macros. The RETURN_TRUE macro returns the Boolean value "true".

The parameter information is defined using the macros ZEND_BEGIN_ARG_INFO and ZEND_END_ARG_INFO

Parameter information is a necessary part of the function. Without further study, the corresponding code is directly given:

 
ZEND_BEGIN_ARG_INFO(arginfo_say_hello_func, 0) 
ZEND_END_ARG_INFO() 

For more information, read the macro definition.

Use the macro PHP_FE to add the function to say_hello_functions
Finally, we need to add the function and parameter information just defined to the say_hello_functions array. The code is as follows:
 
const zend_function_entry say_hello_functions[] = { 
PHP_FE(say_hello_func, arginfo_say_hello_func) 
{NULL, NULL, NULL} 
}; 

This step is implemented through the PHP_EF macro, note that the last line of the array must be {NULL, NULL, NULL}, please do not delete.

Here is the complete say_hello.c code:
 
/* 
+----------------------------------------------------------------------+ 
| PHP Version 5 | 
+----------------------------------------------------------------------+ 
| Copyright (c) 1997-2010 The PHP Group | 
+----------------------------------------------------------------------+ 
| This source file is subject to version 3.01 of the PHP license, | 
| that is bundled with this package in the file LICENSE, and is | 
| available through the world-wide-web at the following url: | 
| http://www.php.net/license/3_01.txt | 
| If you did not receive a copy of the PHP license and are unable to | 
| obtain it through the world-wide-web, please send a note to | 
| license@php.net so we can mail you a copy immediately. | 
+----------------------------------------------------------------------+ 
| Author: | 
+----------------------------------------------------------------------+ 
*/ 

 

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

#include "php.h" 
#include "php_ini.h" 
#include "ext/standard/info.h" 
#include "php_say_hello.h" 

 

 
static int le_say_hello; 

 
PHP_FUNCTION(say_hello_func) 
{ 
char *name; 
int name_len; 

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) 
{ 
return; 
} 
php_printf("Hello %s!", name); 

RETURN_TRUE; 
} 

ZEND_BEGIN_ARG_INFO(arginfo_say_hello_func, 0) 
ZEND_END_ARG_INFO() 
 

 
const zend_function_entry say_hello_functions[] = { 
PHP_FE(say_hello_func, arginfo_say_hello_func) 
{NULL, NULL, NULL}  
}; 
 

 
zend_module_entry say_hello_module_entry = { 
#if ZEND_MODULE_API_NO >= 20010901 
STANDARD_MODULE_HEADER, 
#endif 
"say_hello", 
say_hello_functions, 
NULL, 
NULL, 
NULL, 
NULL, 
PHP_MINFO(say_hello), 
#if ZEND_MODULE_API_NO >= 20010901 
"0.1",  
#endif 
STANDARD_MODULE_PROPERTIES 
}; 
 

#ifdef COMPILE_DL_SAY_HELLO 
ZEND_GET_MODULE(say_hello) 
#endif 

 
PHP_MINFO_FUNCTION(say_hello) 
{ 
php_info_print_table_start(); 
php_info_print_table_header(2, "say_hello support", "enabled"); 
php_info_print_table_row(2, "author", "Zhang Yang");  
php_info_print_table_end(); 

 
} 
 

Compile and install the extension
Enter the following command in the say_hello directory:
 
/usr/bin/phpize 
./configure 
make 
make install 

This completes the installation of the say_hello extension, if no errors are reported.

If you go to the directory where the PHP extension is located, you will find an additional say_hello.so file. As shown in the following figure:

(link: https://www.jb51.net/upload/201012/20101209214719360.png)

The next step is to add it to the php.ini configuration, and then restart Apache if needed. These are the basic PHP configurations that I won't go into.

Extension test

If the above completes successfully, run phpinfo() and you should see the following information:

(link: https://www.jb51.net/upload/201012/20101209214719129.png)

This indicates that the extension has been installed successfully. Then we wrote a test PHP script:

 
<?php 
say_hello_func('Zhang Yang'); 
?> 

Execute the script and the result is as follows:

(link: https://www.jb51.net/upload/201012/20101209214719858.png)

Indicates that the extension is working properly.

conclusion


Related articles: