菜鳥學php擴充 之 自動產生的擴充架構詳解(二)

來源:互聯網
上載者:User

轉載請附上本文地址:http://blog.csdn.net/u011957758/article/details/72456298 前言

上一文:菜鳥學php擴充 之 hello world(一) ,不問所以然的,強行與php擴充say hello了。對於ext_skel自動產生的架構,將在本文進行詳解,當作備忘錄。 本文 ext_skel的用法

./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]]           [--skel=dir] [--full-xml] [--no-help]  --extname=module   module is the name of your extension(模組名,會在目前的目錄建立一個該名稱子目錄)  --proto=file       file contains prototypes of functions to create(函數原型定義檔案)  --stubs=file       generate only function stubs in file  --xml              generate xml documentation to be added to phpdoc-cvs  --skel=dir         path to the skeleton directory(設定骨架產生的目錄,不設定該項則預設在ext/extname下)  --full-xml         generate xml documentation for a self-contained extension                     (not yet implemented)  --no-help          don't try to be nice and create comments in the code                     and helper functions to test if the module compiled (產生的程式碼中不顯示各種協助注釋)
php與擴充相關的流程

1.PHP程式的啟動與終止在概念上是分別存在兩個的。

一個是php模組被載入的時候,模組啟動函數即被引擎調用(PHP_MINIT_FUNCTION)。這使得引擎做一些例如資源類型,註冊INI變數等的一次初始化,並且這些資料是常駐記憶體的,與之對應一個終止(PHP_MSHUTDOWN_FUNCTION)

另一個是PHP請求開始的時候,請求前的啟動函數就別調用(PHP_RINIT_FUNCTION),與之對應一個請求結束後的終止(PHP_RSHUTDOWN_FUNCTION)

2.伴隨著PHP的啟動,便會開始把自身所有已載入的擴充的MINIT方法(全稱Module Initialization,是由每個模組自己定義的函數。)(PHP_MINIT_FUNCTION),都執行一遍,在這個時間裡,擴充可以定義一些自己的常量、類、資源等所有會被使用者端的PHP指令碼用到的東西。 這裡定義的東東都會常駐記憶體,可以被所有請求使用,直到關掉PHP模組。

3.一個請求到來時候,PHP會迅速的開闢一個新的環境,並重新掃描自己的各個擴充, 遍曆執行它們各自的RINIT方法(全稱Request Initialization)(PHP_RINIT_FUNCTION), 這時候一個擴充可能會初始化在本次請求中會使用到的變數等, 還會初始化等會兒使用者端(即PHP指令碼)中的變數等等。

4.當請求經過業務代碼,執行到最後的時候,PHP會啟動回收程式,會執行所有已載入的擴充的RSHUTDOWN(全稱Request Shutdown)(PHP_RSHUTDOWN_FUNCTION)方法,利用核心中的變數表之類的做一些事情,一旦執行結束,便會釋放掉這次請求使用過的所有東西, 包括變數表的所有變數、所有在這次請求中申請的記憶體 等等

5.請求處理結束後,該關閉的也關了,PHP便進入MSHUTDOWN(全稱Module Shutdown)(PHP_MSHUTDOWN_FUNCTION)階段,此時PHP會向所有擴充發出最後通牒,如果哪個擴充還有未了的心愿,就放在自己MSHUTDOWN方法裡,這可是最後的機會了,一旦PHP把擴充的MSHUTDOWN執行完,便會進入自毀程式。(清除擅自申請的記憶體的最後機會,否則就記憶體流失了)

匯總,我理解的流程:
PHP_MINIT_FUNCTION(一個進程執行一次)
|
執行很多個PHP_RINIT_FUNCTION
|
執行很多個PHP_RSHUTDOWN_FUNCTION
|
PHP_MSHUTDOWN_FUNCTION(一個進程執行一次)

附上多線程和多進程的圖

config.m4

dnl代表備忘掉此行,和php中//一樣。為什麼是dnl就不研究了,知道是備忘就好。

