PHP擴展代碼結構詳解 :
這個是繼:使用ext_skel和phpize構建php5擴展 內容 (拆分出來)Zend_API:深入_PHP_內核:http://cn2.php.net/manual/zh/internals2.ze1.php
我們使用ext_skel創建擴展 hello_module,該模塊包含一個方法:hello_world。
使用ext_skel 生成的代碼都是PHP_開頭的宏, 而不是ZEND_開頭. 實際上這兩者是一樣的。
在源代碼src/main/php.h 中發現: PHP_FUNCTION 就是ZEND_FUNCTION的別名,即:
- #define PHP_FUNCTION ZEND_FUNCTION /* <acronym title="Hypertext Preprocessor">PHP</acronym>-named Zend macro wrappers */
其實,很多”PHP_”開頭的宏都用對應的”ZEND_”開頭的宏,這個應該是爲兼容之前的版本吧。
php_hello_module.h
- /* $Id: header 252479 2008-02-07 19:39:50Z iliaa $ */
- #ifndef PHP_HELLO_MODULE_H
- #define PHP_HELLO_MODULE_H
- extern zend_module_entry hello_module_module_entry;
- #define phpext_hello_module_ptr &hello_module_module_entry
- #ifdef PHP_WIN32
- # define PHP_HELLO_MODULE_API __declspec(dllexport)
- #elif defined(__GNUC__) && __GNUC__ >= 4
- # define PHP_HELLO_MODULE_API __attribute__ ((visibility("default")))
- #else
- # define PHP_HELLO_MODULE_API
- #endif
- #ifdef ZTS
- #include "TSRM.h"
- #endif
- PHP_MINIT_FUNCTION(hello_module); //聲明加載模塊時調用的函數
- PHP_MSHUTDOWN_FUNCTION(hello_module);//聲明卸載模塊時調用的函數
- PHP_RINIT_FUNCTION(hello_module); //聲明請求時調用的函數
- PHP_RSHUTDOWN_FUNCTION(hello_module);//聲明請求結束時調用的函數
- PHP_MINFO_FUNCTION(hello_module); //聲明模塊信息函數,即可以在phpinfo看到的信息
- PHP_FUNCTION(confirm_hello_module_compiled); /* For testing, remove later. */
- /*********** 2)聲明導出函數 ************************************************/
- PHP_FUNCTION(hello_world);
- /*
- Declare any global variables you may need between the BEGIN
- and END macros here:
- ZEND_BEGIN_MODULE_GLOBALS(hello_module)
- long global_value;
- char *global_string;
- ZEND_END_MODULE_GLOBALS(hello_module)
- */
- /* In every utility function you add that needs to use variables
- in php_hello_module_globals, call TSRMLS_FETCH(); after declaring other
- variables used by that function, or better yet, pass in TSRMLS_CC
- after the last function argument and declare your utility function
- with TSRMLS_DC after the last declared argument. Always refer to
- the globals in your function as HELLO_MODULE_G(variable). You are
- encouraged to rename these macros something shorter, see
- examples in any other php module directory.
- */
- #ifdef ZTS
- #define HELLO_MODULE_G(v) TSRMG(hello_module_globals_id, zend_hello_module_globals *, v)
- #else
- #define HELLO_MODULE_G(v) (hello_module_globals.v)
- #endif
- #endif /* PHP_HELLO_MODULE_H */
- /* $Id: header 252479 2008-02-07 19:39:50Z iliaa $ */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- /***********************1)包含頭文件(引入所需要的宏、API定義等) **************************/
- #include "php.h" //1)包含頭文件(引入所需要的宏、API定義等);#include "php_ini.h"#include "ext/standard/info.h"#include "php_hello_module.h"/* If you declare any globals in php_hello_module.h uncomment this:ZEND_DECLARE_MODULE_GLOBALS(hello_module)*//* True global resources - no need for thread safety here */static int le_hello_module;
- /************ 3)聲明(引入)Zend(PHP)函數塊 ******************/
- /* {{{ hello_module_functions[] * * Every user visible function must have an entry in hello_module_functions[]. */
- const zend_function_entry hello_module_functions[] = {
- PHP_FE(hello_world, NULL)
- PHP_FE(confirm_hello_module_compiled, NULL) /* For testing, remove later. */
- {NULL, NULL, NULL} /* Must be the last line in hello_module_functions[] */
- };
- /* }}} */
- /*********************************4)聲明 Zend模塊 ***********************/
- /* {{{ hello_module_module_entry
- */
- zend_module_entry hello_module_module_entry = {
- #if ZEND_MODULE_API_NO >= 20010901
- STANDARD_MODULE_HEADER,
- #endif
- "hello_module",
- hello_module_functions, /* 4 )聲明 Zend模塊 */
- PHP_MINIT(hello_module),
- PHP_MSHUTDOWN(hello_module),
- PHP_RINIT(hello_module), /* Replace with NULL if there's nothing to do at request start */
- PHP_RSHUTDOWN(hello_module), /* Replace with NULL if there's nothing to do at request end */
- PHP_MINFO(hello_module),
- #if ZEND_MODULE_API_NO >= 20010901
- "0.1", /* Replace with version number for your extension */
- #endif
- STANDARD_MODULE_PROPERTIES
- };
- /* }}} */
- /****************** 5) 實現get_module()函數 ***********************/
- #ifdef COMPILE_DL_HELLO_MODULE
- ZEND_GET_MODULE(hello_module)
- #endif
- /* {{{ PHP_INI
- */
- /* Remove comments and fill if you need to have entries in php.ini
- PHP_INI_BEGIN()
- STD_PHP_INI_ENTRY("hello_module.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_hello_module_globals, hello_module_globals)
- STD_PHP_INI_ENTRY("hello_module.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_hello_module_globals, hello_module_globals)
- PHP_INI_END()
- */
- /* }}} */
- /* {{{ php_hello_module_init_globals
- */
- /* Uncomment this function if you have INI entries
- static void php_hello_module_init_globals(zend_hello_module_globals *hello_module_globals)
- {
- hello_module_globals->global_value = 0;
- hello_module_globals->global_string = NULL;
- }
- */
- /* }}} */
- /* {{{ PHP_MINIT_FUNCTION
- */
- PHP_MINIT_FUNCTION(hello_module)
- {
- /* If you have INI entries, uncomment these lines
- REGISTER_INI_ENTRIES();
- */
- return SUCCESS;
- }
- /* }}} */
- /* {{{ PHP_MSHUTDOWN_FUNCTION
- */
- PHP_MSHUTDOWN_FUNCTION(hello_module)
- {
- /* uncomment this line if you have INI entries
- UNREGISTER_INI_ENTRIES();
- */
- return SUCCESS;
- }
- /* }}} */
- /* Remove if there's nothing to do at request start */
- /* {{{ PHP_RINIT_FUNCTION
- */
- PHP_RINIT_FUNCTION(hello_module)
- {
- return SUCCESS;
- }
- /* }}} */
- /* Remove if there's nothing to do at request end */
- /* {{{ PHP_RSHUTDOWN_FUNCTION
- */
- PHP_RSHUTDOWN_FUNCTION(hello_module)
- {
- return SUCCESS;
- }
- /* }}} */
- /* {{{ PHP_MINFO_FUNCTION
- */
- PHP_MINFO_FUNCTION(hello_module)
- {
- php_info_print_table_start();
- php_info_print_table_header(2, "hello_module support", "enabled");
- php_info_print_table_end();
- /* Remove comments if you have entries in php.ini
- DISPLAY_INI_ENTRIES();
- */
- }
- /* }}} */
- /* Remove the following function when you have succesfully modified config.m4
- so that your module can be compiled into PHP, it exists only for testing
- purposes. */
- /* Every user-visible function in PHP should document itself in the source */
- /* {{{ proto string confirm_hello_module_compiled(string arg)
- Return a string to confirm that the module is compiled in */
- PHP_FUNCTION(confirm_hello_module_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.", "hello_module", arg);
- RETURN_STRINGL(strg, len, 0);
- }
- /* }}} */
- /* The previous line is meant for vim and emacs, so it can correctly fold and
- unfold functions in source code. See the corresponding marks just before
- function definition, where the functions purpose is also documented. Please
- follow this convention for the convenience of others editing your code.
- */
- /**************************** 6)實現導出函數 *******************************************/
- PHP_FUNCTION(hello_world)
- {
- RETURN_STRING("HelloWorld", 1);
- }
模塊結構
所有的 PHP模塊通常都包含以下幾個部分:
· 包含頭文件(引入所需要的宏、API定義等);
· 聲明導出函數(用於 Zend函數塊的聲明);
· 聲明 Zend函數塊;
· 聲明 Zend模塊;
· 實現get_module()函數;
· 實現導出函數。
1) 包含頭文件
模塊所必須包含的頭文件僅有一個 php.h,它位於 main目錄下。這個文件包含了構建模塊時所必需的各種宏和API定義。
小提示:專門爲模塊創建一個含有其特有信息的頭文件是一個很好的習慣。這個頭文件應該包含 php.h和所有導出函數的定義。如果你是使用 ext_skel來創建模塊的話,那麼你可能已經有了這個文件,因爲這個文件會被 ext_skel自動生成。
2)聲明導出函數
爲了聲明導出函數(也就是讓其成爲可以被 PHP腳本直接調用的原生函數),Zend提供了一個宏來幫助完成這樣一個聲明。代碼如下:
- ZEND_FUNCTION( hello_world );
ZEND_FUNCTION 宏聲明瞭一個使用 Zend內部 API來編譯的新的C函數。這個 C函數是 void類型,以INTERNAL_FUNCTION_PARAMETERS(這是另一個宏)爲參數,而且函數名字以 zif_爲前綴。把上面這句聲明展開可以得到這樣的代碼:
- void zif_hello_world ( INTERNAL_FUNCTION_PARAMETERS );
接着再把 INTERNAL_FUNCTION_PARAMETERS展開就會得到這樣一個結果, 即ZEND_FUNCTION最終擴展結果:
- void zif_my_function( int ht
- , zval *return_value
- , zval *this_ptr
- , intreturn_value_used
- ,zend_executor_globals * executor_globals
- );
可見,ZEND_FUNCTION就是簡單的聲明瞭一個名爲zif_ first_module的C函數,zif可能是”Zend Internal Function”的縮寫。
在解釋器(interpreter)和執行器(executor)被分離出PHP包後,這裏面(指的是解釋器和執行器)原有的一些 API定義及宏也漸漸演變成了一套新的 API系統:Zend API。如今的Zend API 已經承擔了很多原來(指的是分離之前)本屬於 PHP API的職責,大量的 PHP API被以別名的方式簡化爲對應的Zend API。我們推薦您應該儘可能地使用 Zend API,PHP API只是因爲兼容性原因才被保留下來。舉例來說,zval和 pval其實是同一類型,只不過 zval定義在 Zend部分,而 pval定義在 PHP部分(實際上 pval根本就是 zval的一個別名)。但由於INTERNAL_FUNCTION_PARAMETERS是一個 Zend宏,因此我們在上面的聲明中使用了zval。在編寫代碼時,你也應該總是使用 zval以遵循新的 Zend API規範。
這個聲明中的參數列表非常重要,你應該牢記於心。PHP調用函數的 Zend參數:
參數 |
說明 |
ht |
這個參數包含了Zend參數的個數。但你不應該直接訪問這個值,而是應該通過ZEND_NUM_ARGS()宏來獲取參數的個數。 |
return_value |
這個參數用來保存函數向 PHP返回的值。訪問這個變量的最佳方式也是用一系列的宏。後面我們會有詳細說明。 |
this_ptr |
根據這個參數你可以訪問該函數所在的對象(換句話說,此時這個函數應該是一個類的“方法”)。推薦使用函數 getThis()來得到這個值。 |
return_value_used |
這個值主要用來標識函數的返回值是否爲腳本所使用。0表示腳本不使用其返回值,而1則相反。通常用於檢驗函數是否被正確調用以及速度優化方面,這是因爲返回一個值是一種代價很昂貴的操作(可以在 array.c裏面看一下是如何利用這一特性的)。 |
executor_globals |
這個變量指向 Zend Engine的全局設置,在創建新變量時這個這個值會很有用。我們也可以函數中使用宏 TSRMLS_FETCH()來引用這個值。 |
3)聲明(引入)Zend(PHP)函數塊
現在你已經聲明瞭導出函數,但Zend並不知道如何調用,因此還必須得將其引入 Zend。這些函數的引入是通過一個包含有N個zend_function_entry結構的數組來完成的。數組的每一項都對應於一個外部可見的函數,每一項都包含了某個函數在 PHP中出現的名字以及在 C代碼中所定義的名字。zend_function_entry的內部聲明:
- typedef struct _zend_function_entry {
- char *fname;
- void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
- unsigned char *func_arg_types;
- } zend_function_entry;
字段 |
說明 |
fname |
指定在 PHP裏所見到的函數名(比如:fopen、mysql_connect或者是我們樣例中的hello_world)。 |
handler |
指向對應 C函數的句柄。樣例可以參考前面使用宏INTERNAL_FUNCTION_PARAMETERS的函數聲明。 |
func_arg_types |
用來標識一些參數是否要強制性地按引用方式進行傳遞。通常應將其設定爲 NULL。 |
對於我們要創建的模塊:hello_module.該模塊提供一個方法:hello_word,可以這樣聲明:
- const zend_function_entry hello_module_functions[] = {
- PHP_FE(hello_world, NULL)
- PHP_FE(confirm_hello_module_compiled, NULL) /* For testing, remove later. */
- {NULL, NULL, NULL} /* Must be the last line in hello_module_functions[] */
- };
這樣完成C函數到PHP函數的映射的語句。
你可能已經看到了,這個結構的最後一項是 {NULL, NULL, NULL}。事實上,這個結構的最後一項也必須始終是 {NULL, NULL, NULL},因爲 Zend Engine需要靠它來確認這些導出函數的列表是否列舉完畢。
注意:1 )你不應該使用一個預定義的宏來代替列表的結尾部分(即{NULL, NULL, NULL}),因爲編譯器會盡量尋找一個名爲 “NULL” 的函數的指針來代替 NULL!
2)宏 ZEND_FE(“Zend Function Entry”的簡寫)將簡單地展開爲一個zend_function_entry結構。不過需要注意,這些宏對函數採取了一種很特別的命名機制:把你的C函數前加上一個 zif_前綴。比方說,ZEND_FE(hello_word)其實是指向了一個名爲zif_hello_word()的 C函數。如果你想把宏和一個手工編碼的函數名混合使用時(這並不是一個好的習慣),請你務必注意這一點。
小提示:如果出現了一些引用某個名爲zif_*()函數的編譯錯誤,那十有八九與 ZEND_FE 所定義的函數有關。
用來定義函數的宏
宏 |
說明 |
ZEND_FE(name, arg_types) |
定義了一個zend_function_entry內字段name爲 “name”的函數。arg_types應該被設定爲 NULL。這個聲明需要有一個對應的 C函數,該這個函數的名稱將自動以 zif_爲前綴。舉例來說,ZEND_FE("first_module", NULL)就引入了一個名爲 first_module()的 PHP函數,並被關聯到一個名爲 zif_first_module()的C函數。這個聲明通常與ZEND_FUNCTION搭配使用。 |
ZEND_NAMED_FE(php_name, name, arg_types) |
定義了一個名爲 php_name的 PHP函數,並且被關聯到一個名爲 name的 C函數。arg_types應該被設定爲 NULL。如果你不想使用宏 ZEND_FE自動創建帶有 zif_前綴的函數名的話可以用這個來代替。通常與ZEND_NAMED_FUNCTION搭配使用。 |
ZEND_FALIAS(name, alias, arg_types) |
爲 name創建一個名爲 alias的別名。arg_types應該被設定爲 NULL。這個聲明不需要有一個對應的 C函數,因爲它僅僅是創建了一個用來代替 name的別名而已。 |
PHP_FE(name, arg_types) |
以前的 PHP API,等同於 ZEND_FE。僅爲兼容性而保留,請儘量避免使用。 |
PHP_NAMED_FE(runtime_name, name, arg_types) |
以前的 PHP API,等同於ZEND_NAMED_FE。僅爲兼容性而保留,請儘量避免使用。 |
4)聲明 Zend模塊
Zend模塊的信息被保存在一個名爲zend_module_entry的結構,它包含了所有需要向 Zend提供的模塊信息。zend_module_entry的內部聲明”中看到這個 Zend模塊的內部定義:- typedef struct _zend_module_entry zend_module_entry;
- struct _zend_module_entry {
- unsigned short size;
- unsigned int zend_api;
- unsigned char zend_debug;
- unsigned char zts;
- char *name;
- zend_function_entry *functions;
- int (*module_startup_func)(INIT_FUNC_ARGS);
- int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
- int (*request_startup_func)(INIT_FUNC_ARGS);
- int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
- void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);
- char *version;
- … // 其餘的一些我們不感興趣的信息
- };
字段 |
說明 |
size, zend_api, zend_debug and zts |
通常用 "STANDARD_MODULE_HEADER"來填充,它指定了模塊的四個成員:標識整個模塊結構大小的 size,值爲 ZEND_MODULE_API_NO常量的 zend_api,標識是否爲調試版本(使用 ZEND_DEBUG進行編譯)的 zend_debug,還有一個用來標識是否啓用了 ZTS(Zend線程安全,使用 ZTS或USING_ZTS進行編譯)的 zts。 |
name |
模塊名稱 (像“File functions”、“Socket functions”、“Crypt”等等).這個名字就是使用phpinfo()函數後在“Additional Modules”部分所顯示的名稱。 |
functions |
Zend 函數塊的指針,這個我們在前面已經討論過。 |
module_startup_func |
模塊啓動函數。這個函數僅在模塊初始化時被調用,通常用於一些與整個模塊相關初始化的工作(比如申請初始化的內存等等)。如果想表明模塊函數調用失敗或請求初始化失敗請返回 FAILURE,否則請返回 SUCCESS。可以通過宏 ZEND_MINIT 來聲明一個模塊啓動函數。如果不想使用,請將其設定爲 NULL。 |
module_shutdown_func |
模塊關閉函數。這個函數僅在模塊卸載時被調用,通常用於一些與模塊相關的反初始化的工作(比如釋放已申請的內存等等)。這個函數和module_startup_func()相對應。如果想表明函數調用失敗或請求初始化失敗請返回 FAILURE,否則請返回 SUCCESS。可以通過宏ZEND_MSHUTDOWN來聲明一個模塊關閉函數。如果不想使用,請將其設定爲 NULL。 |
request_startup_func |
請求啓動函數。這個函數在每次有頁面的請求時被調用,通常用於與該請求相關的的初始化工作。如果想表明函數調用失敗或請求初始化失敗請返回 FAILURE,否則請返回 SUCCESS。注意:如果該模塊是在一個頁面請求中被動態加載的,那麼這個模塊的請求啓動函數將晚於模塊啓動函數的調用(其實這兩個初始化事件是同時發生的)。可以使用宏 ZEND_RINIT 來聲明一個請求啓動函數,若不想使用,請將其設定爲 NULL。 |
request_shutdown_func |
請求關閉函數。這個函數在每次頁面請求處理完畢後被調用,正好與request_startup_func()相對應。如果想表明函數調用失敗或請求初始化失敗請返回 FAILURE,否則請返回 SUCCESS。注意:當在頁面請求作爲動態模塊加載時,這個請求關閉函數先於模塊關閉函數的調用(其實這兩個反初始化事件是同時發生的)。可以使用宏 ZEND_RSHUTDOWN來聲明這個函數,若不想使用,請將其設定爲NULL。 |
info_func |
模塊信息函數。當腳本調用phpinfo()函數時,Zend便會遍歷所有已加載的模塊,並調用它們的這個函數。每個模塊都有機會輸出自己的信息。通常情況下這個函數被用來顯示一些環境變量或靜態信息。可以使用宏 ZEND_MINFO來聲明這個函數,若不想使用,請將其設定爲 NULL。 |
version |
模塊的版本號。如果你暫時還不想給某塊設置一個版本號的話,你可以將其設定爲NO_VERSION_YET。但我們還是推薦您在此添加一個字符串作爲其版本號。版本號通常是類似這樣: "2.5-dev", "2.5RC1", "2.5" 或者 "2.5pl3"等等。 |
Remaining structure elements |
這些字段通常是在模塊內部使用的,通常使用宏STANDARD_MODULE_PROPERTIES來填充。而且你也不應該將他們設定別的值。STANDARD_MODULE_PROPERTIES_EX通常只會在你使用了全局啓動函數(ZEND_GINIT)和全局關閉函數(ZEND_GSHUTDOWN)時纔用到,一般情況請直接使用 STANDARD_MODULE_PROPERTIES 。 |
在我們的例子當中,這個結構被定義如下:
- zend_module_entry hello_module_module_entry = {
- #if ZEND_MODULE_API_NO >= 20010901
- STANDARD_MODULE_HEADER,
- #endif
- "hello_module",
- hello_module_functions,
- PHP_MINIT(hello_module),
- PHP_MSHUTDOWN(hello_module),
- PHP_RINIT(hello_module), /* Replace with NULL if there's nothing to do at request start */
- PHP_RSHUTDOWN(hello_module), /* Replace with NULL if there's nothing to do at request end */
- PHP_MINFO(hello_module),
- #if ZEND_MODULE_API_NO >= 20010901
- "0.1", /* Replace with version number for your extension */
- #endif
- STANDARD_MODULE_PROPERTIES
- };
這基本上是你可以設定最簡單、最小的一組值。該模塊名稱爲"hello_module”,然後是所引用的函數列表,其後所有的啓動和關閉函數都沒有使用,均被設定爲了 NULL。
作爲參考,你可以在表 3 “所有可聲明模塊啓動和關閉函數的宏”中找到所有的可設置啓動與關閉函數的宏。這些宏暫時在我們的例子中還尚未用到,但稍後我們將會示範其用法。你應該使用這些宏來聲明啓動和關閉函數,因爲它們都需要引入一些特殊的變量(INIT_FUNC_ARGS和 SHUTDOWN_FUNC_ARGS),而這兩個參數宏將在你使用下面這些預定義宏時被自動引入(其實就是圖個方便)。如果你是手工聲明的函數或是對函數的參數列表作了一些必要的修改,那麼你就應該修改你的模塊相應的源代碼來保持兼容。
表3.3所有可聲明模塊啓動和關閉函數的宏
宏 |
描述 |
ZEND_MINIT(module) |
聲明一個模塊的啓動函數。函數名被自動設定爲zend_minit_<module> (比如:zend_minit_first_module)。通常與ZEND_MINIT_FUNCTION搭配使用。 |
ZEND_MSHUTDOWN(module) |
聲明一個模塊的關閉函數。函數名被自動設定爲zend_mshutdown_<module> (比如:zend_mshutdown_first_module)。通常與ZEND_MSHUTDOWN_FUNCTION搭配使用。 |
ZEND_RINIT(module) |
聲明一個請求的啓動函數。函數名被自動設定爲zend_rinit_<module> (比如:zend_rinit_first_module)。通常與ZEND_RINIT_FUNCTION搭配使用。 |
ZEND_RSHUTDOWN(module) |
聲明一個請求的關閉函數。函數名被自動設定爲zend_rshutdown_<module> (比如:zend_rshutdown_first_module)。通常與ZEND_RSHUTDOWN_FUNCTION搭配使用。 |
ZEND_MINFO(module) |
聲明一個輸出模塊信息的函數,用於phpinfo()。函數名被自動設定爲zend_info_<module> (比如:zend_info_first_module)。通常與ZEND_MINFO_FUNCTION搭配使用。 |
5) 實現 get_module()函數
這個函數只用於動態可加載模塊。我們先來看一下如何通過宏ZEND_GET_MODULE來創建這個函數:- #ifdef COMPILE_DL_HELLO_MODULE
- ZEND_GET_MODULE(hello_module)
- #endif
get_module()函數在模塊加載時被 Zend所調用,你也可以認爲是被你 PHP腳本中的dl()函數所調用。這個函數的作用就是把模塊的信息信息塊傳遞 Zend並通知 Zend 獲取這個模塊的相關內容。
如果你沒有在一個動態可加載模塊中實現get_module()函數,那麼當你在訪問它的時候 Zend 就會向你拋出一個錯誤信息。
6)實現導出函數
導出函數的實現是我們構建擴展的最後一步。在我們的hello_module例子中,函數被實現如下:- PHP_FUNCTION(hello_world)
- {
- RETURN_STRING("HelloWorld", 1);
- }
這個函數是用宏 ZEND_FUNCTION來聲明的,和前面我們討論的 Zend函數塊中的 ZEND_FE聲明相對應。在函數的聲明之後,我們的代碼便開始檢查和接收這個函數的參數。在將參數進行轉換後將其值返回。
7)啓動和終止函數
Zend允許模塊在加載和卸載時收到通知,以進行初始化和清除工作,我們要做的就是把相應函數傳遞給Zend,它會在合適的時機自動調用。
Zend提供瞭如下四個宏,分別用於聲明對應的函數:
宏 |
意義 |
ZEND_MODULE_STARTUP_D(hello_module) |
在加載模塊時調用 |
ZEND_MODULE_SHUTDOWN_D(hello_module) |
在卸載模塊時調用 |
ZEND_MODULE_ACTIVATE_D(hello_module) |
一個頁面開始運行時調用 |
ZEND_MODULE_DEACTIVATE_D(hello_module) |
一個頁面運行完畢時調用 |
在聲明模塊信息裏面
- zend_module_entry hello_module_module_entry = {
- #if ZEND_MODULE_API_NO >= 20010901
- STANDARD_MODULE_HEADER,
- #endif
- "hello_module",
- hello_module_functions,
- PHP_MINIT(hello_module),
- PHP_MSHUTDOWN(hello_module),
- PHP_RINIT(hello_module), /* Replace with NULL if there's nothing to do at request start */
- PHP_RSHUTDOWN(hello_module), /* Replace with NULL if there's nothing to do at request end */
- PHP_MINFO(hello_module),
- #if ZEND_MODULE_API_NO >= 20010901
- "0.1", /* Replace with version number for your extension */
- #endif
- STANDARD_MODULE_PROPERTIES
- };
1)STANDARD_MODULE_HEADER和STANDARD_MODULE_PROPERTIES宏填充了該結構的首尾部分,具體填充了什麼並不是我們需要關心的,並且爲了兼容後續版本也最好不要手工修改。
2)第二、三項是模塊名稱和導出函數,名稱可以任意填寫,導出函數就是我們在前面準備好的zend_function_entry數組。
3)後面的四個參數內容:
- PHP_MINIT(hello_module),
- PHP_MSHUTDOWN(hello_module),
- PHP_RINIT(hello_module), /* Replace with NULL if there's nothing to do at request start */
- PHP_RSHUTDOWN(hello_module), /* Replace with NULL if there's nothing to do at request end */
4) 參數值 PHP_MINFO(hello_module),也是指針函數,用於配合phpinfo()來顯示模塊信息。
這些宏的用法和ZEND_FUNCTION宏一樣,展開後就是聲明瞭特定原型的函數,其參數module可以是任意的,但最好使用模塊名稱。這些函數的參數中,對我們有用的是int module_number,它是模塊號,全局唯一,後面會提到其用處。
在聲明和實現相應函數時,都應該使用這些宏。最後,需要把這些函數填寫到zend_module_entry裏,可按順序使用如下的宏,這些宏生成相應的函數名稱:
ZEND_MODULE_STARTUP_N(hello_module) |
ZEND_MODULE_SHUTDOWN_N(hello_module) |
ZEND_MODULE_ACTIVATE_N(hello_module) |
ZEND_MODULE_DEACTIVATE_N(hello_module) |
在我們的hello_module中,對應的聲明(由於ext_skel生成的代碼是PHP API ,和ZEND API本質上一樣)
- PHP_MINIT_FUNCTION(hello_module);
- PHP_MSHUTDOWN_FUNCTION(hello_module);
- PHP_RINIT_FUNCTION(hello_module);
- PHP_RSHUTDOWN_FUNCTION(hello_module);
- PHP_MINFO_FUNCTION(hello_module);
- /* {{{ PHP_MINIT_FUNCTION
- */
- PHP_MINIT_FUNCTION(hello_module)
- {
- /* If you have INI entries, uncomment these lines
- REGISTER_INI_ENTRIES();
- */
- return SUCCESS;
- }
- /* }}} */
- /* {{{ PHP_MSHUTDOWN_FUNCTION
- */
- PHP_MSHUTDOWN_FUNCTION(hello_module)
- {
- /* uncomment this line if you have INI entries
- UNREGISTER_INI_ENTRIES();
- */
- return SUCCESS;
- }
- /* }}} */
- /* Remove if there's nothing to do at request start */
- /* {{{ PHP_RINIT_FUNCTION
- */
- PHP_RINIT_FUNCTION(hello_module)
- {
- return SUCCESS;
- }
- /* }}} */
- /* Remove if there's nothing to do at request end */
- /* {{{ PHP_RSHUTDOWN_FUNCTION
- */
- PHP_RSHUTDOWN_FUNCTION(hello_module)
- {
- return SUCCESS;
- }
- /* }}} */
- /* {{{ PHP_MINFO_FUNCTION
- */
- PHP_MINFO_FUNCTION(hello_module)
- {
- php_info_print_table_start();
- php_info_print_table_header(2, "hello_module support", "enabled");
- php_info_print_table_end();
- /* Remove comments if you have entries in php.ini
- DISPLAY_INI_ENTRIES();
- */
- }
- /* }}} */
如果不想使用這個函數,對應的參數都可以設置爲null
那麼模塊聲明
- zend_module_entry hello_module_module_entry = {
- #if ZEND_MODULE_API_NO >= 20010901
- STANDARD_MODULE_HEADER,
- #endif
- "hello_module",
- hello_module_functions,
- NULL,
- NULL,
- NULL, /* Replace with NULL if there's nothing to do at request start */
- NULL, /* Replace with NULL if there's nothing to do at request end */
- NULL,
- #if ZEND_MODULE_API_NO >= 20010901
- "0.1", /* Replace with version number for your extension */
- #endif
- STANDARD_MODULE_PROPERTIES
- };