PHP擴展開發--編寫一個helloWorld擴展

爲何要用C擴展

C是靜態編譯的,執行效率比PHP代碼高不少。一樣的運算代碼,使用C來開發,性能會比PHP要提高數百倍。php

另外C擴展是在進程啓動時加載的,PHP代碼只能操做Request生命週期的數據,C擴展可操做的範圍更廣。數據庫

下載PHP7.1.1擴展

1.下載地址: http://php.net/get/php-7.1.1.tar.bz2/from/a/mirror vim

2.下載後進行解壓數組

建立擴展骨架

##本例用的是php7.1.1
cd ext
./ext_skel --extname=helloworld

修改config.m4

把下面幾行註釋打開,config.m4中 dnl 爲註釋的意思安全

##動態編譯選項,經過.so的方式連接,去掉dnl註釋:

PHP_ARG_WITH(helloworld, for helloworld support,
Make sure that the comment is aligned:
[  --with-helloworld             Include helloworld support])

##靜態編譯選項,經過enable來啓用,去掉dnl註釋:

 PHP_ARG_ENABLE(helloworld, whether to enable helloworld support,
 Make sure that the comment is aligned:
 [  --enable-helloworld           Enable helloworld support])

修改完成編譯下php7

phpize
#這裏用本身的php-config文件位置
./configure --with-php-config=./configure --with-php-config=/Applications/MAMP/bin/php/php7.1.1/bin/php-config 
make && make install
make test  #測試
#編輯php.ini,加上helloworld擴展
extension=helloworld.so

 

在myfun有個php的測試腳本,執行測試下函數

php -d enable_dl=On myfile.php

輸出結果:工具

Functions available in the test extension:
confirm_myfun_compiled
Congratulations! You have successfully modified ext/myfun/config.m4. Module myfun is now compiled into PHP.

其實confirm_myfun_compiled是構建工具幫咱們生成的測試函數性能

建立helloWorld函數

如今咱們來建立屬於本身的函數 helloWorld(),功能就是輸出 Hello World!測試

vim myfun/php_myfun.h
##在PHP_FUNCTION(confirm_myfun_compiled); 下追加一行
PHP_FUNCTION(helloWorld);

實現helloworld實體

 
 
vim myfun/myfun.c
##zend_function_entry myfun_functions 補充要實現的函數
const zend_function_entry myfun_functions[] = {
        PHP_FE(confirm_myfun_compiled,     NULL)           /* For testing, remove later. */
        PHP_FE(helloWorld,     NULL)  /*這是補充的一行,末尾沒有逗號*/
        {NULL, NULL, NULL}      /* Must be the last line in myfun_functions[] */
};
##在末尾實現helloworld的內容
PHP_FUNCTION(helloWorld)
{
        php_printf("Hello World!\n");
        RETURN_TRUE;
}

從新編譯

./configure && make && make install

測試

php -d enable_dl=On -r "dl('myfun.so');helloWorld();"
Hello World!
php -d enable_dl=On -r "dl('myfun.so');print confirm_myfun_compiled('helloWorld');"
Congratulations! You have successfully modified ext/myfun/config.m4. Module helloWorld is now compiled into PHP.

頭文件分析

#ifndef PHP_MYFUN_H
#define PHP_MYFUN_H
extern zend_module_entry myfun_module_entry;
#define phpext_myfun_ptr &myfun_module_entry
#ifdef PHP_WIN32
#       define PHP_MYFUN_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#       define PHP_MYFUN_API __attribute__ ((visibility("default")))
#else
#       define PHP_MYFUN_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(myfun); /*當PHP被裝載時,模塊啓動函數即被引擎調用。這使得引擎作一些例如資源類型,註冊INI變量等的一次初始化。*/
PHP_MSHUTDOWN_FUNCTION(myfun);  /*當PHP徹底關閉時,模塊關閉函數即被引擎調用。一般用於註銷INI條目*/
PHP_RINIT_FUNCTION(myfun); /*在每次PHP請求開始,請求前啓動函數被調用。一般用於管理請求前邏輯。*/
PHP_RSHUTDOWN_FUNCTION(myfun); /*在每次PHP請求結束後,請求前關閉函數被調用。常常應用在清理請求前啓動函數的邏輯。*/
PHP_MINFO_FUNCTION(myfun); /*調用phpinfo()時模塊信息函數被呼叫,從而打印出模塊信息。*/
PHP_FUNCTION(confirm_myfun_compiled);   /* For testing, remove later. */
PHP_FUNCTION(helloWorld);
#ifdef ZTS
#define MYFUN_G(v) TSRMG(myfun_globals_id, zend_myfun_globals *, v)
#else
#define MYFUN_G(v) (myfun_globals.v)
#endif
#endif  /* PHP_MYFUN_H */

confirm_myfun_compiled分析

PHP_FUNCTION(confirm_myfun_compiled)  //使用了宏PHP_FUNCTION(),該宏能夠生成一個適合於Zend引擎的函數原型
{
        char *arg = NULL;
        int arg_len, len;
        char *strg;
        //得到函數傳遞的參數
        //第一個參數是傳遞給函數的參數個數。一般的作法是傳給它ZEND_NUM_ARGS()。這是一個表示傳遞給函數參數總個數的宏。
        //第二個參數是爲了線程安全,老是傳遞TSRMLS_CC宏。
        //第三個參數是一個字符串,指定了函數指望的參數類型,後面緊跟着須要隨參數值更新的變量列表。由於PHP採用鬆散的變量定義和動態的類型判斷,這樣作就使得把不一樣類型的參數轉化爲指望的類型成爲可能。例如,若是用戶傳遞一個整數變量,可函數須要一個浮點數,那麼zend_parse_parameters()就會自動地把整數轉換爲相應的浮點數。若是實際值沒法轉換成指望類型(好比整形到數組形),會觸發一個警告。
        /*
         類型指示符
         l   long        符號整數
         d   double      浮點數
         s   char *, int 二進制字符串,長度
         b   zend_bool   邏輯型(1或0)
         r   zval *      資源(文件指針,數據庫鏈接等)
         a   zval *      聯合數組
         o   zval *      任何類型的對象
         O   zval *      指定類型的對象。須要提供目標對象的類類型
         z   zval *      無任何操做的zval
        */
        //第四個參數爲傳遞的參數數據的引用
        //第五個參數爲傳遞的參數個數
        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.",     "myfun", arg);
        /*
          經過設置RETURN_type()的方式,將返回控制到PHP。下表解釋了大多數存在的宏。
          RETVAL_LONG(l)  整數
          RETVAL_BOOL(b)  布爾數(1或0)
          RETVAL_NULL()   NULL
          RETVAL_DOUBLE(d)    浮點數
          RETVAL_STRING(s, dup)   字符串。若是dup爲1,引擎會調用estrdup()重複s,使用拷貝。若是dup爲0,就使用s
          RETVAL_STRINGL(s, l, dup)   長度爲l的字符串值。與上一個宏同樣,但由於s的長度被指定,因此速度更快。
          RETVAL_TRUE     返回布爾值true。注意到這個宏沒有括號。
          RETVAL_FALSE    返回布爾值false。注意到這個宏沒有括號。
          RETVAL_RESOURCE(r)  資源句柄。
        */
        RETURN_STRINGL(strg, len, 0);
}
相關文章
相關標籤/搜索