php 5.x 擴展開發要點

導語:php 5.x 擴展開發要點,最近因項目需要開發了一個windows dll形式的php擴展,實現訪問soap webservice。下面就由小編為大家介紹一下php 5.x 擴展開發要點,歡迎大家閲讀!

php 5.x 擴展開發要點

開發環境是visual studio 2010(VC10)。測試用了 xampp-win32-5.6.28-1-VC11-installer。

  為什麼不直接用php soap擴展

php確實有一個php_soap的官方擴展,對soap webservice操作進行了封裝。在本案例中,此前博主已經用gSOAP對某個web service功能進行了封裝,發佈了兩個DLL訪問ws接口,實現數據查詢和加載。php擴展是在gSOAP開發的DLL基礎上,做進一步的封裝。

除此之外,還可以藉助gSOAP封裝的DLL,開發其他語言的擴展,例如python module。良好的功能劃分可以提高複用性,減少工作重複。

  安裝XAMPP

安裝的時候,至少選擇安裝apache和php。

  基本代碼結構

php以extension的.形式提供擴展。位於擴展功能底層核心的是zend引擎。

windows需要包含頭文件

#include "zend_config.w32.h"

/* include standard header */

#include "php.h"

PHP擴展的開發,主要通過一組宏定義,完成擴展的框架構建。例如

PHP_MINIT_FUNCTION(CustomExt); // module加載。通常是apache啟動的時候

PHP_MSHUTDOWN_FUNCTION(CustomExt); //module卸載。通常是apache關閉的時候。

PHP_RINIT_FUNCTION(CustomExt); //一般對應一個php腳本啟動的時候。

PHP_RSHUTDOWN_FUNCTION(CustomExt); // 一般對應一個php腳本退出的時候。

而PHP擴展中的函數,通過PHP_FUNCTION定義。例如

PHP_FUNCTION(paradb_wsquery_new);

PHP_FUNCTION(paradb_wsquery_prepare);

PHP_FUNCTION(paradb_wsquery_query);

PHP_FUNCTION(paradb_anytype_print);

PHP_FUNCTION(paradb_wsload_new);

PHP_FUNCTION(paradb_wsload_prepare);

PHP_FUNCTION(paradb_wsload_load);

  核心數據結構

zend_module_entry CustomExtModule_module_entry = {

STANDARD_MODULE_HEADER,

"CustomExt Module",

CustomExtModule_functions,

PHP_MINIT(CustomExt),

PHP_MSHUTDOWN(CustomExt),

PHP_RINIT(CustomExt),

PHP_RSHUTDOWN(CustomExt),

NULL,

NO_VERSION_YET, STANDARD_MODULE_PROPERTIES

};

這個結構引用了擴展需要的所有東西。PHP核心引擎通過這個結構找到擴展,調用相關的函數。

  內存管理

基本原則是在哪個層次申請的,就在哪個層次釋放。

在PHP層面,不要用malloc()函數,用php提供的emalloc()或者pemalloc()。這種方法申請的內存在php擴展代碼中,不必顯式釋放。php框架對這些內存進行了統一的管理。PHP核心可以確保託管內存不會發生內存泄露而危及平台的運行穩定。

php擴展可以調用第三方DLL中定義的函數,返回一個新的類實例。那麼這個類實例被創建和被析構的地方,都應該位於第三方DLL,例如不要在php層面用emalloc()為第三方DLL的對象申請內存。

如果使用EG(persistent_list)導致空指針訪問違例,博主建議在MINIT函數中自定義一個hashtable。

資源如何定義和返回

為了封裝c++結構,在PHP中使用自己定義的c++類,需要在php擴展中,定義資源resource。每一種資源類型對應着一個唯一的int。例如

int le_paradb_wsquery, le_paradb_xsdany;

#define PHP_PARADB_WSQUERY_RES_NAME "paradb wsquery"

#define PHP_GSOAP_XSD_ANYTYPE_RES_NAME "gsoap xsdany"

#define PHP_PARADB_WSLOAD_RES_NAME "paradb wsload"

在MINIT函數中,對資源進行定義。主要是定義了對應的析構函數。例如

le_paradb_wsquery = zend_register_list_destructors_ex(php_paradb_wsquery_dtor, NULL, PHP_PARADB_WSQUERY_RES_NAME, module_number);

PHP_FUNCTION(paradb_wsquery_new)

{

WsQuery *q;

char *name;

int name_len;

zval* p;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {

RETURN_FALSE;

}

if (name_len < 1) {

php_error_docref(NULL TSRMLS_CC, E_WARNING, "No endpoint given, WsQuery resource not created.");

RETURN_FALSE;

}

paradbc_wsquery_init2(&q, name);

php_printf("[paradb_wsquery_new %p]

", q);

ZEND_REGISTER_RESOURCE(return_value, q, le_paradb_wsquery);

}

通過ZEND_REGISTER_RESOURCE宏,一個結構被返回到PHP腳本。實際是一個指針,其內容是完全透明的。

返回的這個資源,PHP腳本在後續可以使用。

PHP_FUNCTION(paradb_wsquery_query2)

{

WsQuery *q;

zval *zq;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zq) == FAILURE) {

RETURN_FALSE;

}

ZEND_FETCH_RESOURCE(q, WsQuery*, &zq, -1, PHP_PARADB_WSQUERY_RES_NAME, le_paradb_wsquery);

......

}

通過ZEND_FETCH_RESOURCE宏,PHP擴展代碼得到這個resource,從zval得到實際的結構實例。

  其他事項

解決http 80端口被佔用的問題

windows system後台進程可能佔用了本地80端口,到XAMPP控制面板,config,修改,改成 Listen 8080,監聽8080端口。

PHP源碼下載

下載對應當前版本的PHP源碼。例如

解壓為 C:xamppphp-5.6.28

下載依賴包

需要下載bison。可將相關文件拷貝到windows系統目錄。

生成需要的頭文件

利用VC命令行環境,在PHP源碼目錄做一次configure操作。

Setting environment for using Microsoft Visual Studio 2010 x86 tools.

C:Program Files (x86)Microsoft Visual Studio 10.0VC>cd /d C:xamppphp-5.6.28

C:xamppphp-5.6.28>

C:xamppphp-5.6.28>set PATH=C:Program Filesisonin;%PATH%

C:xamppphp-5.6.28>

nmake可以不做。這裏主要是為了生成構建PHP擴展所需的頭文件。

注意:建議使用與xampp完全相同的VC版本,否則需要修改config.w32.h中的PHP_COMPILER_ID與xampp的完全一致。例如apache 報告錯誤

PHP Warning: PHP Startup: CustomExt Module: Unable to initialize module

Module compiled with build ID=API20131226,TS,VC10

PHP compiled with build ID=API20131226,TS,VC11

These options need to match

解決辦法是手動修改 main/config.w32.h

#define PHP_COMPILER_ID "VC11"

-END-