Linux 下PHP擴充開發系列:二. 一個典型的擴充開發

來源:互聯網
上載者:User
看完前言中所說的一些內容後,各位應該對PHP擴充開發有個籠統的瞭解了,可能有些人會覺得開發擴充很麻煩很複雜,實際上並非如此,這一篇我們就快速進入角色,開發出我們的第一個擴充。

一、編譯PHP

開發之前還需要先準備好PHP源碼並編譯,過程如下:

tar -zxvf php-5.3.9.tar.gzcd php-5.3.9

我使用的是php5.3.9,解壓後,我們進入了PHP源碼目錄,然後我們直接編譯並增加php.ini:

./configure --prefix=/usr/local/webserver/php --enable-fastcgi --enable-fpm --enable-debugmake && make installcp /home/soft/php-5.3.9/php.ini-development /usr/local/webserver/php/lib/php.ini

編譯完成,我沒有靜態編譯其他擴充,但是開啟了debug,這個後面會用到。然後修改php.ini中對應的項,這裡就不細說了。

現在把PHP相關加入環境變數中,省去後面很多工作:

vim /root/.bash_profile

我是使用root,其他不同使用者修改對應使用者目錄下的.bask_profile檔案,在檔案中的PATH後面加入:/usr/local/webserver/php/bin/,類似下面這樣:

PATH=$PATH:$HOME/bin:/usr/local/webserver/php/bin/

環境變數設定好了,我們查看下PHP版本:

OK,編譯工作完成,讓我們繼續。

二、典型開發流程

一個典型的擴充開發流程如:

三、擴充功能定義

先定義我們要完成的擴充功能:

該擴充只有一個功能,就是重寫PHP的系統函數ip2long(),解決ip2long在32位與64位系統下值不同的問題(該問題是因為32位與64位的整形範圍不同導致的,具體原因請google)。

我們新的ip2long固定返回32位有符號整數,範圍-2147483648 到 2147483647,與32位系統相同。

我們的副檔名稱為 myip,函數名為 ip2long32

擴充的功能與名稱都OK了,現在按流程進行開發。

四、正式開發

1. 產生開發骨架

首先進入源碼擴充目錄:

cd /home/soft/php-5.3.9/ext

然後瞭解下PHP提供的擴充骨架工具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 (產生的程式碼中不顯示各種協助注釋)

這次我們準備用到兩個選項,--extname=myip 即定義擴充的名稱,而--proto=myip.pro則是定義擴充的函數原型,首先我們產生擴充函數原型檔案:

vim myip.pro

加入以下內容:

int ip2long32(string ip)

這意味著我們的擴充中有一個函數,傳回值為int型,輸入為string。

這時候執行以下命令產生擴充骨架:

./ext_skel --extname=myip --proto=myip.pro

OK,這時候你會發現在當前PHP擴充目錄下產生了一個子目錄myip,進入myip看下:

cd myipll

你會發現產生了一堆檔案,如:

此時我們就可以進行第二步了。

2. 修改config.m4

關於config.m4檔案的功能,我們留到後面的文章中在詳細進行說明,現在只說明要做什麼。

使用vim編輯config.m4:

vim config.m4

將16至18行行首的dnl去掉,如下:

具體這樣做的原因在後面的文章中會說明,這邊我們直接退出並儲存config.m4,繼續進入下一步。

3. 編碼

重頭戲來啦,終於可以進入myip.c中進行功能的編碼了,一起歡呼下吧!

vim myip.c

找到所示的位置:

圖中就是擴充骨架工具根據我們提供的函數原型產生的對應函數,此處有幾個需要注意的地方:

1. PHP_FUNCTION:是PHP核心定義的一個宏,與ZEND_FUNCTION相同,用於定義擴充函數,實際產生的函數名稱為zif_ip2long32。

2. zend_parse_parameters:由於PHP為弱類型語言,而C是強型別,因此需要使用該函數用於接收PHP傳入的參數,並進行一定的類型轉換,將PHP的變數轉為C語言能夠辨認的類型。

zend_parse_parameters函數的原型如下:

zend_parse_parameters(int num_args TSRMLS_CC, char *type_spec, …);

參數說明:

