程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP綜合 >> PHP擴展開發--01.編寫一個helloWorld擴展

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

編輯:PHP綜合

為什麼要用C擴展

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

另外C擴展是在進程啟動時加載的,PHP代碼只能操作Request生命周期的數據,C擴展可操作的范圍更廣。

創建擴展骨架

##本例用的php版本的是5.3.3
cd php-5.3.3/ext/
./ext_skel --extname=myfun --proto=myfun.def
##執行成功後會生成myfun的編譯文件
ls myfun/
config.m4  config.w32  CREDITS  .cvsignore  EXPERIMENTAL  myfun.c  myfun.php  php_myfun.h  tests

修改config.m4

config.m4中 dnl 為注釋的意思

##動態編譯選項,通過.so的方式鏈接,去掉dnl注釋
PHP_ARG_WITH(myfun, for myfun support,
[  --with-myfun             Include myfun support])
##靜態編譯選項,通過enable來啟用,去掉dnl注釋
PHP_ARG_ENABLE(myfun, whether to enable myfun support,
[  --enable-myfun           Enable myfun support])

修改完成編譯下

phpize
./configure --enable-myfun
make
make install

在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);
}
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved