PHP扩展C++

这个文档简单介绍了怎么使用C++为PHP编写一个扩展模块。我已经被问及这个问题无数次了,所以我决定为后来者写一个简单的HOWTO。这篇文章只会说明一些要点和关键之处,如果你希望学习C++或PHP,请查阅其它的文档。

顺便,在本文中我们也会看一下在PHP5中怎么把C++类影射到PHP中。文章中的例子可以在PHP4和PHP5的环境下使用,但是有一个小小的问题:扩展的PHP类只能在PHP5中使用。:P

基本上,我们会介绍怎么样为C++类创建一个可以在PHP4和PHP5下使用的结构化接口,及可以在PHP5下使用的对象化接口。

这个HOWTO 是参考了一些实现得很好的PHP5扩展模块后写成的。如:Sqlite和SimpleXML扩展就是教导大家怎样在PHP5中扩展类的很好例子。但我的那 些作品除外,只有cryptopp-php使用了很多特性。(还没有发布的PHP5将可以支持cryptopp-php 0.0.14。)

注意:PHP5目前仍在预发布状态,它可能还会做一些改动,尽管文档中的代码我已经在最新CVS版本的PHP 5环境下测试过,但在你读到这篇文章的时候可能还要做一些改动才能正常工作。如果文档中的代码在最新PHP 5中不能使用,请知会我,我会做相应的修正。

  1节. 开始之前

开始前,我要说明:这篇文章所描述的主要是在UNIX的PHP环境上的。当然,我会提及一些在Windows上的开发。但是我大部分的编码都是在UNIX系统上的,所以我会更多的介绍一下我所了解的那一部分。

另外一点我要说明的是:文中所介绍的方法在PHP 4.3.x和PHP 5下都是可行的。尽管我们在开始的时候会基于PHP5来介绍,但是你会发现这些方法在PHP 4.3.x中也是可行的。

我在本文中有一些约定...

$PHP_HOME  是指你的PHP源代码的位置,如:你解开的PHP源代码包所放的位置。在我的系统中指的是: /home/jay/setup/php/php-x.x.x .

我们用来做例子的模块叫做 php5cpp .

 

  2节.安装

在你用C++编写PHP扩展前,你先要搭建一个基本的扩展模块的架构。在UNIX下,你可以运行一个在  $PHP_HOME/ext  下叫做 ext_skel  shell脚本。先切换到  $PHP_HOME/ext  目录和执行那个shell脚本,并用  --extname  参数为你的扩展模块命名。

jay@monty ~ $ cd setup/php/php-5.x.x/ext

jay@month ext $ ./ext_skel --extname php5cpp

Windows系统,目前PHP CVS代码中,可以使用位于  $PHP_HOME/ext  的  ext_skel_win32.php  的PHP脚本是。也许它会成为PHP5的一部分及被PHP 4.x的分支包含。但这只是我大胆的猜想,我并不知道会不会实现...

这样,在 $PHP_HOME/ext/php5cpp 下,我们已经有了一个基本的PHP扩展模块架构。唯一的问题是,它是为C搭建的,而不是为C++。

  3节.修改config.m4

现在我们要修改那个扩展模块的 config.m4  文件以支持C++。

你不需要做太多的改动,要做的只是告诉编译PHP的系统,你的模块是使用C++的,而且需要连接C++标准库。下边是一个删去自动生成的注释后, php5cpp  扩展模块的 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  已经变成了  php5cpp.cpp  了。

  4节.编写代码

修改完 config.m4  后,你可以编写代码了。记住把 php5cpp.c  修改成C++文件的名字。根据前边  config.m4 的修改,在这里我们把它改成  php5cpp.cpp .

现在你可以开始编写你的代码了。但是你如果现在编译这个扩展代码的话,可能会生一个so,并且不会产生任何编译错误,但是并不能在PHP中使用。如果你把它静态编译进PHP,则会产生连接错误。这是因为C和C++的变量空间的不一致引起的 (PHP是使用C来编写,你的扩展使用C++来编写) 。

