Zend:如何正确破坏 PHP 7 中的自定义对象?

Posted

技术标签:

【中文标题】Zend:如何正确破坏 PHP 7 中的自定义对象?【英文标题】:Zend: How to correctly destruct a custom object in PHP 7? 【发布时间】:2016-04-03 17:28:35 【问题描述】:

我正在学习 php 扩展编写,以便使一些旧扩展能够与 PHP 7 一起使用。

我尝试从http://devzone.zend.com/1435/wrapping-c-classes-in-a-php-extension/ 修改示例扩展,但在破坏自定义对象时它一直导致段错误。所有其他功能正常工作。 (在我的代码中,Car 被替换为 BDict。)

这是我的代码:

#define Z_BDICT_OBJ_P(zv) php_bdict_object_fetch_object(Z_OBJ_P(zv))

zend_object_handlers bdict_object_handlers;

typedef struct _bdict_object 
    BDict *bdict_data;
    zend_object std;
 bdict_object;

zend_class_entry *bdict_ce;

static void bdict_free_storage(zend_object *object TSRMLS_DC)

    bdict_object *intern = (bdict_object *)object;

    // ***Both the following two lines will cause segfault***
    delete intern->bdict_data;
    zend_object_std_dtor(&intern->std TSRMLS_CC);


zend_object * bdict_object_new(zend_class_entry *ce TSRMLS_DC)

    bdict_object *intern = (bdict_object *)ecalloc(1,
            sizeof(bdict_object) +
            zend_object_properties_size(ce));

    zend_object_std_init(&intern->std, ce TSRMLS_CC);
    object_properties_init(&intern->std, ce);

    intern->std.handlers = &bdict_object_handlers;

    return &intern->std;


static inline bdict_object * php_bdict_object_fetch_object(zend_object *obj)

    return (bdict_object *)((char *)obj - XtOffsetOf(bdict_object, std));


PHP_METHOD(BDict, __construct)

    long maxGear;
    BDict *bdict = NULL;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &maxGear) == FAILURE) 
        RETURN_NULL();
    

    bdict = new BDict(maxGear);
    bdict_object *intern = Z_BDICT_OBJ_P(getThis());
    intern->bdict_data = bdict;


PHP_MINIT_FUNCTION(bencode)

    zend_class_entry ce;
    INIT_CLASS_ENTRY(ce, "BDict", bdict_methods);
    bdict_ce = zend_register_internal_class(&ce TSRMLS_CC);
    bdict_ce->create_object = bdict_object_new;

    memcpy(&bdict_object_handlers,
            zend_get_std_object_handlers(), sizeof(zend_object_handlers));

    bdict_object_handlers.offset = XtOffsetOf(bdict_object, std);
    bdict_object_handlers.free_obj = bdict_free_storage;

    return SUCCESS;

通过更改这两行的顺序并执行$dict = new BDict(10); unset($dict);,我设法得到了它们的错误信息。

/***** delete intern->bdict_data; *****/
Starting program: /opt/php-7.0.1/bin/php test.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0xc002180800000001) at malloc.c:2933

/***** zend_object_std_dtor(&intern->std TSRMLS_CC); *****/
Starting program: /opt/php-7.0.1/bin/php test.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x0000000000a45890 in zend_object_std_dtor (object=0x7ffff4001c90) at /home/frederick/php-7.0.1/Zend/zend_objects.c:59
59                      if (EXPECTED(!(GC_FLAGS(object->properties) & IS_ARRAY_IMMUTABLE))) 

我是 PHP 扩展的新手,现在我真的很困惑。任何帮助将不胜感激,谢谢。

更新

我注意到在bdict_free_storage() 中转换后对象实际上发生了变化,这显然是错误的,但我不知道如何修复它。

这是调试日志。可以看到bdict_free_storage()internobject的数据完全不同,BDict的地址是错误的。

(gdb) b bencode.cc:20   // bdict_free_storage():            bdict_object *intern = (bdict_object *)object;
Breakpoint 1 at 0x7ffff36de608: file /home/frederick/php-7.0.1/ext/php-bencode/bencode.cc, line 20.
(gdb) b bencode.cc:54   // PHP_METHOD(BDict, __construct):  intern->bdict_data = bdict;
Breakpoint 2 at 0x7ffff36de77e: file /home/frederick/php-7.0.1/ext/php-bencode/bencode.cc, line 54.
(gdb) r
Starting program: /opt/php-7.0.1/bin/php test.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 2, zim_BDict___construct (execute_data=0x7ffff40140d0, return_value=0x7ffff40140b0) at /home/frederick/php-7.0.1/ext/php-bencode/bencode.cc:54
54          intern->bdict_data = bdict;
(gdb) n
55      
(gdb) p intern->bdict_data
$1 = (BDict *) 0x150c530
(gdb) c
Continuing.

