【问题标题】:var_dump(object) results "*RECURSION*"var_dump(object) 结果“*RECURSION*”
【发布时间】:2019-02-14 18:58:42
【问题描述】:

我正在编写带有类定义的简单扩展

extension.h

zend_class_entry * ExampleClass_class;
zend_class_entry * get_ExampleClass_class();

extension.c

#include "php.h"
#include "extension.h"
...
zend_class_entry * get_ExampleClass_class(){
    return ExampleClass_class;
}

.... 
PHP_METHOD(ExampleClass, getInstance){
    ZEND_PARSE_PARAMETERS_START(0, 0)
        Z_PARAM_OPTIONAL
    ZEND_PARSE_PARAMETERS_END();

    RETURN_OBJ(
// ----------- fun objectToZval(obj: PhpObject) = obj.zval  //CPointer<zval>
        example_symbols()->kotlin.root.php.extension.proxy.objectToZval(
            example_symbols()->kotlin.root.exampleclass.getInstance(
// -------   Unused parameter
                  example_symbols()
                    ->kotlin.root.php.extension.proxy.phpObj(
                        ExampleClass_class, getThis()
                      )
// -------   Unused parameter end
              )
           )
   )
}

我还编写和编译了带有逻辑实现的静态库(Kotlin Native)

.def

static inline zval* zend_helper_new_ExampleClass() {
    zval *obj = malloc(sizeof(zval));
    object_init_ex(obj, get_ExampleClass_class());
    return obj;
}

.kt

fun newExampleClass() = zend_helper_new_ExampleClass()!!

//PhpObject is wrapper for two fields CPointer<zend_class_entry> and CPointer<zval>
class PhpObject(val context: CPointer<zend_class_entry>, val zval: PhpMixed) {
    companion object {
        fun fromMixed(zval: PhpMixed) = PhpObject(zval.pointed!!.value.obj!!.pointed!!.ce!!, zval)
    }
....
}

val PhpMixed.phpObject get() = PhpObject.fromMixed(this)

fun getInstance(obj: PhpObject) = newExampleClass().phpObject

最后我运行 PHP 代码

var_dump(ExampleClass::getInstance());

然后收到这个

# /opt/rh/rh-php71/root/usr/bin/php -dextension=`ls ./phpmodule/modules/*.so` -r "var_dump(ExampleClass::getInstance());"
*RECURSION*
#

我哪里弄错了?

UPD

static inline zval* zend_helper_new_{className}() {
    zval *obj = malloc(sizeof(zval));
    object_init_ex(obj, get_{className}_class());
    php_printf("Just created FLAGS %u\n", GC_FLAGS(obj->value.obj));   

    return obj;
}

刚刚创建的对象有GC_FLAGS等于0

*RECURSIVE* 通过代码出现在函数php_var_dump

    case IS_OBJECT:
        if (Z_IS_RECURSIVE_P(struc)) {
            PUTS("*RECURSION*\n");
            return;
        }

宏->宏->宏->天哪!->宏->宏...

Z_IS_RECURSIVE_P(struc) = (GC_FLAGS((*(zval)).value.counted) & GC_PROTECTED)

好的...

php_printf("%d\n", GC_FLAGS((*(obj)).value.counted));

返回0

不能触发*RECURSIVE*,但是……为什么!?

【问题讨论】:

  • 您编译的 PHP 版本是什么?如果是master,那么看看对象的GC_FLAGS()(GC_PROTECTED标志)
  • 是的,所以在该对象上设置了IS_APPLY_COUNT 标志,因此它立即以*RECURSION* 失败。错误在别处;您发布的代码很好。您需要调试它,为什么 GC_FLAGS() 的 3 个最低有效位都是三个集合。
  • @bwoebi 抱歉,忘记从函数中删除zval_dtor(绝望实验)。该标志的实际值为 0(零)
  • 那么,你的班级有_DebugInfo吗?否则我没有太多建议要寻找什么,除了从 php_var_dump() 向后调试它打印*RECURSION*

标签: php php-internals


【解决方案1】:

第一

我使用 PHP 7.1.8 进行编译,但编码基于最新源代码。

递归保护已更改 06.10.2017

7.1.8 的实际var_dump 代码

case IS_OBJECT:
    if (Z_OBJ_APPLY_COUNT_P(struc) > 0) {
        PUTS("*RECURSION*\n");
        return;
    }

不过没关系

第二

RETURN_OBJ(
        example_symbols()->kotlin.root.php.extension.proxy.objectToZval(
            example_symbols()->kotlin.root.exampleclass.getInstance(/*unused*/)
           )
   )

让我们扩展宏RETURN_OBJ (r)

  1. RETURN_OBJ(r)
  2. { RETVAL_OBJ(r); return; }
  3. { ZVAL_OBJ(return_value, r); return; }
  4. .

    { do {                      
        zval *__z = (return_value);                     
        Z_OBJ_P(__z) = (r);                     
        Z_TYPE_INFO_P(__z) = IS_OBJECT_EX;      
    } while (0); return; }
    
  5. .

    { do {                      
        zval *__z = (return_value);                     
        Z_OBJ(*(__z)) = (r);                        
        Z_TYPE_INFO(*(__z)) = (IS_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT));     
    } while (0); return; }
    
  6. .

    { do {                      
        zval *__z = (return_value);
        (*(__z)).value.obj = (r);
        (*(__z)).u1.type_info = (8 | ((1<<0) << 8));
    } while (0); return; }
    

你看到了吗? :)

是的,这个宏必须接收 zend_object 而不是 zval

只需将返回表达式更改为

example_symbols()->kotlin.root.php.extension.proxy.zendObject(
    example_symbols()->kotlin.root.exampleclass.getInstance(/*unused*/)
)

在哪里

fun zendObject(obj: PhpObject) = obj.zval.pointed!!.value.obj!!

宾果游戏!

PS 特别感谢 php 开发者社区提供了令人难以置信的宏地狱文档

【讨论】:

  • TBH 这应该会给你一个带有 gcc 或 clang 的编译器警告(将 zval * 分配给 zend_object *)。我通常不会寻找类型错误,因为我猜编译器会发现它。
  • @bwoebi 不幸的是,编译器没有收到任何警告
猜你喜欢
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 2021-06-11
  • 2018-04-28
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多