修改的方法就是,你要告诉你的扩展模块,将把一些PHP API函数当成C函数来对待(他们是用C来写的),而不是当成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()  来包起头文件的那些内容,如对  ZEND_GET_MODULE(php5cpp) 那样。但是在引用声明  BEGIN/END_EXTERN_C()  的  zend.h 文件前,可以通过使用 extern "C"  来达到相同的作用。

Windows系统,可以使用Visual C++编译你的扩展模块。这也需要在的扩展模块的头部加上类似的声明:

#ifdef PHP_WIN32

#include

#include

#endif

这样你可以使得你的代码保持跨平台的特性。

  5节.编译扩展模块

现在可以去编译扩展模块了。如果你想把它编译成静态模块(把它做为PHP的一个部分编译进PHP中去),去到PHP的根目录 $PHP_HOME ,删去  configure  文件和运行 buildconf  (译:需要libtool的支持)。

然后用你平常用的参数运行  configure  并加上 --enable-php5cpp  项。运行  make clean make make install  ,并完成其它的一些必要操作,如:重新编译Apache。

如果你想用动态链接库的方式编译扩展模块,到你的模块的目录下,运行 phpize  命令(假设你已经安装了PEAR),它会为你的模块创建一个  configure  脚本。然后运行 configure make  和  make install 。如果你想让你的模块自动加载,你要修改 php.ini  以加载正确的文件。如:加上类似的一行: extension=php5cpp.so 

现在你的PHP扩展模块已经编译好了。试着运行一下在模块目录下自动生成的 php5cpp.php  ,看看是不是一切正常?

PHP 5的类支持很多新的特性。如:权限(protected, public, private),异常,interfaces,等等。在这个简单的介绍中,我们只做最基本的事情:使PHP可以影射到C++的类。这样你可以用PHP中 使用你的类,之后的事情将会变得很简单的。在看下面的介绍之前,你可以参考一下Sqlite, SimpleXML 及 cryptopp-php 模块的代码。

这里介绍一下。我们要用一个C++ 类做为例子,就叫做MyClass吧。在PHP术语中,把它叫做一个resource(资源)。PHP常使用这类的东西,如数据库的连接就是 resource,它也可能是一个指向实际resource的struct(结构)。在C++中,class 实际上是struct的一个近义词(struct默认为public,classe默认为private   --仅这个区别而已)。

在结构化的接口中,我们会用类似以下的PHP代码来使用resources:

 

$m = myclass_new();

myclass_set_string($m, 'hello world!');

var_dump(myclass_get_string($m));

?>

在面向对象式的接口中,一样可以使用PHP resources,不过已经被封装在一个PHP对象中了,如:

 

$m = new MyClass;

$m->setString('hello world!');

var_dump($m->getString());

?>

我们不需要关心被封装的实际的代码做了些什么。当我们有一个叫MyClass的C++类。我们可以把这个C++类当成resources并把C++类里的方法封装成PHP的函数。然后我们也可以把它封装成一个PHP的对象,使得可以像一般的C++那样使用。

在看本文时,记住我们的目的就是:把C++类封装成PHP可以使用的结构化的函数或对象化的类。也许在一开始有些东西会令你迷惑,但读下去后就会慢慢明白的了。中间会有很多的宏定义,但当你看明白后,会觉得所有东西都很清淅很容易了。

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;

}

这只是一个做为例子的类。

然后我们要让构建系统知道和可以编译这些文件。把 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"  中,否则会出现错误。

2   宏、函数及其它

为了让这个模块可以同时在PHP 4和PHP 5使用,我们需要使用一些宏去声明是依赖于PHP 4或是PHP 5的。因为PHP 4和PHP 5的执行文件是不兼容的,所以你需要为这两个PHP的版本分别编译不同的版本。

通常我会把这些宏的声明放在一个单独的文件中。在这个例子里,就放在 objects.h  中吧。

