var_dump (объект) результаты "* RECURSION *" - PullRequest
0 голосов
/ 10 сентября 2018

Я пишу простое расширение с определением класса

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;
        }

Macro-> macro-> macro-> Oh God! -> macro-> macro ...

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 голосов
/ 11 сентября 2018

Первый

Для компиляции я использовал PHP 7.1.8, но кодирование основано на последних источниках.

Защита рекурсии была изменена 06.10.2017

Фактический var_dump код для 7.1.8

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 за невероятно документированный макро-ад

...