如何从我的扩展将对象返回到 PHP 用户空间?

发布于 2024-09-01 20:06:16 字数 3816 浏览 3 评论 0原文

我有一个 C++ 对象 Graph,其中包含一个名为 cat、类型为 Category 的属性。我在用 C++ 编写的扩展中向 PHP 公开了 Graph 对象。

只要 Graph 的方法返回 boolean 或 long 等原语,我就可以使用 Zend RETURN_*() 宏(例如 RETURN_TRUE(); 或 RETURN_LONG(123); 但我该如何制作

。 图->getCategory();

返回一个 Category 对象供 PHP 代码操作?

我正在关注 http://devzone.zend.com/article/4486,这是我到目前为止的图形代码:

#include "php_getgraph.h"

zend_object_handlers graph_object_handlers;
struct graph_object {
 zend_object std;
 Graph *graph;
};

zend_class_entry *graph_ce;
#define PHP_CLASSNAME "WFGraph"

ZEND_BEGIN_ARG_INFO_EX(php_graph_one_arg, 0, 0, 1)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(php_graph_two_args, 0, 0, 2)
ZEND_END_ARG_INFO()


void graph_free_storage(void *object TSRMLS_DC) 
{
 graph_object *obj = (graph_object*)object;
 delete obj->graph;

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

 efree(obj);
}

zend_object_value graph_create_handler(zend_class_entry *type TSRMLS_DC) 
{
 zval *tmp;
 zend_object_value retval;

 graph_object *obj = (graph_object*)emalloc(sizeof(graph_object));
 memset(obj, 0, sizeof(graph_object));
 obj->std.ce = type;

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

 retval.handle = zend_objects_store_put(obj, NULL, graph_free_storage, NULL TSRMLS_CC);
 retval.handlers = &graph_object_handlers;

 return retval;
}

PHP_METHOD(Graph, __construct)
{
 char *perspectives;
 int perspectives_len;
 Graph *graph = NULL;
 zval *object = getThis();

 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &perspectives, &perspectives_len) == FAILURE) { 
  RETURN_NULL();
 }

 graph = new Graph(perspectives);
 graph_object *obj = (graph_object*)zend_object_store_get_object(object TSRMLS_CC);
 obj->graph = graph;
}
PHP_METHOD(Graph, hasCategory)
{
 long perspectiveId;

 Graph *graph;
 graph_object *obj = (graph_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
 graph = obj->graph;

 if (graph == NULL) {
  RETURN_NULL();
 }

 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perspectiveId) == FAILURE) { 
  RETURN_NULL();
 }

 RETURN_BOOL(graph->hasCategory(perspectiveId));
}
PHP_METHOD(Graph, getCategory)
{
 // what to do here?
 RETURN_TRUE;
}
function_entry php_getgraph_functions[] = {
 PHP_ME(Graph,__construct,NULL,ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)

 PHP_ME(Graph,hasCategory,php_graph_one_arg,ZEND_ACC_PUBLIC) 
 PHP_ME(Graph,getCategory,php_graph_one_arg,ZEND_ACC_PUBLIC) 
 { NULL, NULL, NULL }
};

PHP_MINIT_FUNCTION(getgraph)
{
 zend_class_entry ce;
 INIT_CLASS_ENTRY(ce, PHP_CLASSNAME, php_getgraph_functions);
 graph_ce = zend_register_internal_class(&ce TSRMLS_CC);
 graph_ce->create_object = graph_create_handler;
 memcpy(&graph_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 graph_object_handlers.clone_obj = NULL;
 return SUCCESS;
}

zend_module_entry getgraph_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
 STANDARD_MODULE_HEADER,
#endif
 PHP_GETGRAPH_EXTNAME,
 NULL,                   /* Functions */
 PHP_MINIT(getgraph),
 NULL,                   /* MSHUTDOWN */
 NULL,                   /* RINIT */
 NULL,                   /* RSHUTDOWN */
 NULL,                   /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
 PHP_GETGRAPH_EXTVER,
#endif
 STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_GETGRAPH
 extern "C" {
  ZEND_GET_MODULE(getgraph)
 }
#endif

I have a C++ object, Graph, which contains a property named cat of type Category. I'm exposing the Graph object to PHP in an extension I'm writing in C++.

As long as the Graph's methods return primitives like boolean or long, I can use the Zend RETURN_*() macros (e.g. RETURN_TRUE(); or RETURN_LONG(123);. But how can I make


Graph->getCategory();

return a Category object for the PHP code to manipulate?

I'm following the tutorial over at http://devzone.zend.com/article/4486, and here's the Graph code I have so far:

#include "php_getgraph.h"

zend_object_handlers graph_object_handlers;
struct graph_object {
 zend_object std;
 Graph *graph;
};

zend_class_entry *graph_ce;
#define PHP_CLASSNAME "WFGraph"

ZEND_BEGIN_ARG_INFO_EX(php_graph_one_arg, 0, 0, 1)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(php_graph_two_args, 0, 0, 2)
ZEND_END_ARG_INFO()


void graph_free_storage(void *object TSRMLS_DC) 
{
 graph_object *obj = (graph_object*)object;
 delete obj->graph;

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

 efree(obj);
}

zend_object_value graph_create_handler(zend_class_entry *type TSRMLS_DC) 
{
 zval *tmp;
 zend_object_value retval;

 graph_object *obj = (graph_object*)emalloc(sizeof(graph_object));
 memset(obj, 0, sizeof(graph_object));
 obj->std.ce = type;

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

 retval.handle = zend_objects_store_put(obj, NULL, graph_free_storage, NULL TSRMLS_CC);
 retval.handlers = &graph_object_handlers;

 return retval;
}

PHP_METHOD(Graph, __construct)
{
 char *perspectives;
 int perspectives_len;
 Graph *graph = NULL;
 zval *object = getThis();

 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &perspectives, &perspectives_len) == FAILURE) { 
  RETURN_NULL();
 }

 graph = new Graph(perspectives);
 graph_object *obj = (graph_object*)zend_object_store_get_object(object TSRMLS_CC);
 obj->graph = graph;
}
PHP_METHOD(Graph, hasCategory)
{
 long perspectiveId;

 Graph *graph;
 graph_object *obj = (graph_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
 graph = obj->graph;

 if (graph == NULL) {
  RETURN_NULL();
 }

 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perspectiveId) == FAILURE) { 
  RETURN_NULL();
 }

 RETURN_BOOL(graph->hasCategory(perspectiveId));
}
PHP_METHOD(Graph, getCategory)
{
 // what to do here?
 RETURN_TRUE;
}
function_entry php_getgraph_functions[] = {
 PHP_ME(Graph,__construct,NULL,ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)

 PHP_ME(Graph,hasCategory,php_graph_one_arg,ZEND_ACC_PUBLIC) 
 PHP_ME(Graph,getCategory,php_graph_one_arg,ZEND_ACC_PUBLIC) 
 { NULL, NULL, NULL }
};

