Home  >  Article  >  Backend Development  >  PHP扩展开发-内核执行流程与扩充结构

PHP扩展开发-内核执行流程与扩充结构

WBOY
WBOYOriginal
2016-06-13 12:16:03735browse

PHP扩展开发-内核执行流程与扩展结构

在开发扩展之前,最好了解下PHP内核的执行流程,PHP大概包括三个方面:

SAPI
Zend VM
内部扩展

  • Zend VM是PHP的虚拟机,与JVM类似,都是各自语言的编译/执行的核心。它们都会把各自的代码先编译为一种中间代码,PHP的通常叫opcode,Java通常叫bytecode,不同的是PHP的opcode直接被Zend VM的执行单元调用对应的C函数执行,不会显示保留下来(可以cache保留),而Java通常是生成class文件保留下来。而这一点可能也是PHP interpreter的名称的由来吧。其实相对严格的C/C++等编译型语言,PHP和Java更多的是结合了编译型和解释性的风格。
  • SAPI可以看作是Zend VM向外界提供编译/执行PHP代码服务的方式和规范。无论是作为cli/cgi/fastcgi/apache_mod与其他程序交互,还是嵌入其他语言中如C/C++等,都可以通过SAPI的规范实现。它的一个重要数据结构就是sapi_module_struct(main/SAPI.h line 217)
  • 内部扩展部分可以看作是搭建在Zend VM和SAPI之上的库,为PHP开发人员提供性能和易用性上的保证。Java的各种包/Python的各种模块功能类似,不同的是PHP中为了性能是用C扩展来实现的,类似的在Java中可以通过JNI来实现,Python中如_socket和_select(多路复用)都不是原生Python实现。

生命周期

关于各种SAPI或者PHP本身的生命周期,可能会和其他组件如apache耦合,后续再细谈。关于PHP扩展的生命周期,这里借用一张图。流程应该是很容易明白的,关于这个过程,网上也有很多资料,不再赘述。我们开发扩展需要注意的几个地方也可以对应到图中的某些节点:

全局变量的定义,通常是zend_modulename_globals
模块的初始化,包括资源/类/常量/ini配置等模块级的初始化
请求的初始化,包括与单次请求相关的一些初始化
请求的结束,清理单次请求相关的数据/内存
模块的卸载,清理模块相关的数据/内存

基本上我们要做的就是按照上面的流程,实现相关的内置函数,定义自己的资源/全局变量/类/函数等。值得注意的地方是在在嵌入其他语言如Python或者被嵌入其他组件如apache时,要小心多进程多线程相关的问题。


PHP扩展结构

使用php-src/ext/ext_skel可以生成PHP扩展的框架

