2

我是 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 通信的文章或建议吗?

4

2 回答 2

3

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

创建php_myclass.h

#ifndef PHP_MYCLASS_H
#define PHP_MYCLASS_H

外部“C”{
#include "php.h"
}

// 包括你的 C++ 类定义
#include "Myclass.h"

// 这是代表​​ Myclass 的 PHP 版本的结构。
// 它只包含一个指向 Myclass 的指针和一个用于 PHP 的 zend_object
结构 myclass_object {
    zend_object 标准;
    我的班级 *我的班级;
};

// 这是您的 PHP 类将在用户空间中调用的任何内容(PHP 代码)
#define PHP_MYCLASS_CLASSNAME "我的班级"
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);

// 稍后,这将是你的 Myclass 的方法声明的数组
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;

// 我还是个新手,但我认为这是处理内存管理时的函数
// PHP 类被删除(超出范围,脚本结束,等等)
void myclass_free_storage(void *object TSRMLS_DC)
{
    myclass_object *obj = (myclass_object*)object;
    删除 obj->myclass;

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

    efree(obj);
}

// 同样,我相信这处理,顾名思义,内存管理
// 当你的 Myclass 被实例化时。
zend_object_value myclass_create_handler(zend_class_entry *type TSRMLS_DC)
{
    zval *tmp;
    zend_object_value retval;

    // 在内存中为新的 PHP Myclass 对象腾出空间:
    myclass_object *obj = (myclass_object*)emalloc(sizeof(myclass_object));
    // 用 0 填充该内存
    memset(obj, 0, sizeof(myclass_object));
    obj->std.ce = 类型;

    // 一些神奇的东西(不知道)
    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, myclass_free_storage, NULL TSRMLS_CC);
    retval.handlers = &myclass_object_handlers;

    返回 retval;
}

// 首先,我们为接受参数的方法定义一些参数信息(如果有的话)
// 这显然意味着一个参数:
ZEND_BEGIN_ARG_INFO_EX(php_myclass_one_arg, 0, 0, 1)
ZEND_END_ARG_INFO()

// 这一个两个参数,等等。
ZEND_BEGIN_ARG_INFO_EX(php_myclass_two_args, 0, 0, 2)
ZEND_END_ARG_INFO()

// 这里告诉 PHP 你的 Myclass PHP 类有哪些方法。

函数入口 php_myclass_functions[] = {
    // 此行末尾的构造函数的特殊属性:
    PHP_ME(Myclass,__construct,NULL,ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)

    // 普通方法如下所示:
    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)

    // 三个神奇的 NULL 值,不知道为什么要放在这里。
    {空,空,空}
};

// 现在,定义您刚刚指示 PHP 的每个 Myclass 方法
// 暴露给用户空间:
PHP_METHOD(Myclass, __construct)
{
    我的班级 *myclass = NULL;
    zval *object = getThis();

    // 创建要包装的类的实例
    我的班级 = 新的我的班级();

    // 创建对象(指向您的 PHP 对象实例的 $this)
    // 代表您的 php 类的结构实例
    myclass_object *obj = (myclass_object*)zend_object_store_get_object(object TSRMLS_CC);

    // 将 this 的内部 Myclass 设置为刚刚创建的 Myclass 的实例
    obj->myclass = myclass;

    // 完毕。
}

PHP_METHOD(Myclass, methodNameNoArgs)
{
    // 将 PHP Myclass 的当前实例放入 myclass:
    我的班级 *我的班级;
    myclass_object *mo = (myclass_object*)zend_object_store_get_object(getThis() TSRMLS_CC);\
    myclass = mo->myclass;

    如果(obj == NULL){
        // 错误检查
        RETURN_NULL();
    }

    // 使用 RETURN_* 宏之一返回 myclass 方法的值
    // 这里我们假设这个返回布尔值:
    RETURN_BOOL(myclass->methodNameNoArgs());
}

PHP_METHOD(Myclass, methodName1Arg)
{
    // 现在,假设你的 Myclass::methodName1Arg(int) 接受一个 int
    // 并返回一个 std::vector (你想成为一个数组)
    长参数;

    // 将 PHP Myclass 的当前实例放入 myclass:
    我的班级 *我的班级;
    myclass_object *mo = (myclass_object*)zend_object_store_get_object(getThis() TSRMLS_CC);\
    myclass = mo->myclass;

    如果(obj == NULL){
        // 错误检查
        RETURN_NULL();
    }

    // 下面是解析 PHP 方法调用的参数的方法。
    // 第二个参数是 "l" 表示 long int。阅读在线教程以获取更多信息
    // 关于如何使用这个函数。
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", ¶m) == FAILURE) {
        RETURN_NULL();
    }

    // 获取你想要为 PHP 翻译的真实返回值
    std::vector retval = myclass->methodName1Arg(param);

    // 使用魔法“return_value”(在每个方法的幕后)
    // 并将其初始化为 PHP 数组:
    array_init(return_value);

    // 遍历向量并构建数组:
    for (std::vector::iterator i = retval.begin(); i != retval.end(); ++i) {
        add_next_index_long(return_value, *i);
    }

    // 完毕。return_value 始终为您返回。
}

PHP_METHOD(Myclass, methodName2Args)
{
    // “留给读者练习”是编码器俚语
    // “我*真的*不想再打字了。” :)
}

我希望这个示例代码可以编译,或者至少有帮助。:) 它是从我这里的真实工作代码中匆忙组合起来的,如果查找/替换破坏了某些东西,至少你可能会知道该怎么做。这里还有很多内容,请阅读 Sara Golemon 在http://devzone.zend.com/article/1021上的三部分扩展教程了解更多信息。祝你好运。

于 2010-05-20T17:15:16.530 回答
2

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

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

于 2010-03-25T15:09:32.273 回答