php常见扩展(PHP源码系列之扩展的原理与开发)

wufei123 发布于 2024-01-05 阅读(299)

php教程零基础入门

目录一、前言二、PHP扩展与Zend扩展三、扩展的组成结构四、扩展的加载过程五、扩展的开发教程一、前言1、本文使用源码版本为PHP-7.1.192、本文安装的PHP版本为7.1.1资源网93、使用电脑为Mac,操作系统信息如下

DarwinKernelVersion18.0.0: root:xnu-4903.201.2~1/RELEASE_X86_64x86_644、本文提到的扩展开发资源网是PHP扩展,而不是Zend扩展5、文章原文来自于博客(

https://github.com/WGrape/Blog),关注获取最新文章二、PHP扩展与Zend扩展1、什么是PHP扩展在/php-sr资源网c/Zend/zend_modules.h头文件中定义了_zend_module_entry结构体,这个结构体就是PHP扩展的结构体,称为module,除了一些基本信息外,主要提供了以下个钩子函数

MI资源网NT :模块初始化时被调用MSHUTDOWN :模块结束化时被调用RINT :每一次请求时被调用RSHUTDOWN :每一次请求结束后被调用struct _zend_module_entry {// 资源网基本信息一般通过STANDARD_MODULE_HEADER常量填充即可

unsigned short size; unsignedint zend_api; const资源网struct _zend_ini_entry *ini_entry;int module_started;

int module_number; // 扩展的名称constchar *资源网name; // 扩展的函数指针, 用于获取扩展的函数列表conststruct _zend_function_entry

*functions;// MINT钩子函数int (*mo资源网dule_startup_func)(INIT_FUNC_ARGS); // MSHUTDOWN钩子函数int (*module_shutdown_func)(SHUTDOWN_FU资源网NC_ARGS);

// RINT钩子函数int (*request_startup_func)(INIT_FUNC_ARGS); // RSHUTDOWN钩子函数int (*requ资源网est_shutdown_func)(SHUTDOWN_FUNC_ARGS);

// 调用phpinfo()时打印扩展信息void (*info_func)(ZEND_MODULE_INFO_FUNC_资源网ARGS); };2、什么是Zend扩展在/php-src/Zend/zend_extensions.h头文件中定义了_zend_extension结构体,这个结构体就是Zend扩展的资源网结构体,称为extension。

相比PHP扩展,主要提供了更底层的钩子函数,如下所示struct _zend_extension {// 一些基本信息char *name; char 资源网*version; char *author;

char *URL; char *copyright; /*Zend生命周期内的一些钩子函数*/star资源网tup_func_t startup; shutdown_func_t shutdown;

activate_func_t activate; deactivate_fu资源网nc_t deactivate; message_handler_func_t message_handler;

op_array_handler_func_t op_array_ha资源网ndler; 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; /*Zend生命周期内的一些钩子函数*/ };3、举例(1) Json扩展资源网Json扩展定义结构体为zend_module_entry,可知它是PHP扩展

(2) Opcache扩展Opcache扩展定义结构体为zend_extension,可知它是Zend扩展

(3) Xdeb资源网ug扩展Xdebug扩展必须在Zend生命周期内Hook才能实现对代码的调试,所以Xdebug是Zend扩展4、总结扩展是区分php扩展和zend扩展的,在PHP源码中也严格区分module和exte资源网nsion这两个定义

module表示php extension,即PHP的扩展,通过extension=*加载extension表示zend extension,即Zend的扩展,通过zend_ext资源网ension=*加载三、扩展的组成结构

1、目录结构在源码中的php-src/ext目录就是扩展目录,如json、mysqli、pdo等常用的扩展,其中每个扩展都主要由以下文件组成tests :单元测试资源网目录config.m4 :扩展的编译配置文件(Unix系统)

config.w32 :扩展的编译配置文件(Windows系统)php_{$module}.h :扩展的头文件{$module}.c :扩展资源网源文件{$module}.php :扩展的测试文件2、代码结构

(1) 单元测试在编译扩展成功后,会在扩展目录下生成一个run-test.php脚本文件,这个脚本会自动执行tests目录下的所有单元测试资源网此外在扩展目录下还会自动生成一个{$module}.php扩展的测试文件,可以方便的测试扩展是否可以正常加载和使用。

(2) 编译配置文件扩展下载后只有源码,需要进行编译生成.so扩展文件后才可以被PH资源网P使用,config.m4和config.w32这两个文件就是在后续执行phpize阶段会使用到的编译配置文件m4是一种宏处理文件,主要由PHP_ARG_WITH和PHP_ARG_ENABLE两部分构资源网成,一般使用第二部分即可,用于开启指定的扩展。

这样在编译阶段,就会判断$PHP_PHP_HELLO变量不是no,从而执行此扩展的编译其中dnl宏会删除本行多余的字符,可以简单的理解为注释,如下所示,如资源网果需要编译php_hello这个扩展,把PHP_ARG_ENABLE部分最前面的dnl宏都去掉即可

dnl If your extension references something external资源网, usewith: dnl PHP_ARG_WITH(php_hello, for php_hello support, dnl Make sure that th资源网e

commentis aligned: dnl [ --with-php_hello Include php_hello support]) dnl Otherwis资源网e use

enable: dnl PHP_ARG_ENABLE(php_hello, whether toenable php_hello support, dnl 资源网Make sure that the comment

is aligned: dnl [ --enable-php_hello Enable php_hello support])ift资源网est"$PHP_PHP_HELLO" != "no"

; then PHP_NEW_EXTENSION(php_hello, php_hello.c, $ext_shared,, -D资源网ZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi

(3) 扩展头文件一般名为php_{$module}.h的是扩展头文件,一般用于定义需要的常量和函数等(4) 资源网扩展源文件一般名称为{$module}.c的是扩展源文件,主要由以下部分组成zend_module_entry :定义扩展的结构体

PHP_FUNCTION :定义扩展的函数Hook_FUNCTION 资源网:如 PHP_MINIT_FUNCTION 等四、扩展的加载过程(1) 源码注释在php-src/main/main.c文件中,php_module_startup()函数会执行扩展的加载与初始化。

i资源网ntphp_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additio资源网nal_modules) {

// Zend引擎初始化zend_startup(&zuf, NULL); // 注册常量php_output_register_cons资源网tants(); php_rfc1867_register_constants

(); // 注册ini配置if (php_init_config() == FAILU资源网RE) { returnFAILURE; } REGISTER_INI_ENTRIES();

zend_register_standard_ini_ent资源网ries(); // 注册$_GET/$_POST/$_SERVER/$_REQUEST等全局变量php_startup_auto_globals

() // 加载静资源网态编译的扩展这些扩展包含在main/internal_functions.c中if (php_register_internal_extensions_func(TSRMLS_C) == FAILUR资源网E) {

php_printf("Unable to start builtin modules\n"); returnFAILURE; } // 注册资源网SAPI的扩展模块,即additional_modules中的扩展

php_register_extensions_bc(additional_modules, num_additional_modul资源网es TSRMLS_CC); // 根据ini配置,先加载Zend扩展, 再加载PHP扩展

php_ini_register_extensions(TSRMLS_C); 资源网 // 扩展初始化, 触发MINT()钩子zend_startup_modules(TSRMLS_C);

zend_startup_extensions(); }(2) php_ini资源网_register_extensions函数在此函数中,extension_lists是一个保存着解析ini配置后获得的所有扩展(包括PHP扩展和Zend扩展)的链表,通过使用&extension_l资源网ists.engine和&extension_lists.functions获取PHP扩展列表和Zend扩展链表,然后通过

php_load_zend_extension_cb()或php_load_p资源网hp_extension_cb()分别完成不同类型扩展的加载voidphp_ini_register_extensions(void) {

//注册zend扩展zend_llist_ap资源网ply(&extension_lists.engine, php_load_zend_extension_cb); //注册php扩展zend_llist_apply

(&extensi资源网on_lists.functions, php_load_php_extension_cb); zend_llist_destroy(&extension_lists.engine)资源网;

zend_llist_destroy(&extension_lists.functions); }(3) 扩展的生命周期如在PHP扩展与Zend扩展一节中看到的,这两种扩展分别提供了资源网不同的钩子函数,这些函数在PHP生命周期内的调用顺序如下图所示

五、扩展的开发教程1、获取PHP源码获取PHP源码后,切换至7.1.19版本,按如下命令操作git clone https://githu资源网b.com/php/php-src git checkout remotes/origin/PHP-

7.1.192、生成扩展的基础文件切换到ext扩展目录下,在此目录下,有一个ext_资源网skel脚本,可以用来自动生成扩展的基础文件比如创建一个print_hello的扩展,按如下命令操作cd php-src/ext/ ./ext_skel --extname=print资源网_hello。

执行成功后,会得到如下提示

回到在ext目录下,发现已经成功生成print_hello目录,主要包含如下文件tests :单元测试目录config.m4 :扩展的编译配置文件(Unix系统资源网)config.w32 :扩展的编译配置文件(Windows系统)

php_print_hello.h :扩展的头文件print_hello.c :扩展源文件print_hello.php :扩展的测试资源网文件

3、修改编译配置文件打开config.m4配置文件,如下所示

找到PHP_ARG_ENABLE这段代码,删掉前面的dnl宏,如下所示# 修改前 dnl PHP_ARG_ENABLE(资源网print_hello, whether to enable print_hello support, dnl Make sure that the

commentis aligned:资源网 dnl [ --enable-print_hello Enable print_hello support])# 修改后 PHP_ARG_ENABLE(print_h资源网ello, whether

