Home  >  Article  >  Backend Development  >  PHP extension development-v1.0_PHP tutorial

PHP extension development-v1.0_PHP tutorial

WBOY
WBOYOriginal
2016-07-14 10:08:42890browse

1 Preface
This article is my personal study notes. I send this article to all my colleagues and students who like the PHP language and PHP extension development.
2 Preparation
If you want to learn PHP extension development, I believe you already have a certain understanding of the basic knowledge of PHP. PHP extensions are written in C language, if you don’t know C language yet
There is something pointer-like in it. I suggest you move on and learn C language. Everyone is criticizing Tan Haoqiang's "C Language Programming", but I still recommend that you read this book carefully.
Before doing PHP development, you must first prepare the environment. It's easy to say, but it may also be a nightmare for some novices. It is recommended that you install UBUNTU LINUX. There are many Linux installation tutorials on the Internet. I believe I don’t need to say more.
We assume that you have installed Linux, I used debain, but Ubuntu students don’t need to worry, because ubuntu is a variation of debain, and the so-called principle remains unchanged. I believe you can achieve the same effect by using ubuntu.
2.1 Development environment setup
First, let’s install the php development environment: PHP+APACHE2+MYSQL
Installation under UBUNTU is very simple, so simple that you only need to execute the following command:
sudo apt-get install apache2
sudo apt-get install php5
sudo apt-get install libapache2-mod-php5
sudo apt-get install mysql-server
sudo apt-get install libapache2-mod-auth-mysql
sudo apt-get install php5-mysql
sudo /etc/init.d/apache2 restart
2.2 Source code deployment
To carry out PHP extension development, we also need to download a PHP source code. We use PHP5.3 as an example to explain PHP expansion development. Below are some download addresses. In order to prevent the download link from becoming invalid, I will continue to update the link. Of course The PHP version will not change^_^
http://cn2.php.net/get/php-5.3.19.tar.gz/from/this/mirror
You can also go to www.php.net and click download to download the source code.
Assuming that you have or obtained the source code, let’s decompress the source code below tar xzvf php-5.3.19 If this command cannot decompress correctly, please use tar xvf php-5.3.19 to try, if not, use rar Unzip it, I believe you can always unzip this file~ What a big deal-_ -
Our PHP extension is developed on Linux. If you want to develop it on Win, this article may not be suitable for you. But things like technology are all based on analogy. I hope you can read my article after deploying the environment on win.
3 Everything starts from HelloWorld
I still remember the first time I wrote code, I printed out HelloWorld in C language. I was so excited that day. I hope you will write a helloworld php extension yourself after reading the content of this page.
How to write the first extension program?
It’s actually very simple~~~ Let’s get started~~
Last time we said that our first step is to uninstall the source code of php. Have you downloaded it?
Suggestion: It is recommended that you only install one PHP when developing. In fact, you can install multiple PHP in a system, but in order to avoid many unnecessary explanations in the early stage, readers are best to install one. Once you become proficient in PHP, multiple PHPs will be no longer a problem.
3.1 Enter the extension directory cd php-5.3.19/ext
The ext folder stores the source code of the php extension. Similarly, when we write extensions, we also need to do it here
You can take a look at the names of each folder under the ext folder: mysql, xml, zip, sqlite... These are all extensions of php
3.2 Establish an extension development framework ./ext_skel --extname=helloworld
Execute the above command to establish the framework for expansion development. You will find that there is an additional folder called helloworld under the ext folder, and some text is also output from the command line,
Let’s put these things here without explanation. Later we will take a look at what the output commands mean.
This reporter’s picture will be analyzed below
3.3 Enter the root directory of the php source code and edit the file vi ext/helloworld/config.m4
Remove a few lines of comments in this file
The so-called uncommenting means removing the dnl in front of lines 16~18 (the number of lines in the php source code of different versions may be different, please pay attention)
After canceling the comment, save the file and exit
3.4 Execute the command ./buildconf --force in the php root directory. This step will be explained in subsequent articles
3.5 Compile the php program in the root directory of the php source code. Note that the command is ./configure--with-helloworld
3.6 Enter our expansion directory helloworld and execute the command phpize
3.7 Compile our extension in the helloworld directory./configure --with-php-config=/usr/local/bin/php-config (use the php-config of your own environment) --enable-helloworld
3.8 Execute the make command make
When I executed make, the system reported an error and the error message was
/home/work/src/php-5.3.19/ext/helloworld/helloworld.c:43: error:'PHP_FE_END' undeclared here (not in a function)
make: *** [helloworld.lo] Error 1
At this time, please tell me how to replace PHP_FE_END in line 43 of helloworld.c with {NULL, NULL, NULL}
After the replacement is completed, please execute the make command to recompile
3.9 Install our extension
Some students may know that PHP programs can be installed using makeinstall, but in order to better understand the working principle of PHP,
We use manual installation of php extensions. In fact, the installation of php is very simple
3.9.1 Find the php extension installation directory
work@baidu:~$ php -r "phpinfo();" | grep extension_dir
extension_dir => /usr/lib/php5/20090626+lfs =>/usr/lib/php5/20090626+lfs
We need to install our extension into the /usr/lib/php5/20090626+lfs directory,
Execute the command under the helloworld directory
sudo cpmodules/helloworld.so /usr/lib/php5/20090626+lfs The directory /usr/lib/php5/20090626+lfs needs to be changed according to your actual situation
3.9.2 Open our extension in php.ini
Similarly, please execute the following command
work@baidu:~$ php -r "phpinfo();" | grep "php.ini"
Configuration File (php.ini) Path => /etc/php5/cli
Loaded Configuration File => /etc/php5/cli/php.ini
It can be seen that the path of php.ini is /etc/php5/cli/php.ini
sudo vi /etc/php5/cli/php.ini
Add the code extension=helloworld.so
to the last line of the file
3.10 Verify whether our extension is successfully installed
You can verify by executing the following command php -r "phpinfo();" | grep helloworld
If you installed it successfully, you should see the following message
root@baidu:/usr/lib/php5/20090626+lfs# php -r "phpinfo();" |grep helloworld
helloworld
helloworld support => enabled
At this point, the php extension installation is completed
Maybe you will ask, how should I print helloworld? Don’t worry, I will follow the helloworld program below.
3.11 Enter the extended helloworld directory, edit the file php_helloworld.h, and add the function PHP_FUNCTION(fun_helloworld);
in the last line
3.12 Implement our function in helloworld.c
Add fun_helloworld function to helloworld_functions[]
3.13 Compile extension make
3.14 Follow step 9-2 to reinstall our extension
3.15 Verification extension function
php -r "echo fun_helloworld();"
If everything goes well, you can see our greeting Hello World!
3.16 What is written at the back
Maybe you followed my steps to the end, and you saw our greeting hello world. Maybe you didn’t, but I have tried my best to describe the development process in as much detail as possible. Please don’t scold me. In fact, the process of program development is a process of constantly solving problems. If you encounter problems in the above steps, then I want to congratulate you, because you have got an opportunity to improve your abilities. In the process of solving problems, Baidu and Google are our best helpers. Although you can also solve problems through other tutorials or official documents, Baidu and Google are the fastest ways for novices to solve problems.
Having said all this, I just want to tell everyone: Don’t be afraid of encountering problems during the learning process, because these problems are the cornerstone of our progress.
4 Introduction to SAPI
If you want to write PHP extensions well, it is definitely indispensable to understand SAPI. Maybe you still can’t master SAPI well after reading this section, but it doesn’t matter. This will not affect your subsequent PHP extension development. However, I recommend that readers still read this section carefully. If you have If you have any questions, please contact me.
SAPI (ServerApplication Programming Interface) refers to the PHP specific application programming interface. There are many ways to execute PHP scripts. The most common ones are to put PHP on the web server and execute it directly through php test.php on the command line. In fact, PHP scripts can also be embedded in other programs for execution.
Currently common sapi implementation methods include apache module, fpm, cgi, cli, etc., and these implementations can be divided into three categories: single-process SAPI, multi-process SAPI, and multi-threaded SAPI.
4.1 Single-process SAPI
CLI/CGI mode PHP belongs to the single-process SAPI implementation method (or called implementation model). This type of request is processed once and then closed. Its life cycle is as follows:
Please remember: all the above processes will be performed on every request.
Let me explain to you: If you use the command line to run a php file or access a php file in cgi mode through a web server, you will go through the following process for every request:
1. Execute each extended MINIT method
2. Execute the RINIT method of each extension
3. Execute the php file you want to access
4. Execute the RSHUTDOWN method of each extension
5. Perform cleaning work (for the php file you want to execute)
6. Execute the MSHUTDOWN method of each extension
7. PHP execution ends
Have you seen MINIT, RINIT,RSHUTDWON, MSSHUTDOWN? Do they look familiar?
4.2 Multi-process SAPI
If PHP is compiled into a module of apache to handle PHP requests, then apache will generally adopt a multi-process mode. After apache is started, multiple sub-processes will be created. Each sub-process may handle multiple requests throughout its life cycle. These child processes will only be shut down when apache is shut down. The SAPI life cycle of this mode is as follows:
Explanation: The red part at the top of the above picture will automatically run after apache forks the child process, and the blue color in the middle indicates that the part is executed once for each request. A request can only be processed by one child process. The red part at the bottom of the above picture is red. Part of it is executed when the child process ends. For each process, the MINIT function will only be executed once, but the RINIT RSHUTDOWN function will be executed for each request, and the MSHUTDOWN function will be executed when the process ends.
4.3 Multi-threaded SAPI
Multi-threading mode is similar to a process in multi-process. The difference is that the entire process will repeat the request start->request end in parallel during the declaration cycle. Its life cycle is as follows:
We will explain in turn the request processing process of Apache, the processing process of PHP files, and the PHP extension execution process. If you want to understand extensions in depth and achieve success in extension development, then I suggest you read this section in detail. Maybe you don’t understand it, but it doesn’t matter. We will explain it in detail in the future.
5 php extension calls php.ini configuration
In this section we read the configuration in the php.ini file. In fact, there are many ways to read the php.ini configuration. This time we will only talk about one. If you are interested, you can study it individually.
Without further ado, let’s change the previous explanation method. This time we will go directly to the code. (Actually, it’s because the php extension is too simple to read the configuration file)
(Suppose we create an extension of ini_read, and the changed parts of the code are marked in yellow
5.1 Change configuration
Add the configuration ini_read.helloworld=hellohellohello in php.ini
5.2 Header file changes
The changes to php_ini_read.h are as follows: (I use yellow color for the changes)
#ifndef PHP_INI_READ_H
#define PHP_INI_READ_H
externzend_module_entry ini_read_module_entry;
#define phpext_ini_read_ptr &ini_read_module_entry
#ifdef PHP_WIN32
# define PHP_INI_READ_API __declspec(dllexport)
#elif defined(__GNUC__) &&__GNUC__ >= 4
# define PHP_INI_READ_API __attribute__((visibility("default")))
#else
# define PHP_INI_READ_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(ini_read);
PHP_MSHUTDOWN_FUNCTION(ini_read);
PHP_RINIT_FUNCTION(ini_read);
PHP_RSHUTDOWN_FUNCTION(ini_read);
PHP_MINFO_FUNCTION(ini_read);
 
/*
   Declare any global variables you may need between the BEGIN
   and END macros here:    
 
ZEND_BEGIN_MODULE_GLOBALS(ini_read)
   long  global_value;
   char *global_string;
ZEND_END_MODULE_GLOBALS(ini_read)
*/
 
/* In every utility function you addthat needs to use variables
  in php_ini_read_globals, call TSRMLS_FETCH(); after declaring other
  variables used by that function, or better yet, pass in TSRMLS_CC
  after the last function argument and declare your utility function
  with TSRMLS_DC after the last declared argument.  Always refer to
  the globals in your function as INI_READ_G(variable).  You are
  encouraged to rename these macros something shorter, see
  examples in any other php module directory.
*/
 
#ifdef ZTS
#define INI_READ_G(v)TSRMG(ini_read_globals_id, zend_ini_read_globals *, v)
#else
#define INI_READ_G(v)(ini_read_globals.v)
#endif
 
#endif  /* PHP_INI_READ_H */
 
 
/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
 
PHP_FUNCTION(helloworld);
 
5.3     源文件改动
ini_read.c的改动如下
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
 
#include "php.h"
#include "php_ini.h"
#include"ext/standard/info.h"
#include "php_ini_read.h"
 
/* If you declare any globals inphp_ini_read.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(ini_read)
*/
 
/* True global resources - no need forthread safety here */
static int le_ini_read;
 
/* {{{ ini_read_functions[]
 *
 * Every user visible function must have anentry in ini_read_functions[].
 */
constzend_function_entry ini_read_functions[]= {
   PHP_FE(helloworld, NULL)      /* Fortesting, remove later. */
   {NULL, NULL, NULL}
};
/* }}} */
 
/* {{{ ini_read_module_entry
 */
zend_module_entry ini_read_module_entry={
#if ZEND_MODULE_API_NO >= 20010901
   STANDARD_MODULE_HEADER,
#endif
   "ini_read",
   ini_read_functions,
   PHP_MINIT(ini_read),
   PHP_MSHUTDOWN(ini_read),
   NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
   "0.1",/* Replace with version number for yourextension */
#endif
   STANDARD_MODULE_PROPERTIES
};
/* }}} */
 
#ifdef COMPILE_DL_INI_READ
ZEND_GET_MODULE(ini_read)
#endif
 
/* {{{ PHP_INI
 */
PHP_INI_BEGIN()
    PHP_INI_ENTRY("ini_read.helloworld", "foobar",PHP_INI_ALL, NULL)
PHP_INI_END()
/* }}} */
 
/* {{{ php_ini_read_init_globals
 */
/* Uncomment this function if you haveINI entries
static voidphp_ini_read_init_globals(zend_ini_read_globals *ini_read_globals)
{
   ini_read_globals->global_value = 0;
   ini_read_globals->global_string = NULL;
}
*/
/* }}} */
 
/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(ini_read)
{
   REGISTER_INI_ENTRIES();
   return SUCCESS;
}
/* }}} */
 
/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(ini_read)
{
   UNREGISTER_INI_ENTRIES();
   return SUCCESS;
}
/* }}} */
 
/* Remove if there's nothing to do atrequest start */
/* {{{ PHP_RINIT_FUNCTION
 */
PHP_RINIT_FUNCTION(ini_read)
{
   return SUCCESS;
}
/* }}} */
 
/* Remove if there's nothing to do atrequest end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
 */
PHP_RSHUTDOWN_FUNCTION(ini_read)
{
   return SUCCESS;
}
/* }}} */
 
/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(ini_read)
{
   php_info_print_table_start();
   php_info_print_table_header(2,"ini_read support","enabled");
   php_info_print_table_end();
 
   /* Remove comments if you have entries in php.ini
   DISPLAY_INI_ENTRIES();
   */
}
/* }}} */
 
/* }}} */
/* The previous line is meant for vimand emacs, so it can correctly fold and
  unfold functions in source code. See the corresponding marks just before
  function definition, where the functions purpose is also documented.Please
  follow this convention for the convenience of others editing your code.
*/
 
 
/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
 
PHP_FUNCTION(helloworld)
{
   RETURN_STRING(INI_STR("ini_read.helloworld"),1);
}
 
5.4     结果验证
上面步骤执行完了后,根据之前安装扩张的方式安装一下我们的扩展吧。
如果你安装完了扩展,那么运行下面的命令您就可以看到输出了
php -r "echohelloworld();" 此时的输出应该为hellohellohello
6      全局变量
6.1     抛砖引玉
将这一节的目的是想交给大家,如和声明一个变量。使得这个变量针对每次请求独立,也就是说,同一次请求我们访问的变量是同一个,不同的请求我们使用的变量不是同一个。
说道这里我先抛出一个问题:既然要实现上面的要求,那么我们该怎么办呢?我应该在哪里声明我的全局变量呢?
还记得SAPI简介那一张吗?SAPI的实现有三种方式,单进程,多进程,多线程,但是对于每一次而言,都必须执行的几个过程为RINIT RSHUTDOWN….说道这里你意识到了吗。我们是不是在RINIT过程的时候初始化我们的全局常量,这样每次请求的时候这个变量的值都会变成默认值。还没有看明白?看看下面的流程你也许就懂了。
1.      首先在.h文件中声明全局变量
2.      在RINIT过程时初始化这个变量
3.      调用变量
6.2     实现方式
我们还是直接上源代码:我把一些无用的注释去掉了,希望大家别介意
头文件php-iamnew.h
#ifndef PHP_IAMNEW_H
#define PHP_IAMNEW_H
 
extern zend_module_entry iamnew_module_entry;
#define phpext_iamnew_ptr &iamnew_module_entry
 
#ifdef PHP_WIN32
#   definePHP_IAMNEW_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#   definePHP_IAMNEW_API __attribute__ ((visibility("default")))
#else
#   definePHP_IAMNEW_API
#endif
 
#ifdef ZTS
#include "TSRM.h"
#endif
 
PHP_MINIT_FUNCTION(iamnew);
PHP_MSHUTDOWN_FUNCTION(iamnew);
PHP_RINIT_FUNCTION(iamnew);
PHP_RSHUTDOWN_FUNCTION(iamnew);
PHP_MINFO_FUNCTION(iamnew);
 
PHP_FUNCTION(confirm_iamnew_compiled); /* For testing, remove later. */
 
ZEND_BEGIN_MODULE_GLOBALS(iamnew)
    long  counter;
ZEND_END_MODULE_GLOBALS(iamnew)
 
#ifdef ZTS
#define IAMNEW_G(v) TSRMG(iamnew_globals_id,zend_iamnew_globals *, v)
#else
#define IAMNEW_G(v) (iamnew_globals.v)
#endif
 
#endif
 
PHP_FUNCTION(test_global_value);
源文件iamnew.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
 
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_iamnew.h"
 
ZEND_DECLARE_MODULE_GLOBALS(iamnew) //声明全局变量
 
static int le_iamnew;
 
const zend_function_entry iamnew_functions[]= {
    PHP_FE(test_global_value,NULL)
    {NULL,NULL,NULL} //此处修改以后不再解释
};
 
zend_module_entry iamnew_module_entry ={
#if ZEND_MODULE_API_NO >= 20010901
   STANDARD_MODULE_HEADER,
#endif
    "iamnew",
    iamnew_functions,
    PHP_MINIT(iamnew),
    PHP_MSHUTDOWN(iamnew),
    PHP_RINIT(iamnew),
    PHP_RSHUTDOWN(iamnew),
    PHP_MINFO(iamnew),
#if ZEND_MODULE_API_NO >= 20010901
"0.1",
#endif
    STANDARD_MODULE_PROPERTIES
};
 
#ifdef COMPILE_DL_IAMNEW
ZEND_GET_MODULE(iamnew)
#endif
 
// 这个函数之前是被注释的,去掉注释,并且函数内容为空即可
static void php_iamnew_init_globals(zend_iamnew_globals*iamnew_globals)
{
   
}
 
PHP_MINIT_FUNCTION(iamnew)
{
    ZEND_INIT_MODULE_GLOBALS(iamnew, php_iamnew_init_globals,NULL);
    return SUCCESS;
}
 
PHP_MSHUTDOWN_FUNCTION(iamnew)
{
    return SUCCESS;
}
 
PHP_RINIT_FUNCTION(iamnew)
{
    IAMNEW_G(counter)= 0; //初始化
    return SUCCESS;
}
 
PHP_RSHUTDOWN_FUNCTION(iamnew)
{
    return SUCCESS;
}
 
PHP_MINFO_FUNCTION(iamnew)
{
   php_info_print_table_start();
   php_info_print_table_header(2,"iamnew support","enabled");
   php_info_print_table_end();
}
 
// 增加测试函数
PHP_FUNCTION(test_global_value)
{
    IAMNEW_G(counter)++;
    RETURN_LONG(IAMNEW_G(counter));
}
 
6.3     结果验证
修改完成后,编译安装我们的扩展,执行下面的命令进行测试
php -r "echotest_global_value(); test_global_value();"
以后我们可能不在对这些简单的结果进行验证了,但是为了学习效果,建议大家自己验证一下。
6.4     实现原理
6.4.1    知识点1
我们先看一下头文件中声明全局变量的两个宏
ZEND_BEGIN_MODULE_GLOBALS
ZEND_END_MODULE_GLOBALS
我们看一下这两个宏的展开内容:
#define ZEND_BEGIN_MODULE_GLOBALS(module_name)      
   typedef struct _zend_##module_name##_globals {
#define ZEND_END_MODULE_GLOBALS(module_name)        
    }zend_##module_name##_globals;