PHP_MINIT_FUNCTION(getgraph)
{
 zend_class_entry ce;
 INIT_CLASS_ENTRY(ce, PHP_CLASSNAME, php_getgraph_functions);
 graph_ce = zend_register_internal_class(&ce TSRMLS_CC);
 graph_ce->create_object = graph_create_handler;
 memcpy(&graph_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 graph_object_handlers.clone_obj = NULL;
 return SUCCESS;
}

zend_module_entry getgraph_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
 STANDARD_MODULE_HEADER,
#endif
 PHP_GETGRAPH_EXTNAME,
 NULL,                   /* Functions */
 PHP_MINIT(getgraph),
 NULL,                   /* MSHUTDOWN */
 NULL,                   /* RINIT */
 NULL,                   /* RSHUTDOWN */
 NULL,                   /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
 PHP_GETGRAPH_EXTVER,
#endif
 STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_GETGRAPH
 extern "C" {
  ZEND_GET_MODULE(getgraph)
 }
#endif

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

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

发布评论

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

评论(1

野の 2024-09-08 20:06:16

在您的内部函数中,您只能返回 zval,而不能返回任意 C++ 对象。在您的情况下,您必须将 Category 对象封装在资源或对象中(就像您对 Graph 对象所做的那样)。无论哪种方式,您都无法自动使用 C++ 对象的方法和属性。然后,您必须提供函数或方法(同样,就像您对 Graph 对象所做的那样),然后它们应该调用底层本机方法并将其结果转换为 zval。

编辑:
好的,我假设您已经将 Category 类声明为 PHP 类,其类条目表位于 ce_category 中,并且您具有以下类型:

struct category_object {
    zend_object std;
    Category *categ;
};

那么:

PHP_METHOD(Graph, getCategory)
{
    graph_object *obj = (graph_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
    struct category_object *co;

    //You ought to check whether obj is NULL and maybe throw an exception or call zend_error...
    if (object_init_ex(return_value, ce_category) != SUCCESS) {
        //error handling
    }

    co = (struct category_object *) zend_object_store_get_object(return_value TSRMLS_CC);
    assert (co != NULL); //should not happen; object was just created
    co->categ = retrieve_category_from_graph(obj->graph);

    /* IMPORTANT NOTE: if the Category object is held by the Graph object
     * (that is, it is freed when the Graph object is freed), you should either:
     * - Copy the Category object, so that it is independent.
     * - Increment the refcount of the PHP Graph object with
     *   zend_objects_store_add_ref(_by_handle). In that case, you should also store the
     *   handle of the PHP Graph object so that you can decrease the refcount when the
     *   PHP Category object is destroyed. Alternatively, you can store an IS_OBJECT
     *   zval and indirectly manipulate the object refcount through construction/destruction
     *   of the zval */
}

In your internal functions, you can only return zvals, not arbitrary C++ objects. In your case, you must encapsulate the Category object either in a resource or in an object (like you did for the Graph object). Either way, you cannot automatically use the C++ object's methods and properties. You must then provide functions or methods (again, like you did for the Graph object) that then should call the underlying native methods and convert their results into zvals.

edit:
OK, I assume you've already declare the Category class as a PHP class, its class entry table is in ce_category and you have this type:

struct category_object {
    zend_object std;
    Category *categ;
};

then:

PHP_METHOD(Graph, getCategory)
{
    graph_object *obj = (graph_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
    struct category_object *co;

    //You ought to check whether obj is NULL and maybe throw an exception or call zend_error...
    if (object_init_ex(return_value, ce_category) != SUCCESS) {
        //error handling
    }

    co = (struct category_object *) zend_object_store_get_object(return_value TSRMLS_CC);
    assert (co != NULL); //should not happen; object was just created
    co->categ = retrieve_category_from_graph(obj->graph);

    /* IMPORTANT NOTE: if the Category object is held by the Graph object
     * (that is, it is freed when the Graph object is freed), you should either:
     * - Copy the Category object, so that it is independent.
     * - Increment the refcount of the PHP Graph object with
     *   zend_objects_store_add_ref(_by_handle). In that case, you should also store the
     *   handle of the PHP Graph object so that you can decrease the refcount when the
     *   PHP Category object is destroyed. Alternatively, you can store an IS_OBJECT
     *   zval and indirectly manipulate the object refcount through construction/destruction
     *   of the zval */
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文