用于C ++的PHP扩展包装器

时间:2010-03-15 06:19:34

标签: php c++ zend-framework wrapper php-extension

我是这个为PHP编写扩展的领域的新手,但是我需要为PHP创建一个C ++包装类。我目前正在使用PHP 5.2.13。我读了这篇文章http://devzone.zend.com/article/4486-Wrapping-C-Classes-in-a-PHP-Extension,这是一篇关于如何将C ++类包装成与PHP Zend进行通信的教程,但它是为linux系统编写的。你们有没有关于如何编写包装类与PHP通信的文章或建议?

2 个答案:

答案 0 :(得分:3)

这正是我最近一直在做的事情。你引用的教程很好(这也是我的出发点)。这是我为包装课程所遵循的基本过程。假设您正在包装名为Myclass的C ++类:

创建 php_myclass.h

#ifndef PHP_MYCLASS_H
#define PHP_MYCLASS_H

extern "C" {
#include "php.h"
}

// Include your C++ class definition
#include "Myclass.h"

// Here is the struct which will represent the PHP version of your Myclass.
// It simply includes a pointer to a Myclass and a zend_object for PHP to 
struct myclass_object {
    zend_object std;
    Myclass *myclass;
};

// Here is whatever your PHP class is going to be called in the userspace (the PHP code)
#define PHP_MYCLASS_CLASSNAME "Myclass"
extern zend_class_entry *myclass_ce;
extern zend_object_handlers myclass_object_handlers;
zend_object_value myclass_create_handler(zend_class_entry *type TSRMLS_DC);

// Later, this will be the array full of your Myclass's method declarations
extern function_entry php_myclass_functions[];

#endif /* PHP_MYCLASS_H */

然后在 php_myclass.cpp 中定义你的php类:

#include "php_myclass.h"

zend_class_entry *myclass_ce;
zend_object_handlers myclass_object_handlers;

// I'm still a newb, but I think this is the function that handles memory management when
// the PHP class is deleted (goes out of scope, script ends, whatever)
void myclass_free_storage(void *object TSRMLS_DC) 
{
    myclass_object *obj = (myclass_object*)object;
    delete obj->myclass;

    zend_hash_destroy(obj->std.properties);
    FREE_HASHTABLE(obj->std.properties);

    efree(obj);
}

// And likewise I believe this handles, as the name implies, mem management
// when your Myclass is instantiated.
zend_object_value myclass_create_handler(zend_class_entry *type TSRMLS_DC) 
{
    zval *tmp;
    zend_object_value retval;

    // make room in memory for a new PHP Myclass object:
    myclass_object *obj = (myclass_object*)emalloc(sizeof(myclass_object));
    // fill that memory with 0s
    memset(obj, 0, sizeof(myclass_object));
    obj->std.ce = type;

    // some magic stuff (no idea)
    ALLOC_HASHTABLE(obj->std.properties);
    zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
    zend_hash_copy(obj->std.properties, &type->default_properties, (copy_ctor_func_t)zval_add_ref, (void*)&tmp, sizeof(zval*));

    // make it so you can get an instance of this object in later code
    retval.handle = zend_objects_store_put(obj, NULL, myclass_free_storage, NULL TSRMLS_CC);
    retval.handlers = &myclass_object_handlers;

    return retval;
}

// First, we define some argument info for methods that take arguments (if we have any)
// This one means, obviously, one argument:
ZEND_BEGIN_ARG_INFO_EX(php_myclass_one_arg, 0, 0, 1)
ZEND_END_ARG_INFO()

// This one two args, etc.
ZEND_BEGIN_ARG_INFO_EX(php_myclass_two_args, 0, 0, 2)
ZEND_END_ARG_INFO()

// Here's where you tell PHP what methods your Myclass PHP class has.

function_entry php_myclass_functions[] = {
    // A special property at the end of this line for the constructor:
    PHP_ME(Myclass,__construct,NULL,ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)

    // Normal methods look like this:
    PHP_ME(Myclass,methodNameNoArgs,NULL,ZEND_ACC_PUBLIC)
    PHP_ME(Myclass,methodName1Arg,php_myclass_one_arg,ZEND_ACC_PUBLIC) 
    PHP_ME(Myclass,methodName2Args,php_myclass_two_args,ZEND_ACC_PUBLIC) 

    // Three magic NULL values, no idea why they have to go here.
    { NULL, NULL, NULL }
};

// And now, define each of those Myclass methods you just instructed PHP
// to expose to the userspace:
PHP_METHOD(Myclass, __construct)
{
    Myclass *myclass = NULL;
    zval *object = getThis();

    // Create an instance of the class you're wrapping
    myclass = new Myclass();

    // Make object (which points to $this for your PHP object instance)
    // an instance of the struct that represents your php class
    myclass_object *obj = (myclass_object*)zend_object_store_get_object(object TSRMLS_CC);

    // Set the internal Myclass of this to the instance of Myclass you just made
    obj->myclass = myclass;

    // Done.
}

PHP_METHOD(Myclass, methodNameNoArgs)
{
    // Get the current instance of your PHP Myclass into myclass:
    Myclass *myclass;
    myclass_object *mo = (myclass_object*)zend_object_store_get_object(getThis() TSRMLS_CC);\
    myclass = mo->myclass;

    if (obj == NULL) {
        // error checking
        RETURN_NULL();
    }

    // Return the value of your myclass method using one of the RETURN_* macros
    // Here we'll pretend this one returns boolean:
    RETURN_BOOL(myclass->methodNameNoArgs());
}

PHP_METHOD(Myclass, methodName1Arg)
{
    // Now, let's pretend your Myclass::methodName1Arg(int) takes an int
    // and returns a std::vector (which you want to be an array)
    long param;

    // Get the current instance of your PHP Myclass into myclass:
    Myclass *myclass;
    myclass_object *mo = (myclass_object*)zend_object_store_get_object(getThis() TSRMLS_CC);\
    myclass = mo->myclass;

    if (obj == NULL) {
        // error checking
        RETURN_NULL();
    }

    // Here's how you parse parameters of your PHP method call.
    // The second parameter is "l" for long int. Read the tutorials online for more
    // on how to use this function.
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", ¶m) == FAILURE) {
        RETURN_NULL();
    }

    // Get the real return value you want to translate for PHP
    std::vector retval = myclass->methodName1Arg(param);

    // Use the magic "return_value" (which is in every method behind-the-scenes)
    // and initialize it to be a PHP array:
    array_init(return_value);

    // Loop through the vector and build the array:
    for (std::vector::iterator i = retval.begin(); i != retval.end(); ++i) {
        add_next_index_long(return_value, *i);
    }

    // done. return_value is always returned for you.
}

PHP_METHOD(Myclass, methodName2Args)
{ 
    // "Left as an exercise for the reader" is coder slang for
    // "I *really* don't feel like typing anymore." :)
}

我希望这个示例代码能够编译,或者至少有所帮助。 :)它有点匆匆从我在这里的实际工作代码拼凑而成,如果查找/替换破坏了某些东西,至少你可能会知道该怎么做。有很多内容遗留下来,请阅读Sara Golemon关于http://devzone.zend.com/article/1021的三部分扩展教程。祝你好运。

答案 1 :(得分:2)

你会发现最好的“文档”是PHP的源代码,是扩展(抱歉)。一旦你做了一件非常重要的事情,你就会发现你必须深入研究资源(特别是Zend引擎的标题)。

说了这些之后,您可能会发现一些资源可以帮助您入门。请参阅these articlesExtending and Embedding PHP by Sara Golemon。另见pecl.php.net/support.php