num_args:傳遞給函數的參數個數。通常的做法是使用宏 ZEND_NUM_ARGS()。 TSRMLS_CC:安全執行緒,總是傳遞TSRMLS_CC宏。 詳解:http://www.54chen.com/php-tech/what-is-tsrmls_cc.html type_spec:第三個參數是一個字串,指定了函數期望的參數類型 ...:需要隨參數值更新的變數列表]

type_spec是格式化字串,其常見的含義如下:

參數 代表著的類型

b Boolean

l Integer 整型

d Floating point 浮點型

s String 字串

r Resource 資源

a Array 數組

o Object instance 對象

O Object instance of a specified type 特定類型的對象

z Non-specific zval 任意類型~

Z zval**類型

f 表示函數、方法名稱

我們將該函數修改為如下內容:

 PHP_FUNCTION(ip2long32) {         char *ip = NULL;         int argc = ZEND_NUM_ARGS();         int ip_len;          if (zend_parse_parameters(argc TSRMLS_CC, "s", &ip, &ip_len) == FAILURE) {                 return;         }                  int32_t ip_int32;         unsigned char ip1, ip2, ip3, ip4;                  sscanf(ip, "%hhu.%hhu.%hhu.%hhu", &ip1, &ip2, &ip3, &ip4);         ip_int32 = (int32_t)((ip1 << 24) | (ip2 << 16) | (ip3 << 8) | ip4);         RETURN_LONG(ip_int32); }

功能完成了,這邊有個RETURN_LONG(ip_int32)比較特殊,這也是PHP核心提供的宏,用於傳回值給PHP,具體說明如下:

設定傳回值並且結束函數        設定傳回值             宏傳回型別和參數
RETURN_LONG(l)           RETVAL_LONG(l)           整數
RETURN_BOOL(b)          RETVAL_BOOL(b)          布爾數(1或0)
RETURN_NULL()           RETVAL_NULL()           NULL
RETURN_DOUBLE(d)        RETVAL_DOUBLE(d)        浮點數
RETURN_STRING(s, dup)       RETVAL_STRING(s, dup)      字串。如果dup為1,引擎會調用estrdup()重複s,使用拷貝。如果dup為0,就使用s
RETURN_STRINGL(s, l, dup)     RETVAL_STRINGL(s, l, dup)    長度為l的字串值。與上一個宏一樣,但因為s的長度被指定,所以速度更快。
RETURN_TRUE           RETVAL_TRUE            返回布爾值true。注意到這個宏沒有括弧。
RETURN_FALSE           RETVAL_FALSE          返回布爾值false。注意到這個宏沒有括弧。
RETURN_RESOURCE(r)       RETVAL_RESOURCE(r)       資源控制代碼。

編碼完成了,儲存並退出,然後我們可以開始編譯了。

4. 編譯

phpize./configure --with-php-config=/usr/local/webserver/php/bin/php-configmake && make install

不出意外的話編譯完成後會有如下提示:

Installing shared extensions:     /usr/local/webserver/php/lib/php/extensions/debug-non-zts-20090626/

進入該目錄看下是否已經有myip.so,有的話最後我們就可以修改php.ini載入該so檔案

5. 修改php.ini

cd /usr/local/webserver/php/libvim php.ini

修改extension_dir,並加入 extension = myip.so

extension_dir = "/usr/local/webserver/php/lib/php/extensions/debug-non-zts-20090626/"extension = myip.so

退出儲存,並重啟php,如果是使用Phpfpm的話可以執行如下命令:

kill -USR2 `cat /usr/local/webserver/php/var/run/php-fpm.pid`

看下擴充是否正常載入:

[root@tm977 lib]# php -m|grep myipmyip

說明已經正常載入了,最後我們測試下擴充函數吧!

6. 測試

php -r "var_dump(ip2long32('192.168.1.1'));"int(-1062731519)php -r "var_dump(ip2long('192.168.1.1'));"  int(3232235777)

如上所示,ip2long32輸出的是32位有符號整數,而ip2long輸出的是64位不帶正負號的整數,大功告成!

五、小結

通過這一次的開發樣本,是不是覺得其實開發一個擴充很簡單?

確實是的,但是也別高興的太早了,實際上要開發出功能強大的擴充遠不是這麼簡單的事情,後面的文章我會繼續深入,一邊開發一邊瞭解更加複雜的PHP核心代碼。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.