toenable print_hello support, Make sure that the commentis aligned: [ -资源网-enable-print_hello Enable print_hello support])

4、修改print_hello.c文件找到PHP_FUNCTION(表示定义的扩展函数),在如下conf资源网irm_print_hello_compiled函数中添加一句输出hello world的代码PHP_FUNCTION(confirm_print_hello_compiled) {

c资源网har *arg = NULL; size_t arg_len, len; zend_string *strg; if (zend_parse_par资源网ameters(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); // 新增下面输出hello world的代码 php_printf("资源网hello world!\n"); RETURN_STR(strg); }

5、编译扩展通过执行以下命令执行对扩展的编译处理cd print_hello 资源网 phpize ./configure --with-php-config=/usr/bin/php-config make sudo

make执行mak资源网e命令成功后如下所示

执行sudo make install命令成功后如下所示

6、执行扩展测试脚本测试脚本会先动态加载print_hello扩展,并输出扩展中所有提供的函数,最后执行在print_hel资源网lo.c源文件中定义的confirm_print_hello_compiled 函数,如果正常执行则说明扩展加载且执行成功

$br = (php_sapi_name() == "cli")? "":"

"资源网; // 判断扩展是否已加载if(!extension_loaded(print_hello)) { // 在运行时动态加载扩展库

// 如果加载失败,需要修改php.ini配置文件,资源网直接开启动态加载扩展的选项enable_dl = On即可,在命令行下执行不需要重启PHP dl(print_hello. . PHP_SHLIB_SUFFIX); }资源网 $module =

print_hello; // 依次输出扩展提供的所有函数 $functions = get_extension_funcs($m资源网odule); echo"Functions available in the test extension:$br\n"

; foreach($functions as资源网 $func) { echo $func."$br\n"; } echo"$br\n"; // 如果扩展加载成功, 则执行 confi资源网rm_print_hello_compiled 函数

$function = confirm_ . $module . _compiled; if (extension_loaded($资源网module)) { $str = $function($module); }

else { $str = "Module $module is not 资源网compiled into PHP"; } echo"$str\n";脚本执行成功后如下所示

7、结束到目前为止,简单的print_hello扩展就已经开发完成,当然还资源网可以在print_hello.c源文件中定义更多的扩展函数,做更多有趣的事情!不过篇幅有限就不再讲解,关于扩展的高阶使用请关注博客(

https://github.com/WGrape/Blog),获取资源网最新文章!

亲爱的读者们,感谢您花时间阅读本文。如果您对本文有任何疑问或建议,请随时联系我。我非常乐意与您交流。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

河南中青旅行社综合资讯 奇遇综合资讯 盛世蓟州综合资讯 综合资讯 游戏百科综合资讯 新闻39177