PHP擴充開發(1):入門,php擴充
有關PHP擴充開發的文章、部落格已經很多了,比較經典的有:
我準備在此系列博文中總結我有關PHP擴充開發的學習和感悟,力圖簡單清晰地描述在Linux系統下開發一個PHP擴充應該具備的最基本知識。水平較低,難免有錯誤,望指出。
準備工作
首先要擷取一份PHP源碼(可以從Github上籤出,或者到官網上下載最新的穩定版),然後編譯之。為了加快編譯速度,我們推薦禁用所有額外的擴充(使用--disable-all選項),但最好開啟debug(使用--enable-debug選項)和安全執行緒(使用--enable-maintainer-zts),但要在發布擴充的時候關閉debug,視情況選擇是否需要開啟安全執行緒:
$ ./buildconf --force$ ./configure --disable-all --enable-debug --enable-maintainer-zts$ make
注意,我們沒有指定--prefix選項(同時也沒有make install),因為這不是必須的。注意查看輸出資訊,也許你需要安裝一些依賴包才能成功編譯PHP。
編譯後的PHP的可執行程式在源碼的sapi目錄下,對應不同的宿主環境有不同的子目錄,我們以後都主要使用cli(command line interface)環境,可以建一個別名方便引用:
$ alias php-dev=/usr/local/src/php-5.6.5/sapi/cli/php
有一些命令列選項是很有用的:
php-dev -h # 列印協助資訊php-dev -v # 列印版本資訊php-dev --ini # 列印配置資訊 php-dev -m # 列印載入的模組資訊php-dev -i # phpinfophp-dev -r # 執行code裡的代碼
擴充骨架
PHP的所有官方擴充都在源碼的ext目錄下,我們自己寫的擴充也可以放在該目錄下。注意,該目錄下有個名為ext_skel的shell指令碼,它是用來產生PHP擴充骨架的,使用該指令碼,可以幫我們快速建立PHP擴充:
$ ./ext_skel --extname=myext
上面的命令幫我們建立了一個名為myext的擴充,源碼在myext目錄下。不帶任何參數的執行該指令碼可以列印協助資訊,這樣你可以查看到該指令碼提供的更多選項。
接下來讓我們完成我們的擴充。進入myext目錄,編輯config.m4設定檔,找到PHP_ARG_ENABLE宏函數,去掉前面的dnl注釋(共三行)。退回到源碼根目錄,重新執行buildconf、configure和make命令:
$ ./buildconf --force$ ./configure --help | grep myext --enable-myext Enable myext support$ ./configure --disable-all --enable-myext --enable-debug --enable-maintainer-zts$ make
注意,我們用./configure --help | grep myext列印了我們擴充的載入情況,如果看不到下面的輸出,則說明我們的擴充沒有配置成功,回頭檢查下config.m4檔案。
這次編譯應該非常快,因為大部分代碼都已經編譯過了。PHP還有另外一種編譯擴充的方法(使用動態串連的方式,將擴充編譯為.so的檔案),不過我們推薦在開發擴充的時候使用靜態編譯,因為這樣省去了在設定檔中載入擴充的步驟。
一切順利的話,我們的第一個擴充就已經可以執行了:
$ php-dev -m | grep myextmyext$ php-dev -r 'echo confirm_myext_compiled("myext") . "\n";'Congratulations! You have successfully modified ext/myext/config.m4. Module myext is now compiled into PHP.
第一個命令顯示了我們的擴充已經被載入。第二個命令執行了ext_skel擴充骨架自動為我們建立的函數。當然,這個函數毫無意義,不過我們可以很容易的把這個函數改編成hello world。
手動建立擴充
大部分教程都是以ext_skel擴充骨架為原型講述擴充開發的,這種做法當然很方便快捷。但是我個人更喜歡純手工開發擴充的方式,因為這樣更容易理解其中的每一個細節。
手動建立擴充,先進入ext目錄,建立我們的擴充目錄myext2。有幾個檔案是必須的:config.m4,myext2.c和php_myext2.h。
首先,我們來編寫設定檔config.m4:
PHP_ARG_ENABLE(myext2, whether to enable myext2 support,[ --enable-myext2 Enable myext2 support])if test "PHP_MYEXT2" != "no"; then PHP_NEW_EXTENSION(myext2, myext2.c, $ext_shared)fi
config.m4其實是autoconf程式使用的設定檔,autoconf是autotools工具箱裡重要的組成。完整介紹autoconf的用法是需要很長時間的,好在我們這裡的用法非常簡單。
PHP_ARG_ENABLE是PHP為autoconf定義的宏函數,myext2是它的第一個參數,指出了擴充的名字;後面兩個參數只是在make和configure執行時用來顯示的,所以我們可以隨便寫。[ ]在autoconf文法中的作用類似於雙引號,用來包裹字串(注意第二個參數中包含了空格,但是可以不用方括弧起來)。還有第四個參數用來指明擴充預設是開啟還是關閉(yes或no),預設是no。
下面三行其實就是shell文法,判斷我們是否開啟了PHP_MYEXT2擴充模組。如果開啟了該擴充模組(--enable-myext2),則$PHP_MYEXT2變數的值不為no,因此執行PHP_NEW_EXTENSION宏。這個宏函數也是PHP為autoconf定義的擴充文法,第一個參數同樣是副檔名稱;第二個參數是擴充要編譯的C檔案,如果有多個,依次寫下去就可以了(空格分隔);第三個參數固定是$ext_shared。
接下來編寫php_myext2.h標頭檔,該檔案的命名是PHP擴充的規範 — php_副檔名.h:
1 #ifndef PHP_MYEXT2_H 2 #define PHP_MYEXT2_H 3 4 extern zend_module_entry myext2_module_entry; 5 #define phpext_myext2_ptr &myext2_module_entry 6 7 #define PHP_MYEXT2_VERSION "0.1.0" 8 9 /* prototypes */10 PHP_FUNCTION(hello);11 12 #endif /* PHP_MYEXT2_H */
這裡主要的代碼是定義了名為phpext_myext2_ptr的宏,PHP底層通過該宏來引用我們的擴充。可以看出,該宏的命名同樣是有規範的 — phpext_副檔名_ptr。而myext2_module_entry是我們稍後要在.c檔案裡定義的結構體,它的命名也是規範的 — 副檔名_module_entry。
此外我們還定義了一個標識我們擴充版本號碼的宏和一個函數原型(通過PHP_FUNCTION宏,PHP_FUNCTION宏函數的參數是外部可使用的函數名),稍後我們會來實現這個函數。
最後來看下myext2.c檔案的實現:
1 #include "php.h" 2 #include "php_myext2.h" 3 4 /* {{{ myext2_functions[] 5 * 6 * Every user visible function must have an entry in myext2_functions[]. 7 */ 8 static const zend_function_entry myext2_functions[] = { 9 PHP_FE(hello, NULL)10 PHP_FE_END11 };12 /* }}} */13 14 /* {{{ myext2_module_entry15 */16 zend_module_entry myext2_module_entry = {17 STANDARD_MODULE_HEADER,18 "myext2", /* module name */19 myext2_functions, /* module functions */20 NULL, /* module initialize */21 NULL, /* module shutdown */22 NULL, /* request initialize */23 NULL, /* request shutdown */24 NULL, /* phpinfo */25 PHP_MYEXT2_VERSION, /* module version */26 STANDARD_MODULE_PROPERTIES27 };28 /* }}} */29 30 #ifdef COMPILE_DL_MYEXT231 ZEND_GET_MODULE(myext2)32 #endif33 34 /* {{{ proto void hello()35 Print "hello world!" */36 PHP_FUNCTION(hello)37 {38 php_printf("hello world!\n");39 }40 /* }}} */
對比下擴充骨架建立的.c檔案就會發現,我們的.c檔案非常的簡單,其實這些對一個最基本的擴充來說就已經足夠了。
上面的代碼是簡單而清晰的,大部分注釋已經很具說明性了。我們再簡要概括下:
這裡面涉及了一些宏,比如PHP_FE,PHP_FE_END,PHP_FUNCTION等等,完整介紹這些宏要到後續的博文中才可以,眼下最簡單的辦法就是記住這些宏。
注意到我們每一個檔案的命名,變數的命名,空格和縮排,以及注釋等都是非常規範的,遵循這些規範,可以使我們編寫的代碼和PHP本身的代碼更加契合,我們也推薦你使用這樣的規範來開發PHP擴充。
最後,編譯運行我們的擴充:
$ ./buildconf --force$ ./configure --help | grep myext2 --enable-myext2 Enable myext2 support$ ./configure --disable-all --enable-myext2 --enable-debug --enable-maintainer-zts$ make$ php-dev -m | grep myext2myext2$ php-dev -r 'hello();'hello world!
http://www.bkjia.com/PHPjc/960240.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/960240.htmlTechArticlePHP擴充開發(1):入門,php擴充 有關PHP擴充開發的文章、部落格已經很多了,比較經典的有: 我準備在此系列博文中總結我有關PHP擴充開發...