Breakpoint 1, bdict_free_storage (object=0x7ffff4001c88) at /home/frederick/php-7.0.1/ext/php-bencode/bencode.cc:20
20          bdict_object *intern = (bdict_object *)object;
(gdb) n
21          zend_object_std_dtor(&intern->std TSRMLS_CC);
(gdb) p *object     // type = 8 means IS_OBJECT
$2 = gc = refcount = 1, u = v = type = 8 '\b', flags = 24 '\030', gc_info = 49154, type_info = 3221362696, handle = 1, ce = 0x14fe6e0,
  handlers = 0x7ffff38e0300 <bdict_object_handlers>, properties = 0x0, properties_table = value = lval = 48, dval = 2.3715151000379834e-322, counted = 0x30, str = 0x30, arr = 0x30,
        obj = 0x30, res = 0x30, ref = 0x30, ast = 0x30, zv = 0x30, ptr = 0x30, ce = 0x30, func = 0x30, ww = w1 = 48, w2 = 0, u1 = v = type = 232 '\350', type_flags = 237 '\355',
          const_flags = 109 'm', reserved = 243 '\363', type_info = 4084067816, u2 = var_flags = 32767, next = 32767, cache_slot = 32767, lineno = 32767, num_args = 32767,
        fe_pos = 32767, fe_iter_idx = 32767
(gdb) p *intern     // type = 0 means IS_UNDEF, all other data are also different
$3 = bdict_data = 0xc002180800000001, std = gc = refcount = 1, u = v = type = 0 '\000', flags = 0 '\000', gc_info = 0, type_info = 0, handle = 22013664,
    ce = 0x7ffff38e0300 <bdict_object_handlers>, handlers = 0x0, properties = 0x30, properties_table = value = lval = 140737277455848, dval = 6.9533453880162249e-310,
          counted = 0x7ffff36dede8, str = 0x7ffff36dede8, arr = 0x7ffff36dede8, obj = 0x7ffff36dede8, res = 0x7ffff36dede8, ref = 0x7ffff36dede8, ast = 0x7ffff36dede8, zv = 0x7ffff36dede8,
          ptr = 0x7ffff36dede8, ce = 0x7ffff36dede8, func = 0x7ffff36dede8, ww = w1 = 4084067816, w2 = 32767, u1 = v = type = 0 '\000', type_flags = 131 '\203',
            const_flags = 3 '\003', reserved = 1 '\001', type_info = 17007360, u2 = var_flags = 0, next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0

我发现其他一些人正在使用

static void bdict_free_storage(void *object TSRMLS_DC)

而不是

static void bdict_free_storage(zend_object *object TSRMLS_DC)

我试过了,但它给了我一个编译时错误。

/home/frederick/php-7.0.1/ext/php-bencode/bencode.cc: In function ‘int zm_startup_bencode(int, int)’:
/home/frederick/php-7.0.1/ext/php-bencode/bencode.cc:131:36: error: invalid conversion from ‘void (*)(void*)’ to ‘zend_object_free_obj_t aka void (*)(_zend_object*)’ [-fpermissive]
     bdict_object_handlers.free_obj = bdict_free_storage;

【问题讨论】:

怎么样:$dict = null; unset($dict); 问题不就是你使用(bdict_object *)object而不是php_bdict_object_fetch_object吗? 我还建议在 MINIT 中进行整个 bdict_object_handlers 初始化(您当前在 create_object 中分配了两个成员) @Hackerman 这个可以正常执行没有任何问题,但是为什么呢? @FrederickZhang 你不需要getThis(),只需 php_bdict_object_fetch_object(object)`。 【参考方案1】:

更改“zend_object_handlers bdict_object_handlers;”到“zend_object_handlers bdict_object_handlers = std_object_handlers;”可以解决这个问题,因为你没有初始化结构。

【讨论】:

【参考方案2】:

“bdict_free_storage”中的参数必须通过它的偏移量来达到

你可以试试:

bdict_object *intern = (bdict_object *) ((char *) object - XtOffsetOf(bdict_object, std));

【讨论】:

是的,实际上我很久以前就用相同的解决方案解决了这个问题,但忘了在这里发布。不管怎样,谢谢你!顺便说一句,新代码可以在github.com/Frederick888/php-bencode/blob/master/binit.h#L9中找到

以上是关于Zend:如何正确破坏 PHP 7 中的自定义对象?的主要内容,如果未能解决你的问题,请参考以下文章

自定义表单装饰器中的 Zend 框架类

Zend Framework 1 应用程序中的缓存破坏静态资产

如何在 PHP/Eclipse 中对 foreach 循环中从数组中拉出的自定义对象进行智能感知?

Zend Framework 2 - 如何使用外部库

zend studio 13.5版添加php自定义code templates的方法

Zend 中的 PHP 数据对象