<code class=" hljs avrasm">./ext_skel --extname=myext[[email&#160;protected] ~/software/needbak/php-<span class="hljs-number">5.5</span><span class="hljs-number">.20</span>/ext <span class="hljs-number">12</span>:<span class="hljs-number">24</span>]$==> ls myext/config<span class="hljs-preprocessor">.m</span>4  config<span class="hljs-preprocessor">.w</span>32  CREDITS  EXPERIMENTAL  myext<span class="hljs-preprocessor">.c</span>  myext<span class="hljs-preprocessor">.php</span>  php_myext<span class="hljs-preprocessor">.h</span>  tests</code>

比较重要的文件是config.m4(当然还有源码),config.m4文件可以使用phpize命令生成configure文件,其中说明了我们是否开启模块,以及外部依赖的库。

<code class=" hljs bash">//config.m4//如果你的扩展依赖其他外部库dnl PHP_ARG_WITH(myext, <span class="hljs-keyword">for</span> myext support,dnl Make sure that the comment is aligned:dnl [  --with-myext             Include myext support])//扩展不依赖外部库dnl PHP_ARG_ENABLE(myext, whether to enable myext support,dnl Make sure that the comment is aligned:dnl [  --enable-myext           Enable myext support])//寻找并包含头文件<span class="hljs-keyword">if</span> test <span class="hljs-string">"<span class="hljs-variable">$PHP_MYEXT</span>"</span> != <span class="hljs-string">"no"</span>; <span class="hljs-keyword">then</span>  dnl Write more examples of tests here...  dnl <span class="hljs-comment"># --with-myext -> check with-path</span>  dnl SEARCH_PATH=<span class="hljs-string">"/usr/local /usr"</span>     <span class="hljs-comment"># you might want to change this</span>  dnl SEARCH_FOR=<span class="hljs-string">"/include/myext.h"</span>  <span class="hljs-comment"># you most likely want to change this</span>  dnl <span class="hljs-keyword">if</span> test -r <span class="hljs-variable">$PHP_MYEXT</span>/<span class="hljs-variable">$SEARCH_FOR</span>; <span class="hljs-keyword">then</span> <span class="hljs-comment"># path given as parameter</span>  dnl   MYEXT_DIR=<span class="hljs-variable">$PHP_MYEXT</span>  dnl <span class="hljs-keyword">else</span> <span class="hljs-comment"># search default path list</span>  dnl   AC_MSG_CHECKING([<span class="hljs-keyword">for</span> myext files <span class="hljs-keyword">in</span> default path])  dnl   <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-variable">$SEARCH_PATH</span> ; <span class="hljs-keyword">do</span>  dnl     <span class="hljs-keyword">if</span> test -r <span class="hljs-variable">$i</span>/<span class="hljs-variable">$SEARCH_FOR</span>; <span class="hljs-keyword">then</span>  dnl       MYEXT_DIR=<span class="hljs-variable">$i</span>  dnl       AC_MSG_RESULT(found <span class="hljs-keyword">in</span> <span class="hljs-variable">$i</span>)  dnl     <span class="hljs-keyword">fi</span>  dnl   <span class="hljs-keyword">done</span>  dnl <span class="hljs-keyword">fi</span>  dnl  dnl <span class="hljs-keyword">if</span> test -z <span class="hljs-string">"<span class="hljs-variable">$MYEXT_DIR</span>"</span>; <span class="hljs-keyword">then</span>  dnl   AC_MSG_RESULT([not found])  dnl   AC_MSG_ERROR([Please reinstall the myext distribution])  dnl <span class="hljs-keyword">fi</span>  dnl <span class="hljs-comment"># --with-myext -> add include path</span>  dnl PHP_ADD_INCLUDE(<span class="hljs-variable">$MYEXT_DIR</span>/include)  //加载的lib位置  dnl <span class="hljs-comment"># --with-myext -> check for lib and symbol presence</span>  dnl LIBNAME=myext <span class="hljs-comment"># you may want to change this</span>  dnl LIBSYMBOL=myext <span class="hljs-comment"># you most likely want to change this </span>  dnl PHP_CHECK_LIBRARY(<span class="hljs-variable">$LIBNAME</span>,<span class="hljs-variable">$LIBSYMBOL</span>,  dnl [  dnl   PHP_ADD_LIBRARY_WITH_PATH(<span class="hljs-variable">$LIBNAME</span>, <span class="hljs-variable">$MYEXT_DIR</span>/<span class="hljs-variable">$PHP_LIBDIR</span>, MYEXT_SHARED_LIBADD)  dnl   AC_DEFINE(HAVE_MYEXTLIB,<span class="hljs-number">1</span>,[ ])  dnl ],[  dnl   AC_MSG_ERROR([wrong myext lib version or lib not found])  dnl ],[  dnl   -L<span class="hljs-variable">$MYEXT_DIR</span>/<span class="hljs-variable">$PHP_LIBDIR</span> -lm  dnl ])  dnl  dnl PHP_SUBST(MYEXT_SHARED_LIBADD)  PHP_NEW_EXTENSION(myext, myext.c, <span class="hljs-variable">$ext_shared</span>)<span class="hljs-keyword">fi</span></code>
<code class=" hljs vala"><span class="hljs-comment">//php_myext.h</span><span class="hljs-preprocessor">#ifndef PHP_MYEXT_H</span><span class="hljs-preprocessor">#define PHP_MYEXT_H</span>extern zend_module_entry myext_module_entry;<span class="hljs-preprocessor">#define phpext_myext_ptr &myext_module_entry</span><span class="hljs-comment">//导出符号,在链接的时候有用</span><span class="hljs-preprocessor">#ifdef PHP_WIN32</span><span class="hljs-preprocessor">#   define PHP_MYEXT_API __declspec(dllexport)</span><span class="hljs-preprocessor">#elif defined(__GNUC__) && __GNUC__ >= 4</span><span class="hljs-preprocessor">#   define PHP_MYEXT_API __attribute__ ((visibility("default")))</span><span class="hljs-preprocessor">#else</span><span class="hljs-preprocessor">#   define PHP_MYEXT_API</span><span class="hljs-preprocessor">#endif</span><span class="hljs-preprocessor">#ifdef ZTS</span><span class="hljs-preprocessor">#include "TSRM.h"</span><span class="hljs-preprocessor">#endif</span><span class="hljs-comment">//几个核心函数的声明</span>PHP_MINIT_FUNCTION(myext);PHP_MSHUTDOWN_FUNCTION(myext);PHP_RINIT_FUNCTION(myext);PHP_RSHUTDOWN_FUNCTION(myext);PHP_MINFO_FUNCTION(myext);<span class="hljs-comment">//自动生成的测试函数声明,我们自己定义的模块函数需要在此声明</span>PHP_FUNCTION(confirm_myext_compiled);   <span class="hljs-comment">//全局变量在这定义,展开后是zend_myext_globals结构体</span>ZEND_BEGIN_MODULE_GLOBALS(myext)    <span class="hljs-keyword">long</span>  global_value;    <span class="hljs-keyword">char</span> *global_string;ZEND_END_MODULE_GLOBALS(myext)<span class="hljs-comment">//线程安全与非线程安全下获取全局变量的方式</span><span class="hljs-preprocessor">#ifdef ZTS</span><span class="hljs-preprocessor">#define MYEXT_G(v) TSRMG(myext_globals_id, zend_myext_globals *, v)</span><span class="hljs-preprocessor">#else</span><span class="hljs-preprocessor">#define MYEXT_G(v) (myext_globals.v)</span><span class="hljs-preprocessor">#endif</span><span class="hljs-preprocessor">#endif  /* PHP_MYEXT_H */</span></code>
<code class=" hljs vala"><span class="hljs-comment">//myext.c</span><span class="hljs-preprocessor">#ifdef HAVE_CONFIG_H</span><span class="hljs-preprocessor">#include "config.h"</span><span class="hljs-preprocessor">#endif</span><span class="hljs-preprocessor">#include "php.h"</span><span class="hljs-preprocessor">#include "php_ini.h"</span><span class="hljs-preprocessor">#include "ext/standard/info.h"</span><span class="hljs-preprocessor">#include "php_myext.h"</span><span class="hljs-comment">//全局变量声明</span>ZEND_DECLARE_MODULE_GLOBALS(myext)<span class="hljs-comment">/* True global resources - no need for thread safety here */</span><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> le_myext;<span class="hljs-comment">//模块函数的导出</span><span class="hljs-keyword">const</span> zend_function_entry myext_functions[] = {    PHP_FE(confirm_myext_compiled,  NULL)       <span class="hljs-comment">/* For testing, remove later. */</span>   <span class="hljs-constant"> PHP_FE_END </span> <span class="hljs-comment">/* Must be the last line in myext_functions[] */</span>};<span class="hljs-comment">//模块结构</span>zend_module_entry myext_module_entry = {<span class="hljs-preprocessor">#if ZEND_MODULE_API_NO >= 20010901</span>    STANDARD_MODULE_HEADER,<span class="hljs-preprocessor">#endif</span>    <span class="hljs-string">"myext"</span>,    myext_functions,    PHP_MINIT(myext),    PHP_MSHUTDOWN(myext),    PHP_RINIT(myext),       <span class="hljs-comment">/* Replace with NULL if there's nothing to do at request start */</span>    PHP_RSHUTDOWN(myext),   <span class="hljs-comment">/* Replace with NULL if there's nothing to do at request end */</span>    PHP_MINFO(myext),<span class="hljs-preprocessor">#if ZEND_MODULE_API_NO >= 20010901</span>    PHP_MYEXT_VERSION,<span class="hljs-preprocessor">#endif</span>    STANDARD_MODULE_PROPERTIES};<span class="hljs-preprocessor">#ifdef COMPILE_DL_MYEXT</span>ZEND_GET_MODULE(myext)<span class="hljs-preprocessor">#endif</span><span class="hljs-comment">//ini配置文件的设置</span>PHP_INI_BEGIN()    STD_PHP_INI_ENTRY(<span class="hljs-string">"myext.global_value"</span>,      <span class="hljs-string">"42"</span>, PHP_INI_ALL, OnUpdateLong, global_value, zend_myext_globals, myext_globals)    STD_PHP_INI_ENTRY(<span class="hljs-string">"myext.global_string"</span>, <span class="hljs-string">"foobar"</span>, PHP_INI_ALL, OnUpdateString, global_string, zend_myext_globals, myext_globals)PHP_INI_END()<span class="hljs-comment">//初始化全局变量</span><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> php_myext_init_globals(zend_myext_globals *myext_globals){    myext_globals->global_value = <span class="hljs-number">0</span>;    myext_globals->global_string = NULL;}<span class="hljs-comment">//模块加载时的函数</span>PHP_MINIT_FUNCTION(myext){    <span class="hljs-comment">/* If you have INI entries, uncomment these lines     REGISTER_INI_ENTRIES();    */</span>    <span class="hljs-keyword">return</span> SUCCESS;}<span class="hljs-comment">//模块卸载时函数</span>PHP_MSHUTDOWN_FUNCTION(myext){    <span class="hljs-comment">/* uncomment this line if you have INI entries    UNREGISTER_INI_ENTRIES();    */</span>    <span class="hljs-keyword">return</span> SUCCESS;}<span class="hljs-comment">//请求初始化函数</span>PHP_RINIT_FUNCTION(myext){    <span class="hljs-keyword">return</span> SUCCESS;}<span class="hljs-comment">//请求关闭函数</span>PHP_RSHUTDOWN_FUNCTION(myext){    <span class="hljs-keyword">return</span> SUCCESS;}<span class="hljs-comment">//模块信息,phpinfo</span>PHP_MINFO_FUNCTION(myext){    php_info_print_table_start();    php_info_print_table_header(<span class="hljs-number">2</span>, <span class="hljs-string">"myext support"</span>, <span class="hljs-string">"enabled"</span>);    php_info_print_table_end();    <span class="hljs-comment">/* Remove comments if you have entries in php.ini    DISPLAY_INI_ENTRIES();    */</span>}<span class="hljs-comment">//测试函数</span>PHP_FUNCTION(confirm_myext_compiled){    <span class="hljs-keyword">char</span> *arg = NULL;    <span class="hljs-keyword">int</span> arg_len, len;    <span class="hljs-keyword">char</span> *strg;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, <span class="hljs-string">"s"</span>, &arg, &arg_len) == FAILURE) {        <span class="hljs-keyword">return</span>;    }    len = spprintf(&strg, <span class="hljs-number">0</span>, <span class="hljs-string">"Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP."</span>, <span class="hljs-string">"myext"</span>, arg);    RETURN_STRINGL(strg, len, <span class="hljs-number">0</span>);}</code>
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