一、写在前面
本文是以PHP7.4
作为基础,讲解如何从零开始创建一个PHP扩展。本文主要讲解创建一个扩展的基本步骤都有哪些。示例中,我们将实现如下功能:
<?phpecho hello();?>
输出内容:
$ php74 ./hello.php$ hello word
在扩展中实现一个hello
方法,调用hello
方法后,输出 hello word!
。
二、生成代码
PHP为我们提供了生成基本代码的工具 ext_skel.php 。这个工具在PHP源代码的php-对应版本/ext/
目录下。
输入命令 php ext_skel.php --ext_skel.php --ext hello --author sunct --std
会出现以下效果:
$ php ext_skel.php ext_skel.php --ext hello --author sunct --stdCopying config scripts... doneCopying sources... doneCopying tests... doneSuccess. The extension is now ready to be compiled. To do so, use thefollowing steps:cd /path/to/php-src/hellophpize./configuremakeDon't forget to run tests once the compilation is done:make testThank you for using PHP!
如果不加参数--ext
,直接运行php ext_skel.php
则报Error
错误。
$ php ext_skel.php Error: No extension name passed, use "--ext <name>"
在 ext
目录下便生成 hello
目录。
PHP 扩展由几个文件组成,这些文件对所有扩展来说都是通用的。不同扩展之间,这些文件的很多细节是相似的,只是要费力去复制每个文件的内容。幸运的是,有脚本可以做所有的初始化工作,名为 ext_skel,自 PHP 4.0 起与其一起分发。
以下是源包中的json
文件内容。
可选参数:
OPTIONS php ext_skel.php --ext <name> [--experimental] [--author <name>] [--dir <path>] [--std] [--onlyunix] [--onlywindows] [--help] --ext <name> The name of the extension defined as <name> //扩展名定义为 <name> --experimental Passed if this extension is experimental, this creates the EXPERIMENTAL file in the root of the extension //如果此扩展名是实验性的,则通过扩展程序根目录中的EXPERIMENTAL文件 --author <name> Your name, this is used if --std is passed and for the CREDITS file //您的名字,如果传递了--std,则CREDITS文件使用此名称 --dir <path> Path to the directory for where extension should be created. Defaults to the directory of where this script lives --std If passed, the standard header used in extensions that is included in the core, will be used --onlyunix Only generate configure scripts for Unix --onlywindows Only generate configure scripts for Windows --help This help
通常来说,开发一个新扩展时,仅需关注的参数是 --ext name
和 --help
。除非已经熟悉扩展的结构; 指定此参数会造成 ext_skel 不会在生成的文件里省略很多有用的注释。 剩下的 --ext name 会将扩展的名称传给 ext_skel
。"name" 是一个全为小写字母的标识符
,仅包含字母和下划线
,在 PHP 发行包的 ext/
文件夹下是唯一
的。
三、修改config.m4配置文件
扩展的 config.m4 文件告诉 UNIX 构建系统哪些扩展 configure 选项是支持的,你需要哪些扩展库,以及哪些源文件要编译成它的一部分。对所有经常使用的 autoconf 宏,包括 PHP 特定的及 autoconf 内建的。
config.m4的作用就是配合phpize工具生成configure文件。configure文件是用于环境检测的。检测扩展编译运行所需的环境是否满足。现在我们开始修改config.m4文件。
其中,dnl 是注释符号。
上面的代码说,如果你所编写的扩展如果依赖其它的扩展或者lib库,需要去掉PHP_ARG_WITH
相关代码的注释。否则,去掉 PHP_ARG_ENABLE
相关代码段的注释。我们编写的扩展不需要依赖其他的扩展和lib库。因此,我们去掉PHP_ARG_ENABLE
前面的注释。
上图生成的时候就已经指定是不依赖其他的扩展。
四、代码实现
修改hello.c文件。实现hello方法。
在执行 php ext_skel.php --ext hello
时,hello.c
文件已经给我们生成了两个test方法:hello_test1
和hello_test2
。
文件生成代码:
/* {{{ void hello_test1() */PHP_FUNCTION(hello_test1){ ZEND_PARSE_PARAMETERS_NONE(); php_printf("The extension %s is loaded and working!\r\n", "hello");}/* }}} *//* {{{ string hello_test2( [ string $var ] ) */PHP_FUNCTION(hello_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);}
其中:
执行函数hello_test1()
会输出The extension hello is loaded and working!
,不可传参,否则echo hello_test1('这是一个参数');
报Warning错误:PHP Warning: hello_test1() expects exactly 0 parameters, 1 given in /Users/sunct/code/php/hello.php on line 2
;
执行函数hello_test2()
会输出Hello World
,并且该函数可传参hello_test2('这是一个参数')
,输出:Hello 这是一个参数
我们可以仿照写一个 hello
函数,放到函数PHP_FUNCTION(hello_test2)
后面:
/*新增函数*/PHP_FUNCTION(hello){ zend_string *strg; strg = strpprintf(0, "hello word"); RETURN_STR(strg);}
用来输出 hello word
找到 PHP_FE_END 在上面增加如下代码:
PHP_FE(hello, NULL)
如图所示:
五、编译安装
cd hello/phpize./configure make
其中: ./configure
根据自己环境的情况加参数即可。
为了便于测试我使用:./configure --prefix=/usr/local/php7 --without-iconv --with-apxs2 --enable-fpm --with-config-file-path=/usr/local/php7/etc;
修改php.ini文件,增加如下代码:
extension = say.so
执行:
$ php74 ./hello.php$ hello word0
如果没有出现hello
,在使用当前PHP时,会出现以下Warning错误:
$ php74 ./hello.php$ hello word1
这说明/usr/local/php7/lib/php/extensions/no-debug-non-zts-20190902/hello.so
文件不存在,我们可以手动把生成的hello.so
移到这里。 在我们开始编译开始执行后·,hello
文件夹下生成modules
文件夹,里面就已生成hello.so
移到文件:
$ php74 ./hello.php$ hello word2
查看phpinfo():
六、调用测试
写一个PHP文件,调用hello方法。看输出的内容是否符合预期。
结果如下:
$ php74 ./hello.php$ hello word3
七、※ 剖析文件
1、config.m4
UNIX构建系统配置
2、config.w32
Windows构建系统配置
3、php_hello.h
当将扩展作为静态模块构建到PHP二进制文件中时,构建系统将期望php_ 在扩展名之前添加一个头文件,该头文件包括一个指向扩展模块结构的指针的声明。就像任何标头一样,此文件通常包含其他宏,原型和全局变量。
4、hello.c
主扩展源文件。按照惯例,该文件的名称是扩展名,但这不是必需的。该文件包含模块结构声明、INI条目、管理函数、用户空间函数和扩展的其他要求。
PHP扩展的主要源文件包含C程序的一些结构。其中最重要的是zend_module结构,这是开始写一个新扩展时最先接触的。该结构包含大量信息,这些信息告诉Zend Engine扩展的依赖项,版本,回调和其他关键数据。
$ php74 ./hello.php$ hello word4
模块结构字段值
[^1] 此字段不适用于模块开发人员。 [^2] 该字段由STANDARD_MODULE_HEADER_EX
填充。 [^3] 该字段由STANDARD_MODULE_HEADER
填充。 [^4] 该字段由STANDARD_MODULE_PROPERTIES
填充。 [^5] 该字段由NO_MODULE_GLOBALS
填充。 [^6] 该字段由PHP_MODULE_GLOBALS
填充。 [^7] 仅当USING_ZTS是时,此字段存在true。 [^8] 仅当USING_ZTS是时,此字段存在false。 [^9] 该字段由STANDARD_MODULE_PROPERTIES_EX
填充。
END
如有问题请在下方留言。
或关注我的公众号“孙三苗
”(sunsanmiao
),输入“联系方式
”。获得进一步帮助。