>백엔드 개발 >PHP 튜토리얼 >PHP扩展开发-内核执行流程与扩充结构

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

WBOY
WBOY원래의
2016-06-13 12:16:03757검색

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>
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.