[转载]用c写PHP的扩展接口(php5,c++)
原文[http://bugs.tutorbuddy.com/php5cpp/php5cpp/]
第1節(jié). 開始之前
開始前,我要說明:這篇文章所描述的主要是在UNIX的PHP環(huán)境上的。
另外一點(diǎn)我要說明的是:文中所介紹的方法在PHP 4.3.x和PHP 5下都是可行的。盡管我們在開始的時候會基于PHP5來介紹,但是你會發(fā)現(xiàn)這些方法在PHP 4.3.x中也是可行的。
我在本文中有一些約定...
$PHP_HOME 是指你的PHP源代碼的位置,如:你解開的PHP源代碼包所放的位置。在我的系統(tǒng)中指的是:/home/jay/setup/php/php-x.x.x.
我們用來做例子的模塊叫做php5cpp.
第2節(jié).安裝
在你用C++編寫PHP擴(kuò)展前,你先要搭建一個基本的擴(kuò)展模塊的架構(gòu)。在UNIX下,你可以運(yùn)行一個在 $PHP_HOME/ext 下叫做ext_skel 的shell腳本。先切換到 $PHP_HOME/ext 目錄和執(zhí)行那個shell腳本,并用 --extname 參數(shù)為你的擴(kuò)展模塊命名。
jay@monty ~ $ cd setup/php/php-5.x.x/ext
jay@month ext $ ./ext_skel --extname php5cpp
在Windows系統(tǒng),目前PHP CVS代碼中,可以使用位于 $PHP_HOME/ext 的 ext_skel_win32.php 的PHP腳本是。也許它會成為PHP5的一部分及被PHP 4.x的分支包含。但這只是我大膽的猜想,我并不知道會不會實(shí)現(xiàn)...
這樣,在$PHP_HOME/ext/php5cpp下,我們已經(jīng)有了一個基本的PHP擴(kuò)展模塊架構(gòu)。唯一的問題是,它是為C搭建的,而不是為C++。
第3節(jié).修改config.m4
現(xiàn)在我們要修改那個擴(kuò)展模塊的config.m4 文件以支持C++。
你不需要做太多的改動,要做的只是告訴編譯PHP的系統(tǒng),你的模塊是使用C++的,而且需要連接C++標(biāo)準(zhǔn)庫。下邊是一個刪去自動生成的注釋后,php5cpp 擴(kuò)展模塊的config.m4文件的例子:
PHP_ARG_ENABLE(php5cpp, for php5cpp support,
[ --enable-php5cpp Enable php5cpp support])
if test "$PHP_php5cpp" != "no" ; then
PHP_REQUIRE_CXX()
PHP_NEW_EXTENSION(php5cpp, php5cpp.cpp, $ext_shared)
fi
注意其中的PHP_REQUIRE_CXX(),和php5cpp.c 已經(jīng)變成了 php5cpp.cpp 了。
第4節(jié).編寫代碼
修改完config.m4 后,你可以編寫代碼了。記住把php5cpp.c 修改成C++文件的名字。根據(jù)前邊 config.m4的修改,在這里我們把它改成 php5cpp.cpp.
現(xiàn)在你可以開始編寫你的代碼了。但是你如果現(xiàn)在編譯這個擴(kuò)展代碼的話,可能會生一個so,并且不會產(chǎn)生任何編譯錯誤,但是并不能在PHP中使用。如果你把它靜態(tài)編譯進(jìn)PHP,則會產(chǎn)生連接錯誤。這是因?yàn)镃和C++的變量空間的不一致引起的 (PHP是使用C來編寫,你的擴(kuò)展使用C++來編寫) 。
修改的方法就是,你要告訴你的擴(kuò)展模塊,將把一些PHP API函數(shù)當(dāng)成C函數(shù)來對待(他們是用C來寫的),而不是當(dāng)成C++。
你需要把一些代碼用 BEGIN/END_EXTERN_C()包起來。你的php5cpp.cpp 可能要寫成像下邊的樣子:
extern "C" {
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
}
#ifdef COMPILE_DL_PHP5CPP
BEGIN_EXTERN_C()
ZEND_GET_MODULE(php5cpp)
END_EXTERN_C()
#endif
一般地,我們是用 BEGIN/END_EXTERN_C() 來包起頭文件的那些內(nèi)容,如對 ZEND_GET_MODULE(php5cpp)那樣。但是在引用聲明 BEGIN/END_EXTERN_C() 的 zend.h文件前,可以通過使用extern "C" 來達(dá)到相同的作用。
在Windows系統(tǒng),可以使用Visual C++編譯你的擴(kuò)展模塊。這也需要在的擴(kuò)展模塊的頭部加上類似的聲明:
#ifdef PHP_WIN32
#include
#include
#endif
這樣你可以使得你的代碼保持跨平臺的特性。
第5節(jié).編譯擴(kuò)展模塊
現(xiàn)在可以去編譯擴(kuò)展模塊了。如果你想把它編譯成靜態(tài)模塊(把它做為PHP的一個部分編譯進(jìn)PHP中去),去到PHP的根目錄$PHP_HOME,刪去 configure 文件和運(yùn)行buildconf (譯:需要libtool的支持)。
然后用你平常用的參數(shù)運(yùn)行 configure 并加上--enable-php5cpp 項(xiàng)。運(yùn)行 make clean, make, make install ,并完成其它的一些必要操作,如:重新編譯Apache。
如果你想用動態(tài)鏈接庫的方式編譯擴(kuò)展模塊,到你的模塊的目錄下,運(yùn)行phpize 命令(假設(shè)你已經(jīng)安裝了PEAR),它會為你的模塊創(chuàng)建一個 configure 腳本。然后運(yùn)行configure, make 和 make install。如果你想讓你的模塊自動加載,你要修改php.ini 以加載正確的文件。如:加上類似的一行:extension=php5cpp.so 。
現(xiàn)在你的PHP擴(kuò)展模塊已經(jīng)編譯好了。試著運(yùn)行一下在模塊目錄下自動生成的php5cpp.php ,看看是不是一切正常?:)
第6節(jié).將C++類影射到PHP中
目錄
類MyClass
宏、函數(shù)及其它
封裝代碼
PHP 5的類支持很多新的特性。如:權(quán)限(protected, public, private),異常,interfaces,等等。在這個簡單的介紹中,我們只做最基本的事情:使PHP可以影射到C++的類。這樣你可以用PHP中使用你的類,之后的事情將會變得很簡單的。在看下面的介紹之前,你可以參考一下Sqlite, SimpleXML 及 cryptopp-php 模塊的代碼。
這里介紹一下。我們要用一個C++類做為例子,就叫做 MyClass吧。在PHP術(shù)語中,把它叫做一個resource(資源)。PHP常使用這類的東西,如數(shù)據(jù)庫的連接就是resource,它也可能是一個指向?qū)嶋Hresource的struct(結(jié)構(gòu))。在C++中,class 實(shí)際上是struct的一個近義詞(struct默認(rèn)為public,classe默認(rèn)為private --僅這個區(qū)別而已)。
在結(jié)構(gòu)化的接口中,我們會用類似以下的PHP代碼來使用resources:
$m = myclass_new();
myclass_set_string($m, 'hello world!');
var_dump(myclass_get_string($m));
?>
在面向?qū)ο笫降慕涌谥?#xff0c;一樣可以使用PHP resources,不過已經(jīng)被封裝在一個PHP對象中了,如:
$m = new MyClass;
$m->setString('hello world!');
var_dump($m->getString());
?>
我們不需要關(guān)心被封裝的實(shí)際的代碼做了些什么。當(dāng)我們有一個叫MyClass的C++類。我們可以把這個C++類當(dāng)成resources并把C++類里的方法封裝成PHP的函數(shù)。然后我們也可以把它封裝成一個PHP的對象,使得可以像一般的C++那樣使用。
在看本文時,記住我們的目的就是:把C++類封裝成PHP可以使用的結(jié)構(gòu)化的函數(shù)或?qū)ο蠡念悺R苍S在一開始有些東西會令你迷惑,但讀下去后就會慢慢明白的了。中間會有很多的宏定義,但當(dāng)你看明白后,會覺得所有東西都很清淅很容易了。
1 類MyClass
首先我們需要一個類。下邊是一個只有一個私有屬性和幾個公有方法的簡單的類。
以下是這個類的聲明頭文件 myclass.h:
#ifndef __MYCLASS_H__
#define __MYCLASS_H__
#include
using namespace std;
class MyClass {
private:
string itsString;
public:
MyClass(string s = "default");
~MyClass();
string getString() const;
bool setString(const string s);
};
#endif
下邊是定義代碼myclass.cpp:
#include "myclass.h"
MyClass::MyClass(string s)
{
itsString = s;
}
MyClass::~MyClass()
{
}
string MyClass::getString() const
{
return itsString;
}
bool MyClass::setString(const string s)
{
itsString = s;
return true;
}
這只是一個做為例子的類。
然后我們要讓構(gòu)建系統(tǒng)知道和可以編譯這些文件。把config.m4文件做以下修改:
PHP_NEW_EXTENSION(php5cpp, php5cpp.cpp, $ext_shared)
becomes...
PHP_NEW_EXTENSION(php5cpp, php5cpp.cpp myclass.cpp, $ext_shared)
在php5cpp.cpp 文件中增加#include “myclass.h” :
extern "C" {
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
}
#include "php_php5cpp.h"
#include "myclass.h"
不要把include php_php5cpp.h 和 myclass.h 的語句放在 extern "C" 中,否則會出現(xiàn)錯誤。
2 宏、函數(shù)及其它
為了讓這個模塊可以同時在PHP 4和PHP 5使用,我們需要使用一些宏去聲明是依賴于PHP 4或是PHP 5的。因?yàn)镻HP 4和PHP 5的執(zhí)行文件是不兼容的,所以你需要為這兩個PHP的版本分別編譯不同的版本。
通常我會把這些宏的聲明放在一個單獨(dú)的文件中。在這個例子里,就放在objects.h 中吧。
objects.h 中要寫一些PHP 5需要的函數(shù),如:
#ifndef __PHP5CPP_OBJECTS_H__
#define __PHP5CPP_OBJECTS_H__
#if PHP_MAJOR_VERSION == 5
zend_class_entry *php5cpp_ce_myclass;
static zend_object_handlers php5cpp_object_handlers_myclass;
function_entry php5cpp_myclass_methods[] = {
ZEND_ME_MAPPING(MyClass, myclass_new, NULL)
ZEND_ME_MAPPING(setString, myclass_set_string, NULL)
ZEND_ME_MAPPING(getString, myclass_get_string, NULL)
{NULL, NULL, NULL}
};
typedef enum {
is_myclass
} php5cpp_obj_type;
struct php5cpp_obj {
zend_object std;
int rsrc_id;
php5cpp_obj_type type;
union {
MyClass *myclass;
} u;
};
你可以發(fā)現(xiàn),為了保持一致,每個聲明都加上了php5cpp_ 前綴。這是習(xí)慣上的一種約定。
另外,#if PHP_MAJOR_VERSION == 5表明在下邊的那些宏、函數(shù)等都只是在PHP 5下才生效,當(dāng)我們在PHP 4下編譯時它們會被預(yù)處理忽略掉。
php5cpp_ce_myclass 是類 MyClass 的入口。 php5cpp_object_handlers_myclass 是類的內(nèi)部處理handler(句柄)。
php5cpp_myclass_methods[] 把MyClass中的函數(shù)影射成可在PHP使用的標(biāo)準(zhǔn)函數(shù)。這樣我們在PHP中就可以使用 myclass_new, myclass_get_string 等來執(zhí)行這些函數(shù)。你會發(fā)現(xiàn)這里并沒有定義myclass_destroy函數(shù),因?yàn)樵谀銓σ粋€類實(shí)例使用unset() 時,系統(tǒng)會自動調(diào)用它的釋構(gòu)函數(shù)的了。
在結(jié)構(gòu)php5cpp_obj中的枚舉變量 php5cpp_obj_type 聲明了對象的類型。如果你想在擴(kuò)展中再加入一個類,如: AnotherClass,你需要再增加一項(xiàng),如: is_anotherclass。
php5cpp_obj 結(jié)構(gòu)中聲明了在PHP中使用的一些基本信息,包括:
一個resource ID:rsrc_id ,它指向PHP內(nèi)部的一個記錄著C++對象的track的resource(資源)。實(shí)際上,我們的類在PHP中的操作是更像是一個PHP resource,它有自己的垃圾回收和引用計(jì)數(shù)等機(jī)制。
一個zend_object 來聲明我們的PHP類,使得它可以像PHP的類那樣。當(dāng)使用這個PHP類時,實(shí)際上就會調(diào)用我們的C++類去處理了。(需要記住的是:使用時需要像使用其它PHP類那樣動態(tài)的創(chuàng)建它。(譯:使用new))
type 聲明現(xiàn)在處理的對象。在這里,它只有一個:is_myclass。但正如我前邊說過,你可以再增加其它的類。
聯(lián)合類型變量u 保存了指向C++對象實(shí)例的指針。如果你在這個擴(kuò)展中還有其它c(diǎn)lass,那還需要在這再增加其它的指針,如:AnotherClass *anotherclass。
static void php5cpp_object_free_storage(zend_object *object TSRMLS_DC)
{
php5cpp_obj *obj = (php5cpp_obj *) object;
zend_hash_destroy(obj->std.properties);
FREE_HASHTABLE(obj->std.properties);
if (obj->u.myclass) {
zend_list_delete(obj->rsrc_id);
}
efree(obj);
}
static void php5cpp_object_new(zend_class_entry *class_type, zend_object_handlers *handlers, zend_object_value *retval TSRMLS_DC)
{
zval *tmp;
php5cpp_obj *obj = (php5cpp_obj *) emalloc(sizeof(php5cpp_obj));
memset(obj, 0, sizeof(php5cpp_obj));
obj->std.ce = class_type;
ALLOC_HASHTABLE(obj->std.properties);
zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
zend_hash_copy(obj->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
retval->handle = zend_objects_store_put(obj, NULL, php5cpp_object_free_storage, NULL TSRMLS_CC);
retval->handlers = handlers;
}
php5cpp_object_free_storage() 可以看成了對象是 php5cpp_obj 的釋構(gòu)函數(shù),因?yàn)樗龅木褪前袽yClass的對象釋放掉
php5cpp_object_new() 基本上算是一個構(gòu)造函數(shù),它負(fù)責(zé)分配內(nèi)存空間,分配zend_object結(jié)構(gòu),及初始化handler(句柄)等等。它負(fù)責(zé)為擴(kuò)展中所有的類的構(gòu)造,不管是對象MyClass 還是 AnotherClass。
php5cpp_object_new_myclass() 通過調(diào)用 php5cpp_object_new() 來創(chuàng)建一個PHP MyClsss的實(shí)例。如果在擴(kuò)展中有幾個類的話,你要為每個類寫一個類似php5cpp_object_new_*()函數(shù)。
// Register the class entry..
#define PHP5CPP_REGISTER_CLASS(name, obj_name) \
{ \
zend_class_entry ce; \
INIT_CLASS_ENTRY(ce, obj_name, php5cpp_ ## name ##_methods); \
ce.create_object = php5cpp_object_new_ ## name; \
php5cpp_ce_ ## name = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); \
memcpy(&php5cpp_object_handlers_ ## name, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \
php5cpp_object_handlers_ ## name.clone_obj = NULL; \
php5cpp_ce_ ## name->ce_flags |= ZEND_ACC_FINAL_CLASS; \
}
// Register resources. If we're using an object, put it into the object store.
#define PHP5CPP_REGISTER_RESOURCE(obj_type, return_value, res, le) \
{ \
int rsrc_id = ZEND_REGISTER_RESOURCE(object ? NULL : return_value, res, le); \
if (object) { \
php5cpp_obj *obj = (php5cpp_obj *) zend_object_store_get_object(object TSRMLS_CC); \
obj->u.obj_type= res; \
obj->rsrc_id = rsrc_id; \
obj->type = is_ ## obj_type; \
} \
}
#define PHP5CPP_REGISTER_MYCLASS_RESOURCE(return_value, res, le) \
PHP5CPP_REGISTER_RESOURCE(myclass, return_value, res, le)
接著我們要在php5cpp.cpp中加上以上的宏。雖然看起來很相似,但不要把它們弄亂,實(shí)際上PHP5CPP_REGISTER_CLASS() 和PHP5CPP_REGISTER_RESOURCE()的處理是不一樣的。
PHP5CPP_REGISTER_CLASS() 登記一個類的實(shí)際處理的程序。在后邊介紹模塊初始化函數(shù)(PHP_MINIT_FUNCTION())時還會遇到它。
PHP5CPP_REGISTER_RESOURCE() 負(fù)責(zé)在登記一個resource時取得resource得ID。其實(shí)resource就是我們的C++對象的一個實(shí)例。當(dāng)我們處理一個對象時,它會創(chuàng)建一個PHP對象,把實(shí)際的對象保存在PHP的對象貯存器中,然后把resource ID記錄在PHP對象中。
PHP5CPP_REGISTER_MYCLASS_RESOURCE() 只是為了使用 PHP5CPP_REGISTER_RESOURCE()時可以方便一些。
// These are for parsing parameters and getting the actual object from the store.
#define PHP5CPP_GET_THIS() \
zval* object = getThis();
#define PHP5CPP_SET_OBJ(type) \
php5cpp_obj *obj = (php5cpp_obj *) zend_object_store_get_object(object TSRMLS_CC); \
type ## _instance = obj->u.type;
#define PHP5CPP_OBJ_PARAMS(type, params) \
PHP5CPP_GET_THIS(); \
if (object) { \
if (params == FAILURE) { \
RETURN_FALSE; \
} \
PHP5CPP_SET_OBJ(type) \
} \
else
#define PHP5CPP_OBJ_NO_PARAMS(type) \
PHP5CPP_GET_THIS(); \
if (object) { \
if (ZEND_NUM_ARGS() != 0) { \
php_error(E_WARNING, "didn't expect any arguments in %s()", get_active_function_name(TSRMLS_C)); \
} \
PHP5CPP_SET_OBJ(type) \
} \
else
#define PHP5CPP_MYCLASS_OBJ_PARAMS(params) PHP5CPP_OBJ_PARAMS(myclass, params)
#define PHP5CPP_MYCLASS_OBJ_NO_PARAMS() PHP5CPP_OBJ_NO_PARAMS(myclass)
PHP5CPP_GET_THIS() 會返回當(dāng)前使用對象的指針。如果當(dāng)前使用的是一個結(jié)構(gòu),那么getThis() 會返回NULL;如果使用的是一個對象,getThis() 返回一個指向當(dāng)前PHP對象的指針。
PHP5CPP_SET_OBJ() 會從對象貯存器中取得PHP對象實(shí)際使用的C++對象實(shí)例,然后可以用來做其它處理。在使用PHP函數(shù)/方法時,對象會貯存在類似"type ## _instance" 的類型里,如:在我們的例子中是myclass_instance,即MyClass*類型。
PHP5CPP_*_OBJ_PARAMS() 和 PHP5CPP_*_NO_OBJ_PARAMS() 會在調(diào)用我們的函數(shù)/方法時被使用,它們會處理從PHP方傳進(jìn)來的參數(shù)。在封裝函數(shù)和聲明中,可以通過zend_parse_parameters() 去分析這些參數(shù)。
你可以注意到,宏P(guān)HP5CPP_*_PARAMS() 是以 else結(jié)尾的。這樣的話,當(dāng)所處理的不是一個對象時,它會試著用結(jié)構(gòu)化方式去處理。這些宏都可以在下邊找到。
為PHP 5寫的類處理的部分已經(jīng)寫完了,下邊的部分是為PHP 4寫的很簡單易懂的處理。如果我們用的是PHP 4,上邊的那部分代碼在預(yù)處理時會被乎略掉,而只處理下邊的這些代碼。如果我們用的是PHP 5則反之。
#else // End of PHP5-specific macros
// This stuff is for PHP 4. They're empty on purpose, obviously.
#define PHP5CPP_GET_THIS()
#define PHP5CPP_MYCLASS_OBJ_PARAMS(params)
#define PHP5CPP_MYCLASS_OBJ_NO_PARAMS()
#define PHP5CPP_REGISTER_CLASS(name, obj_name)
#define PHP5CPP_REGISTER_MYCLASS_RESOURCE(return_value, res, le) \
ZEND_REGISTER_RESOURCE(return_value, res, le);
#endif // End of PHP4-specific macros
非常的簡單,除了PHP5CPP_REGISTER_MYCLASS_RESOURCE()外其它的宏都是空的。 PHP5CPP_REGISTER_MYCLASS_RESOURCE()仍會注冊一個resource,但不會為對象做任何的檢查。好了,完成這些后,就使得代碼在PHP 4和PHP 5中都可以編譯通過了。
下邊的一些宏在PHP 4和PHP 5中都是一樣的,它們是處理結(jié)構(gòu)和非面向?qū)ο蟠a的。
// These are for both PHP 4 and 5
#define PHP5CPP_MYCLASS_RSRC_PARAMS(params) \
if (params == FAILURE) { \
RETURN_FALSE; \
} \
else { \
ZEND_FETCH_RESOURCE(myclass_instance, MyClass*, &resource, -1, "myclass", le_myclass); \
}
#define PHP5CPP_MYCLASS_RSRC_NO_PARAMS() \
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &resource) == FAILURE) { \
RETURN_FALSE; \
} \
else { \
ZEND_FETCH_RESOURCE(myclass_instance, MyClass*, &resource, -1, "myclass", le_myclass); \
}
static ZEND_RSRC_DTOR_FUNC(destroy_myclass)
{
if (rsrc->ptr) {
delete (MyClass*) rsrc->ptr;
rsrc->ptr = NULL;
}
}
#endif
它們同樣是很簡單的:先試著分析PHP界面?zhèn)鬟M(jìn)來的參數(shù),和取得要處理的resource。
destroy_myclass 負(fù)責(zé)資源回收。當(dāng)觸發(fā)垃圾回收機(jī)制,或destroy 對象/ resource 時,它會把在創(chuàng)建C++對象實(shí)例時分配的內(nèi)存清理/釋放掉。
在php5cpp.cpp里我們會這樣使用這些宏:
PHP5CPP_MYCLASS_OBJ_PARAMS(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s, &len))
PHP5CPP_MYCLASS_RSRC_PARAMS(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &resource, &s, &len))
在PHP 5中,經(jīng)過預(yù)處理后會變成這樣:
zval* object = getThis();
if (object) {
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s, &len) == FAILURE) {
RETURN_FALSE;
}
php5cpp_obj *obj = (php5cpp_obj *) zend_object_store_get_object(object TSRMLS_CC);
myclass_instance = obj->u.myclass;
}
else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &resource, &s, &len) == FAILURE) {
RETURN_FALSE;
}
else {
ZEND_FETCH_RESOURCE(myclass_instance, MyClass*, &resource, -1, "myclass", le_myclass); \
}
可以看到,在面向?qū)ο竽J较?#xff0c;它會試著去取得對象,分析參數(shù)和取得實(shí)際的MyClass對象。如果在非面向?qū)ο蟮哪J较?#xff0c;這段代碼會試著用結(jié)構(gòu)的方式去分析參數(shù),取得resource等等。
在PHP 4中,經(jīng)過預(yù)處理后會變成了下邊這樣:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &resource, &s, &len) == FAILURE) {
RETURN_FALSE;
}
else {
ZEND_FETCH_RESOURCE(myclass_instance, MyClass*, &resource, -1, "myclass", le_myclass); \
}
在這里(PHP 4中),面向?qū)ο蟮拇a會完全給忽略掉,只留下結(jié)構(gòu)化程序的代碼。
為了上邊我們做的有意義,現(xiàn)在我們要做一個最簡單的部分,寫我們的封裝代碼,和使得在PHP中可以使用我們的class。
3 封裝代碼
我們要在php_php5cpp.h中加入一些封裝函數(shù)和方法的定義:
PHP_FUNCTION(myclass_new);
PHP_FUNCTION(myclass_destroy);
PHP_FUNCTION(myclass_set_string);
PHP_FUNCTION(myclass_get_string);
簡單吧。好,那我們在php5cpp.cpp中寫入封裝函數(shù)的執(zhí)行代碼:
PHP_FUNCTION(myclass_new)
{
MyClass *myclass_instance;
char* s;
int len = 0;
PHP5CPP_GET_THIS();
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &s, &len) == FAILURE) {
RETURN_FALSE;
}
if (len) {
myclass_instance = new MyClass(string(s, len));
}
else {
myclass_instance = new MyClass;
}
if (myclass_instance != NULL) {
PHP5CPP_REGISTER_MYCLASS_RESOURCE(return_value, myclass_instance, le_myclass);
}
}
PHP_FUNCTION(myclass_destroy)
{
zval *resource;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &resource) == FAILURE) {
RETURN_FALSE;
}
zend_list_delete(Z_RESVAL_P(resource));
}
PHP_FUNCTION(myclass_set_string)
{
zval *resource;
MyClass *myclass_instance;
int len = -1;
char *s;
PHP5CPP_MYCLASS_OBJ_PARAMS(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s, &len))
PHP5CPP_MYCLASS_RSRC_PARAMS(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &resource, &s, &len))
if (myclass_instance == NULL) {
php_error(E_WARNING, "can't set string on null resource in %s()", get_active_function_name(TSRMLS_C));
RETURN_FALSE;
}
else {
myclass_instance->setString(string(s, len));
RETURN_TRUE;
}
}
PHP_FUNCTION(myclass_get_string)
{
zval *resource;
MyClass *myclass_instance = NULL;
string retval;
PHP5CPP_MYCLASS_OBJ_NO_PARAMS()
PHP5CPP_MYCLASS_RSRC_NO_PARAMS()
if (myclass_instance == NULL) {
php_error(E_WARNING, "can't get string from null resource in %s()", get_active_function_name(TSRMLS_C));
RETURN_FALSE;
}
else {
retval = myclass_instance->getString();
RETVAL_STRINGL((char*) retval.data(), retval.length(), 1);
}
}
這里沒什么太難的地方,所以我不想每個函數(shù)都解釋一次。現(xiàn)在就只簡單看一下myclass_new()和myclass_set_string()做為例子。
在myclass_new()中,首先嘗試取得一個對象。再次說明,只在面向?qū)ο蠓绞较虏艜?#xff0c;在PHP 4下會被預(yù)處理所忽略掉。
因?yàn)镃++類的構(gòu)造函數(shù)有默認(rèn)的參數(shù),所以我們要看看PHP是否有傳一個string類型的參數(shù)過來。分析完傳進(jìn)來的參數(shù)后,就為分配 myclass_instance內(nèi)存空間,然后用PHP5CPP_REGISTER_MYCLASS_RESOURCE把得到的resource貯存起來。在PHP 5的OO模式下,PHP5CPP_REGISTER_MYCLASS_RESOURC會把resource當(dāng)成對象貯存,在PHP 4的結(jié)構(gòu)化模塊下只會簡單的創(chuàng)建一個resource。
接著說myclass_set_string()...
首先,先定義一個zval結(jié)構(gòu)去處理在PHP用結(jié)構(gòu)化接口傳進(jìn)來的參數(shù)。myclass_instance會被用來指向?qū)嶋H的C++對象。len和*s用來保存從PHP方傳來的字串和字串的長度。
在PHP 4下,PHP5CPP_MYCLASS_OBJ_PARAMS()會被預(yù)處理忽略。在PHP 5它會嘗試取得對象,如果獲取對象失敗,否會返回使用結(jié)構(gòu)化接口的步驟。
在取得myclass_instance后,可以像平時使用類那樣使用C++ MyClass類。先調(diào)用MyClass的setString()方法。然后把得到的C++標(biāo)準(zhǔn)string類對象轉(zhuǎn)成類似C方式的char指針,并傳回給PHP方。
最后,要讓PHP知道這個新class,很簡單,在PHP_MINIT_FUNCTION中加入:
PHP5CPP_REGISTER_CLASS(myclass, "MyClass");
le_myclass = zend_register_list_destructors_ex(destroy_myclass, NULL, "myclass", module_number);
好了,到這里代碼已經(jīng)寫完了。:P
第7節(jié).然后...
然后,你可以按自己需要的方式編譯擴(kuò)展模塊。你也許需要增加一些另外的代碼,像using namespace std 等。這個介紹的下邊幾頁會包含一個完整的可運(yùn)行例子。這些代碼我已經(jīng)在gentoo系統(tǒng)上測試過了,包括目前(2004-03-19)在CVS上是新的 PHP 5和PHP_4_3的環(huán)境。(譯:本譯文只提供了可查看代碼的鏈接,沒有包括這些代碼)
第8節(jié).例子源代碼
譯文中沒有包括完整在例子源代碼,你通過下邊的鏈接去查看:
http://bugs.tutorbuddy.com/php5cpp/php5cpp/example_code.html
第9節(jié).授權(quán)許可
本文在GNU Free Documentation License下授權(quán)傳播。以下是許可License的內(nèi)容鏈接...
http://bugs.tutorbuddy.com/php5cpp/php5cpp/license.html
轉(zhuǎn)載于:https://www.cnblogs.com/wudan7/p/4185049.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的[转载]用c写PHP的扩展接口(php5,c++)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java删除文件夹部分内容_java 删
- 下一篇: ajax加php实现三级联动