dnl $Id$dnl config.m4 for extension helloworlddnl Comments in this file start with the string 'dnl'.dnl Remove where necessary. This file will not workdnl without editing.dnl If your extension references something external, use with:##指定PHP模組的工作方式,動態編譯選項,如果想通過.so的方式接入擴充,請去掉前面的dnl注釋PHP_ARG_WITH(helloworld, for helloworld support,Make sure that the comment is aligned:[  --with-helloworld             Include helloworld support])dnl Otherwise use enable:##指定PHP模組的工作方式,靜態編譯選項,如果想通過enable的方式來啟用,去掉dnl注釋PHP_ARG_ENABLE(helloworld, whether to enable helloworld support,Make sure that the comment is aligned:[  --enable-helloworld           Enable helloworld support])if test "$PHP_HELLOWORLD" != "no"; then  dnl Write more examples of tests here...  dnl # --with-helloworld -> check with-path  dnl SEARCH_PATH="/usr/local /usr"     # you might want to change this  dnl SEARCH_FOR="/include/helloworld.h"  # you most likely want to change this  dnl if test -r $PHP_HELLOWORLD/$SEARCH_FOR; then # path given as parameter  dnl   HELLOWORLD_DIR=$PHP_HELLOWORLD  dnl else # search default path list  dnl   AC_MSG_CHECKING([for helloworld files in default path])  dnl   for i in $SEARCH_PATH ; do  dnl     if test -r $i/$SEARCH_FOR; then  dnl       HELLOWORLD_DIR=$i  dnl       AC_MSG_RESULT(found in $i)  dnl     fi  dnl   done  dnl fi  dnl   dnl if test -z "$HELLOWORLD_DIR"; then  dnl   AC_MSG_RESULT([not found])  dnl   AC_MSG_ERROR([Please reinstall the helloworld distribution])  dnl fi  dnl # --with-helloworld -> add include path  dnl PHP_ADD_INCLUDE($HELLOWORLD_DIR/include)  dnl # --with-helloworld -> check for lib and symbol presence  dnl LIBNAME=helloworld # you may want to change this  dnl LIBSYMBOL=helloworld # you most likely want to change this   dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,  dnl [  dnl   PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $HELLOWORLD_DIR/$PHP_LIBDIR, HELLOWORLD_SHARED_LIBADD)  dnl   AC_DEFINE(HAVE_HELLOWORLDLIB,1,[ ])  dnl ],[  dnl   AC_MSG_ERROR([wrong helloworld lib version or lib not found])  dnl ],[  dnl   -L$HELLOWORLD_DIR/$PHP_LIBDIR -lm  dnl ])  dnl  ##用於說明這個擴充編譯成動態連結程式庫的形式  dnl PHP_SUBST(HELLOWORLD_SHARED_LIBADD)  ##用於指定有哪些源檔案應該被編譯,檔案和檔案之間用空格隔開  PHP_NEW_EXTENSION(helloworld, helloworld.c, $ext_shared)fi
php_helloworld.h

發現網上很多很早以前寫的教材,都有在標頭檔理由申明一下函數。貌似較新的版本就不需要了。因為預設產生的架構在標頭檔裡面也沒有看到類似“PHP_FUNCTION(confirm_helloworld_compiled)”的字樣。所以此檔案不用太管他。(但是標頭檔申明一下要實現的函數是好習慣)

知道一下helloworld.c下面會用到的版本號碼在這裡定義了

#define PHP_HELLOWORLD_VERSION "0.1.0"
helloworld.c 代碼結構

以PHP_XXX的宏很多是在main/php.h裡頭定義的