从展开信息中我们可以看到,这两个宏仅仅是定了一个叫做zend_##module##_globals的结构体,而ZEND_BEGIN_MODULE_GLOBALS和ZEND_END_MODULE_GLOBALS就是结构体zend_##module##_globals的成员变量。
6.4.2    知识点2
我们再来看一下ZEND_DECLARE_MODULE_GLOBALS这句话是
#define ZEND_DECLARE_MODULE_GLOBALS(module_name)                            
ts_rsrc_idmodule_name##_globals_id;
其实ts_rsrc_id就是int类型,查看ts_rsrc_id的定义就可以知道:typedef int ts_rsrc_id;
6.4.3    知识点3
在一个宏声明ZEND_INIT_MODULE_GLOBALS,查看一下这个宏的定义
#define ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor,globals_dtor)  
ts_allocate_id(&module_name##_globals_id,sizeof(zend_##module_name##_globals), (ts_allocate_ctor) globals_ctor,(ts_allocate_dtor) globals_dtor);
From the definition, ZEND_INIT_MODULE_GLOBALS assigns an id to the variable module_name##_globals_id, which is a thread-safe resource id. And module_name##_globals_id is not the variable assigned by ZEND_DECLARE_MODULE_GLOBALS!
globals_ctor is a callback function pointer, no more details will be said here.
6.4.4 Knowledge Points
Let’s take a look at the definition of the IAMNEW_G macro
#ifdef ZTS //Is it thread safe
#defineIAMNEW_G(v) TSRMG(iamnew_globals_id,zend_iamnew_globals *, v)
#else
#defineIAMNEW_G(v) (iamnew_globals.v)
#endif
-------------------------------------------------- -----
TSRMG is defined as:
#define TSRMG(id, type, element) (((type) (*((void ***)tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
tsrm_ls is defined as:
void ***tsrm_ls;
It can be seen from the overall definition of the macro that IAMNEW_G obtains the value of the variable.
In fact, IAMNEW_G is the value of the global variable.
If you are very interested in TSRMG, you can also take a look at the specific implementation of this macro. It is indeed a bit difficult for novices.
7 Initialize global variables using the configuration in php.ini
Earlier we talked about how to read the configuration in php.ini and how to initialize global variables. The task of this section is also very clear, which is how to initialize global variables using the configuration in php.ini. We will handle this section as a classroom task and will not explain it separately.
8 Parameter reception
Earlier we talked about the general architecture of PHP extension development. In this section we introduce how the extension receives the parameters passed in the PHP script.
8.1 Normal parameter reception
Task: Write an extension to output the parameters passed in by the php script. For example, php –r “echo hello(‘param test.’);” will output param test.
First we create an extension of paramtest. The suggestions, compilation, installation, and testing processes will not be described again.
First of all, we need to create a function called hello. The creation process is the same as before, so there is no need to go into details.
Let’s take a closer look at the implementation of the hello function. The implementation of the hello function in paramtest.c is as follows:
PHP_FUNCTION(hello)
{
char* str_hello;
int int_hello_str_length;
if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"s",
&str_hello,&int_hello_str_length)==FAILURE)
{
RETURN_NULL();
}
php_printf("%s",str_hello);
RETURN_TRUE;
}
Let’s first take a look at the function definition of zend_parse_parameters: ZEND_NUM_ARGS() TSRMLS_CC is actually the two parameters of zend_parse_parameters. Specifically, you can find the definitions of these two macros. These two macros are mainly used to pass in parameter information and ensure thread safety. The parameter "s" is actually a format string, which means that this parameter tells the ZEND compiler what the parameter types I can receive look like. Other parameters of Zend_parse_parameters are responsible for specifically receiving the values ​​of function variables in the php script.
Please note: We passed in one parameter in the function of the php script, but two parameters are needed to receive it in zend_parse_parameters. Let me explain the reason: the parameter we passed in the php script is a string. For C language, the length of the string passed in by the PHP script cannot be obtained directly using the function strlen. The reason is because we can pass in the php script
In zend_parse_parameters, there are many formatting characters (in this case "s"). If you want to accept multiple parameters, just add the corresponding type identifier to the formatting parameter and add Just accept the variable. For example, if your php function needs to pass in two variables, the first variable is the greeting string, and the second parameter is bool, indicating whether to output, then the zend_parse_parameters function can be written like this: zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"sb ", &str_hello, &len,&is_output).
Among them, the format string can be listed as follows:
PHP variable type
Code
C extended variable type
boolean
b
zend_bool
long
l
long
double
d
double
string
s
char*, int
resource
r
zval*
array
a
zval*
object
o
zval*
zval
z
zval*
There are many zval types in the above table. We will introduce this type separately in the next section.
Another thing to note is that we use php_printf to output in the C function. Can you think of the reason? We have mentioned before that php can be run as a script on the command line, or it can be run through a web server in a single process, multi-process, or multi-thread mode. If we output our information to stdout in the web server, it will cause The information cannot be output or is output incorrectly. To give you an example, we can write a script in an apache+php environment and access this script through a browser. If you use printf to output a string, the browser will not be able to see your output information. But if you use php_printf to output, the browser can display the information you output.
8.2 Optional parameter reception
We have learned how to receive ordinary parameters. Everyone knows that there are optional parameters in php, so how do we receive optional parameters? (If you don’t know that php can use optional parameters, it is recommended that you review the basic knowledge of php first.)
This is very simple, let’s go directly to the code:
PHP_FUNCTION(hello)
{
char*str_hello;
int int_hello_str_length;
zend_bool is_output =0;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s|b",&str_hello,
&int_hello_str_length,&is_output)== FAILURE)
{
RETURN_NULL();
}
if(is_output)
{
php_printf("%s", str_hello);
}
RETURN_TRUE;
}
It’s very simple. If you have optional parameters, just add | in the format string. The ones after | are optional parameters. The rest are received the same as ordinary variables.
9 Zval structure analysis
9.1 First acquaintance with zval
Let’s take a look at the definition of zval first:
typedef union_zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
struct _zval_struct {
/* Variable information*/
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /*active type */
zend_uchar is_ref__gc;
};
typedef struct_zval_struct zval;
9.2 Creation and use of zval
Let’s look at a piece of code first:
PHP_FUNCTION(hello)
{
zval* t;
ALLOC_INIT_ZVAL(t);
Z_TYPE_P(t)= IS_LONG;
Z_LVAL_P(t)= 1234567890;
zval_ptr_dtor(&t);
}
ALLOC_INIT_ZVAL macro is used to allocate memory to t and initialize t to an empty variable. Z_TYPE_P is used to specify the type of the variable for the zval variable. Z_LVAL_P is used to assign a value to the variable. zval_ptr_dtor is used to clean up the variable space.
In addition to using the above code to use variables, we can also use the macro ZVAL_LONG to quickly define variables and assign values ​​to variables. In other words, we can use the following code instead of the above code.
PHP_FUNCTION(hello)
{
zval* t;
ZVAL_LONG(t, 1234567890);
}
We can use the following macro to quickly define and use zval variables:
ZVAL_RESOURCE、 ZVAL_BOOL、ZVAL_NULL、ZVAL_LONG、ZVAL_DOUBLE
ZVAL_STRING, ZVAL_STRINGL, ZVAL_EMPTY_STRING, ZVAL_ZVAL
The above macro is very simple. If you don’t understand anything, you can take a look at the source code.
10 Function return value
Finally finished talking about zval. Earlier we talked about the definition and use of functions, but we did not talk about the return value of the function. Because in the C extension, the return value type of the function is zval, so we put this section here to explain.
Assignments in this section, we write a simple calculator to complete addition, subtraction, multiplication and division operations. Requirements: Write the function calculate(num1, num2, opt). We hope to complete num1 opt num2=? operation. Let’s take a look at the code that accomplishes the above task:
PHP_FUNCTION(calculate)
{
int num1;
int num2;
char* opt;
int opt_len;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"lls",
&num1,&num2,&opt,&opt_len)==FAILURE)
{
php_printf("param error. example: calculate(123, 456, '+')n");
RETURN_NULL();
}
if(1!= opt_len)
{
php_printf("param error. example: calculate(123, 456, '+')n");
RETURN_NULL();
}
switch (opt[0])
{
case '+':
return_value->type= IS_LONG;
return_value->value.lval= num1 + num2;
break;
case '-':
return_value->type= IS_LONG;
return_value->value.lval= num1 - num2;
break;
case '*':
return_value->type= IS_LONG;
return_value->value.lval= num1 * num2;
break;
case '/':
return_value->type= IS_DOUBLE;
                                                                                                                                                                                                           .
break;
default:
return_value->type= IS_LONG;
return_value->value.lval= 0;
break;
}
}
Seeing the above code, I wonder if you have any doubts about where return_value comes from?
return_value is defined in your macro PHP_FUNCTION. PHP_FUNCTION will declare this variable and assign this variable to NULL. Let’s take a look at the macro definition:
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define INTERNAL_FUNCTION_PARAMETERSint ht, zval *return_value,
zval **return_value_ptr, zval *this_ptr, intreturn_value_used TSRMLS_DC
From the macro definition above, return_value is a pointer to zval. PHP uses this variable to point to the return value of the function. In fact, we also have a macro that can perform simple returns. The macro definition is as follows, RETVAL_*(v), * represents various variable types, v represents the value of the variable, for example, RETVAL_LONG(34), which will return a long value. Its value is 34.
00:14 Let me digress a little bit. When I was a child, I loved celebrating the New Year, but when I grew up, I found that I was afraid of it. I wonder if you feel the same way?
No more talking. If you have any questions, please send me an email at niujiaming0819@163.com, or contact me at qq947847775.
It’s too late now, good night everyone.
11 Array usage and introduction to HashTable
In this section we will talk about PHP arrays. In PHP, arrays are implemented using HashTable. In this section, we first introduce HashTable in detail, and then talk about how to use HastTable
11.1 Variable length structure
The so-called variable length structure is actually a special usage of our C language structure, and there is nothing novel about it. Let's first look at a common definition method of variable length structures.
typedef struct bucket {
int n;
char key[30];
char value[1];
}Bucket;
We have defined a structure Bucket, and we hope to use this structure to store students' personal profiles. The key is used to store the student's name, and the value is used to store the student's profile. You may be curious, our value declares a length of 1. How much information can be stored in 1 char?
In fact, for variable-length structures, the variables we are using cannot be directly defined, such as: Bucket bucket; if you use it this way, the value will definitely not store much information. For variable-length structures, we need to first declare a pointer to the variable-length structure when using it, and then allocate function space through the malloc function. We can malloc as much as the length of the space we need to use. The general usage methods are as follows:
Bucket*pBucket;
pBucket =malloc(sizeof(Bucket)+ n *sizeof(char));
Where n is the length of value you want to use. If used in this way, will the string pointed to by value become longer soon?
11.2 Introduction to Hashtable
Let’s take a look at the definition of HashTable first
struct _hashtable;
typedef struct bucket {
ulong h;//Use
when the element is a numeric index
uint nKeyLength;//When using string index, this variable represents the length of the index, and the index (string) is stored in the last element aKey
void *pData;//Used to point to the saved data. If the saved data is a pointer, pDataPtr points to this data, and pData points to pDataPtr
void *pDataPtr;
struct bucket *pListNext;//Previous element
struct bucket *pListLast; //Next element
struct bucket *pNext;//Pointer to the next bucket
struct bucket *pLast;//Pointer to the previous bucket
char arKey[1];//Must be placed at the end, mainly to achieve variable length structure
}Bucket;
typedef struct _hashtable {
uint nTableSize; //The size of the hash table
uint nTableMask; //Numerically equal to nTableSize - 1
uint nNumOfElements; //Records the number of records saved in the current HashTable
ulongnNextFreeElement; //Point to the next free Bucket
Bucket *pInternalPointer; //This variable is used for array inversion
Bucket *pListHead; //Point to the head of the Bucket
Bucket *pListTail; //Point to the tail of the Bucket
Bucket **arBuckets;
dtor_func_tpDestructor; //Function pointer, automatically called when the array is added, deleted, modified, and checked, used for certain cleanup operations
zend_bool persistent; // Is it persistent?
unsigned char nApplyCount;
zend_boolbApplyProtection; //Works together with nApplyCount to prevent infinite recursion during array traversal
#if ZEND_DEBUG
int inconsistent;
#endif
}HashTable;
I hope everyone can take a good look at the above definitions. There are some things that I will not understand when I explain them. It is better for everyone to look at the code and it will be clearer. PHP's array is actually a doubly linked list with a head node, where HashTable is the head and Bucket stores specific node information.
11.3 Analysis of HashTable internal functions
11.3.1 Macro HASH_PROTECT_RECURSION
#defineHASH_PROTECT_RECURSION (ht)
if ((ht)->bApplyProtection) {                                                                                                                    
if ((ht)->nApplyCount++ >= 3){
zend_error(E_ERROR, "Nestinglevel too deep - recursive dependency?");
                                                                                        
}
This macro is mainly used to prevent circular references.
11.3.2 Macro ZEND_HASH_IF_FULL_DO_RESIZE
#defineZEND_HASH_IF_FULL_DO_RESIZE(ht)                                                                          
if ((ht)->nNumOfElements >(ht)->nTableSize) {
zend_hash_do_resize(ht);
}
The function of this macro is to check whether the number of elements in the current HashTable is greater than the total size of the HashTable. If the number is greater than the size of the HashTable, then we will reallocate the space. Let’s take a look at zend_hash_do_resize
static intzend_hash_do_resize(HashTable*ht)
{
Bucket **t;
IS_CONSISTENT(ht);
if ((ht->nTableSize<< 1) >0) { /* Let's double the table size */
t = (Bucket**) perealloc_recoverable(ht->arBuckets,
(ht->nTableSize<< 1) * sizeof(Bucket*), ht->persistent);
if (t){
HANDLE_BLOCK_INTERRUPTIONS();
ht->arBuckets = t;
ht->nTableSize = (ht->nTableSize<< 1);
ht->nTableMask = ht->nTableSize- 1;
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
return FAILURE;
}
return SUCCESS;
}
From the above code we can see that when HashTable allocates space, the newly allocated space is equal to twice the original space.
11.3.3 Function _zend_hash_init
This function is used to initialize HashTable. Let’s take a look at the code first:
ZEND_API int_zend_hash_init(HashTable*ht, uint nSize,hash_func_t pHashFunction,dtor_func_t pDestructor,zend_bool persistent ZEND_FILE_LINE_DC)
{
uint i = 3; //The default size of HashTable is 2 raised to the third power
Bucket **tmp;
SET_INCONSISTENT(HT_OK);
if (nSize>= 0x80000000){
ht->nTableSize = 0x80000000;
} else{
while ((1U << i)< nSize){
i++;
}
ht->nTableSize = 1 << i;
}
ht->nTableMask = ht->nTableSize-1;
ht->pDestructor = pDestructor;
ht->arBuckets =NULL;
ht->pListHead =NULL;
ht->pListTail =NULL;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->pInternalPointer = NULL;
ht->persistent = persistent;
ht->nApplyCount =0;
ht->bApplyProtection = 1;
/* Uses ecalloc() so that Bucket* == NULL */
if (persistent){
tmp = (Bucket **)calloc(ht->nTableSize,sizeof(Bucket*));
if (!tmp){
return FAILURE;
}
ht->arBuckets = tmp;
} else{
tmp = (Bucket **)ecalloc_rel(ht->nTableSize,sizeof(Bucket*));
if (tmp){
ht->arBuckets = tmp;
}
}
return SUCCESS;
}
It can be seen that the size of HashTable is initialized to the nth power of 2. In addition, we see that there are two memory methods, one is calloc and the other is ecalloc_rel. We have discussed these two memory allocation methods in detail. If you are interested, you can check it out yourself.
11.3.4 Function_zend_hash_add_or_update
This function adds or modifies element information to HashTable
ZEND_API int_zend_hash_add_or_update(HashTable*ht,const char *arKey, uintnKeyLength,void *pData, uintnDataSize,void **pDest,int flag ZEND_FILE_LINE_DC)
{
ulong h;
    uint nIndex;
    Bucket *p;
 
    IS_CONSISTENT(ht);
 
    if (nKeyLength<= 0){
#if ZEND_DEBUG
        ZEND_PUTS("zend_hash_update: Can't put inempty keyn");
#endif
        return FAILURE;
    }
 
    h = zend_inline_hash_func(arKey, nKeyLength);
    nIndex = h & ht->nTableMask;
 
    p = ht->arBuckets[nIndex];
    while (p!= NULL){
        if ((p->h== h)&& (p->nKeyLength==nKeyLength)){
            if (!memcmp(p->arKey, arKey,nKeyLength)){
                if (flag &HASH_ADD){
                    return FAILURE;
                }
               HANDLE_BLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
                if (p->pData == pData){
                   ZEND_PUTS("Fatalerror in zend_hash_update: p->pData == pDatan");
                   HANDLE_UNBLOCK_INTERRUPTIONS();
                    return FAILURE;
                }
#endif
                if (ht->pDestructor){
                    ht->pDestructor(p->pData);
                }
               UPDATE_DATA(ht, p, pData,nDataSize);
                if (pDest) {
                    *pDest = p->pData;
                }
               HANDLE_UNBLOCK_INTERRUPTIONS();
                return SUCCESS;
            }
        }
        p = p->pNext;
    }
   
    p = (Bucket*)pemalloc(sizeof(Bucket)- 1 +nKeyLength, ht->persistent);
    if (!p){
        return FAILURE;
    }
    memcpy(p->arKey, arKey, nKeyLength);
    p->nKeyLength = nKeyLength;
    INIT_DATA(ht, p, pData, nDataSize);
    p->h = h;
   CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
    if (pDest){
        *pDest = p->pData;
    }
 
   HANDLE_BLOCK_INTERRUPTIONS();
   CONNECT_TO_GLOBAL_DLLIST(p, ht);
    ht->arBuckets[nIndex]= p;
   HANDLE_UNBLOCK_INTERRUPTIONS();
 
    ht->nNumOfElements++;
   ZEND_HASH_IF_FULL_DO_RESIZE(ht);       /* Ifthe Hash table is full, resize it */
    return SUCCESS;
}
11.3.5 宏CONNECT_TO_BUCKET_DLLIST
#define CONNECT_TO_BUCKET_DLLIST(element, list_head)        
    (element)->pNext= (list_head);                        
    (element)->pLast= NULL;                                
    if((element)->pNext) {                                
        (element)->pNext->pLast= (element);                
    }
这个宏是将bucket加入到bucket链表中
11.3.6 其他函数或者宏定义
We just briefly introduce HashTable. If you want to understand HashTable in detail, it is recommended that you take a look at the source code of php. The code for HashTable is in Zend/zend_hash.h and Zend/zend_hash.c.
zend_hash_add_empty_element adds an empty element to the function
zend_hash_del_key_or_index deletes elements based on index
zend_hash_reverse_apply Reverse traverse HashTable
zend_hash_copy copy
_zend_hash_merge merge
zend_hash_find String index search
zend_hash_index_find Numeric index method search
zend_hash_quick_find encapsulation of the above two functions
zend_hash_exists Whether the index exists
zend_hash_index_exists Whether the index exists
zend_hash_quick_exists encapsulation of the above two methods

www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/477765.htmlTechArticle1 Preface This article is my personal study notes. I send this article to everyone who likes the PHP language. Peers and students who like PHP extension development. 2 Preliminary preparations. If you don’t want to gossip, please withdraw...
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn