var_dump(object)结果“ * RECURSION *”

时间:2018-09-10 10:46:47

标签: php php-internals

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

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本机)编写和编译静态库

.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*,但是...为什么!?

1 个答案:

答案 0 :(得分:0)

第一

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

递归保护已更改2017年10月6日

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开发人员社区提供了令人难以置信的文档化宏地狱