#ifdef HAVE_CONFIG_H#include "config.h"#endif##包含標頭檔(引入所需要的宏、API定義等)#include "php.h"#include "php_ini.h"#include "ext/standard/info.h"#include "php_helloworld.h"static int le_helloworld;##PHP核心定義的一個宏,與ZEND_FUNCTION相同,用於定義擴充函數(這個函數是系統預設產生的,用於確認之用)PHP_FUNCTION(confirm_helloworld_compiled){    char *arg = NULL;    int arg_len, len;    char *strg;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {        return;    }    len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "helloworld", arg);    RETURN_STRINGL(strg, len, 0);}##定義PHP中可以調用的函數PHP_FUNCTION(helloworld) {    php_printf("Hello World!\n");    RETURN_TRUE;}##初始化module時運行PHP_MINIT_FUNCTION(helloworld){    /* If you have INI entries, uncomment these lines     REGISTER_INI_ENTRIES();    */    return SUCCESS;}##當module被卸載時運行PHP_MSHUTDOWN_FUNCTION(helloworld){    /* uncomment this line if you have INI entries    UNREGISTER_INI_ENTRIES();    */    return SUCCESS;}##當一個REQUEST請求初始化時運行PHP_RINIT_FUNCTION(helloworld){    return SUCCESS;}##當一個REQUEST請求結束時運行PHP_RSHUTDOWN_FUNCTION(helloworld){    return SUCCESS;}##聲明模組資訊函數,即可以在phpinfo看到的資訊PHP_MINFO_FUNCTION(helloworld){    php_info_print_table_start();    php_info_print_table_header(2, "helloworld support", "enabled");    php_info_print_table_end();    /* Remove comments if you have entries in php.ini    DISPLAY_INI_ENTRIES();    */}##聲明(引入)Zend(PHP)函數塊const zend_function_entry helloworld_functions[] = {    PHP_FE(confirm_helloworld_compiled, NULL)       /* For testing, remove later. */    ##上一講中就是在這裡添加了自己定義的函數模組    PHP_FE(helloworld,  NULL)       /*  */    ##zend引擎認為結束的標記,老版本的是“{NULL,NULL,NULL}”,後面PHP原始碼直接定義了個宏PHP_FE_END,這裡就直接用這個了。雖然都一個意思但看過去爽多了    ##如果遇到PHP_FE_END未定義undefine的問題,請見附錄1    PHP_FE_END  /* Must be the last line in helloworld_functions[] */};##聲明 Zend模組,是不是感覺下面的模組名字很熟悉,對的,就是前文講到的PHP流程中會用到的,現在懂了為什麼要先講流程了吧~zend_module_entry helloworld_module_entry = {    STANDARD_MODULE_HEADER,    "helloworld",    helloworld_functions,    PHP_MINIT(helloworld),    PHP_MSHUTDOWN(helloworld),    PHP_RINIT(helloworld),      /* Replace with NULL if there's nothing to do at request start */    PHP_RSHUTDOWN(helloworld),  /* Replace with NULL if there's nothing to do at request end */    PHP_MINFO(helloworld),    PHP_HELLOWORLD_VERSION,    STANDARD_MODULE_PROPERTIES};##實現get_module()函數#ifdef COMPILE_DL_HELLOWORLDZEND_GET_MODULE(helloworld)#endif
模組結構

1.包含標頭檔(引入所需要的宏、API定義等);

模組所必須包含的標頭檔僅有一個 php.h,它位於 main目錄下。這個檔案包含了構建模組時所必需的各種宏和API定義。

2.聲明匯出函數(用於 Zend函數塊的聲明);

ZEND_FUNCTION 宏聲明了一個使用 Zend內部 API來編譯的新的C函數。這個 C函數是 void類型,以 INTERNAL_FUNCTION_PARAMETERS(這是另一個宏)為參數,而且函數名字以 zif_為首碼。
PHP_FUNCTION和這個是一樣的有在/main/php.h中已有定義宏了

#define PHP_FUNCTION            ZEND_FUNCTION

3.聲明 Zend函數塊;

現在你已經聲明了匯出函數,但Zend並不知道如何調用,因此還必須得將其引入 Zend。這些函數的引入是通過一個包含有N個zend_function_entry結構的數組來完成的。數組的每一項都對應於一個外部可見的函數,每一項都包含了某個函數在 PHP中出現的名字以及在 C代碼中所定義的名字。

4.聲明 Zend模組;

Zend模組的資訊被儲存在一個名為zend_module_entry的結構,它包含了所有需要向 Zend提供的模組資訊。

5.實現get_module()函數;

這個函數只用於動態可載入模組

6.實現匯出函數。

實現想要擴充的函數,PHP_FUNCTION(helloworld)

ps:模組部分是學習這篇文章的,本來寫好了,後面發現他寫的比我好就借鑒了PHP擴充代碼結構詳解 附錄

1.error: ‘PHP_FE_END’ undeclared here (not in a function)錯誤。

原因:是因為zend引擎版本太老了。
1、切換到php的源碼目錄,
2、執行下面兩行

# sed -i 's|PHP_FE_END|{NULL,NULL,NULL}|' ./ext/**/*.c# sed -i 's|ZEND_MOD_END|{NULL,NULL,NULL}|' ./ext/**/*.c

3.切換到mcrypt目錄,如php-5.x.x/ext/mcrypt/。再次執行make命令,一切恢複正常。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.