PHP核心的學習--建立PHP擴充
開始看PHP核心也有一段時間了,現在開始邊學邊總結,今天就總結一下如何建立自己的PHP擴充。
我的環境如下:
系統:Ubuntu 14.04
php版本:5.5.19
參考摘錄:用C/C++擴充你的PHP
PHP取得成功的一個主要原因之一是它擁有大量的可用擴充。web開發人員無論有何種需求,這種需求最有可能在PHP發行包裡找到。PHP發行包包括支援各種資料庫,圖形檔案格式,壓縮,XML技術擴充在內的許多擴充。
擴充API的引入使PHP3取得了巨大的進展,擴充API機制使PHP開發社區很容易的開發出幾十種擴充。現在,兩個版本過去了,API仍然和PHP3時的非常相似。擴充主要的思想是:儘可能的從擴充編寫者那裡隱藏PHP的內部機制和指令碼引擎本身,僅僅需要開發人員熟悉API。
有兩個理由需要自己編寫PHP擴充。第一個理由是:PHP需要支援一項她還未支援的技術。這通常包括包裹一些現成的C函數庫,以便提供PHP介面。例如,如果一個叫FooBase的資料庫已推出市場,你需要建立一個PHP擴充協助你從PHP裡調用FooBase的C函數庫。這個工作可能僅由一個人完成,然後被整個PHP社區共用(如果你願意的話)。第二個不是很普遍的理由是:你需要從效能或功能的原因考慮來編寫一些商業邏輯。
假設你正在開發一個網站,需要一個把字串重複n次的函數。下面是用PHP寫的例子:
function util_str_repeat($string, $n){ $result = ""; for($i = 0; $i < $n; $i++){ $result .= $string; } return $result;} util_str_repeat("One", 3);// returns "OneOneOne".util_str_repeat("One", 1);// returns "One".
假設由於一些奇怪的原因,你需要時常調用這個函數,而且還要傳給函數很長的字串和大值n。這意味著在指令碼裡有相當巨大的字串串連量和記憶體重新分配過程,以至顯著地降低指令碼執行速度。如果有一個函數能夠更快地分配大量且足夠的記憶體來存放結果字串,然後把$string重複n次,就不需要在每次迴圈迭代中分配記憶體。
為擴充建立函數的第一步是寫一個函數定義檔案,該函數定義檔案定義了擴充對外提供的函數原形。該例中,定義函數只有一行函數原形util_str_repeat() :
string util_str_repeat(string str, int n)
函數定義檔案的一般格式是一個函數一行。你可以定義選擇性參數和使用大量的PHP類型,包括: bool, float, int, array等。
儲存為util.def檔案至PHP原代碼分類樹下(即與ext_skel檔案放在同一目錄下,我的目錄是/usr/share/php5/)。
然後就是通過擴充骨架(skeleton)構造器運行函數定義檔案的時機了。該構造器指令碼就是ext_skel。假設你把函數定義儲存在一個叫做util.def的檔案裡,而且你希望把擴充取名為util,運行下面的命令來建立擴充骨架:
sudo ./ext_skel --extname=util --proto=util.def
執行之後,我這裡報了如下錯誤:
./ext_skel: 1: cd: can't cd to /usr/lib/php5/skeletonCreating directory utilawk: cannot open /create_stubs (No such file or directory)Creating basic files: config.m4 config.w32 .svnignore util.c./ext_skel: 216: ./ext_skel: cannot open /skeleton.c: No such file php_util.h./ext_skel: 234: ./ext_skel: cannot open /php_skeleton.h: No such file CREDITS./ext_skel: 238: ./ext_skel: cannot open /CREDITS: No such file EXPERIMENTAL./ext_skel: 242: ./ext_skel: cannot open /EXPERIMENTAL: No such file tests/001.phpt./ext_skel: 247: ./ext_skel: cannot open /tests/001.phpt: No such file util.php./ext_skel: 251: ./ext_skel: cannot open /skeleton.php: No such filerm: cannot remove ‘function_entries’: No such file or directoryrm: cannot remove ‘function_declarations’: No such file or directoryrm: cannot remove ‘function_stubs’: No such file or directory [done].To use your new extension, you will have to execute the following steps:1. $ cd ..2. $ vi ext/util/config.m43. $ ./buildconf4. $ ./configure --[with|enable]-util5. $ make6. $ ./php -f ext/util/util.php7. $ vi ext/util/util.c8. $ makeRepeat steps 3-6 until you are satisfied with ext/util/config.m4 andstep 6 confirms that your module is compiled into PHP. Then, start writingcode and repeat the last two steps as often as necessary.
很明顯是/usr/lib/php5/skeleton路徑的錯誤,編輯ext_skel檔案,將/usr/lib/php5/skeleton修改為/usr/share/php5/skeleton,然後移除掉產生的util檔案夾,再次執行之前的命令,成功後提示如下:
Creating directory utilCreating basic files: config.m4 config.w32 .svnignore util.c php_util.h CREDITS EXPERIMENTAL tests/001.phpt util.php [done].To use your new extension, you will have to execute the following steps:1. $ cd ..2. $ vi ext/util/config.m43. $ ./buildconf4. $ ./configure --[with|enable]-util5. $ make6. $ ./php -f ext/util/util.php7. $ vi ext/util/util.c8. $ makeRepeat steps 3-6 until you are satisfied with ext/util/config.m4 andstep 6 confirms that your module is compiled into PHP. Then, start writingcode and repeat the last two steps as often as necessary.
然後採用靜態編譯的方式編譯擴充。為了使擴充能夠被編譯,需要修改擴充目錄util/下的config.m4檔案。擴充沒有包裹任何外部的C庫,你需要添加支援–enable-util配置開關到PHP編譯系統裡(–with-extension 開關用於那些需要使用者指定相關C庫路徑的擴充)。找到如下內容:
dnl PHP_ARG_ENABLE(util, whether to enable util support,dnl Make sure that the comment is aligned:dnl [ --enable-util Enable util support])
將前面的dnl 去掉,修改為如下結果:
PHP_ARG_ENABLE(util, whether to enable util support,Make sure that the comment is aligned:[ --enable-util Enable util support])
然後修改util.c檔案,找到如下代碼:
PHP_FUNCTION(util_str_repeat){ char *str = NULL; int argc = ZEND_NUM_ARGS(); int str_len; long n; if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE) return; php_error(E_WARNING, "util_str_repeat: not yet implemented");}
將其修改為如下代碼:
PHP_FUNCTION(util_str_repeat){ char *str = NULL; int argc = ZEND_NUM_ARGS(); int str_len; long n; char *result; /* Points to resulting string */ char *ptr; /* Points at the next location we want to copy to */ int result_length; /* Length of resulting string */ if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE) return; /* Calculate length of result */ result_length = (str_len * n); /* Allocate memory for result */ result = (char *) emalloc(result_length + 1); /* Point at the beginning of the result */ ptr = result; while (n--) { /* Copy str to the result */ memcpy(ptr, str, str_len); /* Increment ptr to point at the next position we want to write to */ ptr += str_len; }
/* Null terminate the result. Always null-terminate your strings even if they are binary strings */ *ptr = '\0'; /* Return result to the scripting engine without duplicating it*/ RETURN_STRINGL(result, result_length, 0);}
裡面的具體內容,就不在這裡說了,之後會慢慢寫到。
然後就是編譯,安裝。在util目錄下,命令如下(命令可能都需要加sudo):
phpize./configuremakemake testmake install
然後配置產生的擴充檔案,在php5.5版本中,進入到/etc/php5/mods-available目錄下,建立util.ini檔案,寫入如下內容:
extension=util.so
然後enable util擴充
sudo php5enmod util
最後,重啟php-fpm
sudo service php5-fpm restart
建立一個php檔案,測試一下,測試檔案如下:
phpfor ($i = 1; $i <= 3; $i++) { print util_str_repeat("CraryPrimitiveMan ", $i); print "\n";}?>
執行結果如下:
CraryPrimitiveMan CraryPrimitiveMan CraryPrimitiveMan CraryPrimitiveMan CraryPrimitiveMan CraryPrimitiveMan
這樣我們就成功建立了一個包含簡單的PHP函數的擴充。
盜圖一張~~
今天就先到這裡~~
-
1樓吉編輯
-
很不錯,感謝分享,有興趣寫書出書嗎?
-
Re: 瘋狂的原始人
-
@吉編輯,水平不夠啊。。。