C++ 的 PHP 扩展包装器

发布于 2024-08-25 07:21:19 字数 347 浏览 10 评论 0原文

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

I am new in this area of writing extension for PHP, however I need to create a wrapper class for C++ to PHP. I am currently using PHP 5.2.13. I read this article http://devzone.zend.com/article/4486-Wrapping-C-Classes-in-a-PHP-Extension, a tutorial on how I could proceed to wrap C++ class to communicate with PHP Zend however it is written to for linux system. Do you guys have any article or advice on how I could proceed to write a wrapper class to communicate with PHP?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

鱼窥荷 2024-09-01 07:21:19

这正是我最近一直在做的事情。您引用的教程是一个很好的教程(这也是我的起点)。这是我包装课程时遵循的基本流程。假设您正在包装名为 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 了解更多信息。祝你好运。

This is exactly what I've been doing lately. The tutorial you referenced is a good one (it was my starting point as well). Here's the basic process I've followed for wrapping my classes. Say you're wrapping your C++ class named Myclass:

Create 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 */

Then define your php class in php_myclass.cpp:

#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." :)
}

I hope this example code compiles, or at least helps. :) It was sort of hastily put together from real working code I have here, and if the find/replaces broke something, at least you might get an idea of what to do. There's plenty that's left out of this, read Sara Golemon's three-part extensions tutorial on http://devzone.zend.com/article/1021 for more. Good luck.

睡美人的小仙女 2024-09-01 07:21:19

您会发现的最好的“文档”是 PHP 的源代码和扩展(抱歉)。您会发现,一旦您在做一些不平凡的事情,就必须深入研究源代码(尤其是 Zend 引擎的标头)。

话虽如此,您可能会发现一些对您入门有用的资源。请参阅这些文章Sara Golemon 扩展和嵌入 PHP。另请参见 pecl.php.net/support.php

The best "documentation" you'll find is the source code of PHP and is extensions (sorry). You'll find you have to dig into the sources (especially the headers of the Zend engine) as soon as you're doing something non-trivial.

Having said this, there are a few resources you might find useful to get you started. See these articles and Extending and Embedding PHP by Sara Golemon. See also pecl.php.net/support.php

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文