objects.h  中要写一些PHP 5需要的函数,如:

#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;

};

你可以发现,为了保持一致,每个声明都加上了 php5cpp_  前缀。这是习惯上的一种约定。

另外, #if PHP_MAJOR_VERSION == 5 表明在下边的那些宏、函数等都只是在PHP 5下才生效,当我们在PHP 4下编译时它们会被预处理忽略掉。

php5cpp_ce_myclass  是类  MyClass  的入口。  php5cpp_object_handlers_myclass  是类的内部处理handler(句柄)。

php5cpp_myclass_methods[]  把MyClass中的函数影射成可在PHP使用的标准函数。这样我们在PHP中就可以使用  myclass_new myclass_get_string 等来执行这些函数。你会发现这里并没有定义 myclass_destroy 函数,因为在你对一个类实例使用 unset()  时,系统会自动调用它的释构函数的了。

在结构 php5cpp_obj 中的枚举变量  php5cpp_obj_type  声明了对象的类型。如果你想在扩展中再加入一个类,如:  AnotherClass ,你需要再增加一项,如:  is_anotherclass

php5cpp_obj  结构中声明了在PHP中使用的一些基本信息,包括:

一个resource ID: rsrc_id  ,它指向PHP内部的一个记录着C++对象的track的resource(资源)。实际上,我们的类在PHP中的操作是更像是一个PHP resource,它有自己的垃圾回收和引用计数等机制。

一个 zend_object  来声明我们的PHP类,使得它可以像PHP的类那样。当使用这个PHP类时,实际上就会调用我们的C++类去处理了。(需要记住的是:使用时需要像使用其它PHP类那样动态的创建它。(译:使用new))

type  声明现在处理的对象。在这里,它只有一个: is_myclass 。但正如我前边说过,你可以再增加其它的类。

联合类型变量 u  保存了指向C++对象实例的指针。如果你在这个扩展中还有其它class,那还需要在这再增加其它的指针,如: 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  的释构函数,因为它要做的就是把MyClass的对象释放掉

php5cpp_object_new()  基本上算是一个构造函数,它负责分配内存空间,分配 zend_object 结构,及初始化handler(句柄)等等。它负责为扩展中所有的类的构造,不管是对象 MyClass  还是  AnotherClass

php5cpp_object_new_myclass()  通过调用  php5cpp_object_new()  来创建一个PHP MyClsss的实例。如果在扩展中有几个类的话,你要为每个类写一个类似 php5cpp_object_new_*() 函数。

// 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 中加上以上的宏。虽然看起来很相似,但不要把它们弄乱,实际上 PHP5CPP_REGISTER_CLASS()  和 PHP5CPP_REGISTER_RESOURCE() 的处理是不一样的。

PHP5CPP_REGISTER_CLASS()  登记一个类的实际处理的程序。在后边介绍模块初始化函数( PHP_MINIT_FUNCTION() )时还会遇到它。

PHP5CPP_REGISTER_RESOURCE()  负责在登记一个resource时取得resource得ID。其实resource就是我们的C++对象的一个实例。当我们处理一个对象时,它会创建一个PHP对象,把实际的对象保存在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()  会返回当前使用对象的指针。如果当前使用的是一个结构,那么 getThis()  会返回 NULL ;如果使用的是一个对象, getThis()  返回一个指向当前PHP对象的指针。

PHP5CPP_SET_OBJ()  会从对象贮存器中取得PHP对象实际使用的C++对象实例,然后可以用来做其它处理。在使用PHP函数/方法时,对象会贮存在类似" type ## _instance " 的类型里,如:在我们的例子中是 myclass_instance ,即 MyClass* 类型。

PHP5CPP_*_OBJ_PARAMS()  和  PHP5CPP_*_NO_OBJ_PARAMS()  会在调用我们的函数/方法时被使用,它们会处理从PHP方传进来的参数。在封装函数和声明中,可以通过 zend_parse_parameters()  去分析这些参数。

你可以注意到,宏 PHP5CPP_*_PARAMS()  是以  else 结尾的。这样的话,当所处理的不是一个对象时,它会试着用结构化方式去处理。这些宏都可以在下边找到。

PHP 5写的类处理的部分已经写完了,下边的部分是为PHP 4写的很简单易懂的处理。如果我们用的是PHP 4,上边的那部分代码在预处理时会被乎略掉,而只处理下边的这些代码。如果我们用的是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中都是一样的,它们是处理结构和非面向对象代码的。

// 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界面传进来的参数,和取得要处理的resource。

destroy_myclass  负责资源回收。当触发垃圾回收机制,或destroy 对象/ resource 时,它会把在创建C++对象实例时分配的内存清理/释放掉。

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中,经过预处理后会变成这样:

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); /

}

可以看到,在面向对象模式下,它会试着去取得对象,分析参数和取得实际的MyClass对象。如果在非面向对象的模式下,这段代码会试着用结构的方式去分析参数,取得resource等等。

PHP 4中,经过预处理后会变成了下边这样:

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中),面向对象的代码会完全给忽略掉,只留下结构化程序的代码。

为了上边我们做的有意义,现在我们要做一个最简单的部分,写我们的封装代码,和使得在PHP中可以使用我们的class。

3   封装代码

我们要在 php_php5cpp.h 中加入一些封装函数和方法的定义:

PHP_FUNCTION(myclass_new);

PHP_FUNCTION(myclass_destroy);

PHP_FUNCTION(myclass_set_string);

PHP_FUNCTION(myclass_get_string);

简单吧。好,那我们在 php5cpp.cpp 中写入封装函数的执行代码:

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);

     }

}

这里没什么太难的地方,所以我不想每个函数都解释一次。现在就只简单看一下 myclass_new() myclass_set_string() 做为例子。

myclass_new() 中,首先尝试取得一个对象。再次说明,只在面向对象方式下才会生效,在PHP 4下会被预处理所忽略掉。

因为C++类的构造函数有默认的参数,所以我们要看看PHP是否有传一个string类型的参数过来。分析完传进来的参数后,就为分配 myclass_instance 内存空间,然后用 PHP5CPP_REGISTER_MYCLASS_RESOURCE 把得到的resource贮存起来。在PHP 5的OO模式下, PHP5CPP_REGISTER_MYCLASS_RESOURC 会把resource当成对象贮存,在PHP 4的结构化模块下只会简单的创建一个resource。

接着说 myclass_set_string() ...

首先,先定义一个 zval 结构去处理在PHP用结构化接口传进来的参数。 myclass_instance 会被用来指向实际的C++对象。 len *s 用来保存从PHP方传来的字串和字串的长度。

PHP 4下, PHP5CPP_MYCLASS_OBJ_PARAMS() 会被预处理忽略。在PHP 5它会尝试取得对象,如果获取对象失败,否会返回使用结构化接口的步骤。

在取得 myclass_instance 后,可以像平时使用类那样使用C++ MyClass类。先调用MyClass的 setString() 方法。然后把得到的C++标准string类对象转成类似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);

好了,到这里代码已经写完了。:P

  7节.然后...

然后,你可以按自己需要的方式编译扩展模块。你也许需要增加一些另外的代码,像 using namespace std  等。这个介绍的下边几页会包含一个完整的可运行例子。这些代码我已经在gentoo系统上测试过了,包括目前(2004-03-19)在CVS上是新的PHP 5和PHP_4_3的环境。(译:本译文只提供了可查看代码的链接,没有包括这些代码)

  8节.例子源代码

译文中没有包括完整在例子源代码,你通过下边的链接去查看:

http://bugs.tutorbuddy.com/php5cpp/php5cpp/example_code.html

 

  9节.授权许可

本文在GNU Free Documentation License下授权传播。以下是许可License的内容链接...

http://bugs.tutorbuddy.com/php5cpp/php5cpp/license.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值