Создание класса во время выполнения кода а не в MINIT

vGhost

Новичок
Пытаюсь создать класс в процессе выполнения процедуры, но не очень получается

код такой:

Код:
        php_printf("\n\n %s\n %s\n %d \n\n", rVariable->obj_qcn, rVariable->obj_lqcn, strlen(rVariable->obj_qcn));
        // ce_array - это CG(class_table)
        found = zend_hash_find(ce_array, rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, (void **)&fce);
        if (found == FAILURE) {
            INIT_CLASS_ENTRY(e, rVariable->obj_qcn, NULL);
            php_printf("\n\n %s\n \n\n", e.name);
            ce = zend_register_internal_class_ex(&e, c2dw_class_entry, NULL TSRMLS_CC);
            zend_hash_update(ce_array, rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, &ce, sizeof(zend_class_entry *), NULL);
        } else {
            ce = *fce;
        }

        if(object_init_ex(return_value, ce) != SUCCESS) {
            zend_error(E_ERROR, "Error creating %s object instance", ce->name);
        } else {
            add_property_long(return_value, "self", rVariable->obj_id );
            add_property_string(return_value, "class", rVariable->obj_qcn, 1);
            add_property_string(return_value, "name", rVariable->obj_name, 1);
        }
Немного пояснений:
php_printf("\n\n %s\n %s\n %d \n\n", rVariable->obj_qcn, rVariable->obj_lqcn, strlen(rVariable->obj_qcn));
вот что выводит в консоль и что следовательно находится в выше выведенных переменных:
Delphi\Vcl\Forms\TForm
delphi\vcl\forms\tform
22

т.е. с именами всё нормально.

Далее строка:
php_printf("\n\n %s\n \n\n", e.name);
И вот тут происходит не понятная мне магия! Предыдущая инструкция выведет:
Del
Куда делось остальное и почему?
Соответственно объект создаётся таким:
Код:
Del Object
(
    [self] => 12345
    [class] => Delphi\Vcl\Forms\TForm
    [name] => trololo
)
А по завершению работы скрипта ещё сегфолт.


Если класс был создан в MINIT секции, то никаких сегфолтов, а результат выполнения данного кода будет:
Код:
Delphi\Vcl\Forms\TForm Object
(
    [self] => 12345
    [class] => Delphi\Vcl\Forms\TForm
    [name] => trololo
)


Upd:

Если вместо
INIT_CLASS_ENTRY(e, rVariable->obj_qcn, NULL);
захардкодить имя класса вот так:
INIT_CLASS_ENTRY(e, "Delphi\\Vcl\\Forms\\TForm", NULL);
То результат будет такой (но уже без сегфолтов):
Код:
Т Object
(
    [self] => 12345
    [class] => Delphi\Vcl\Forms\TForm
    [name] => trololo
)


Объясните новичку, что не так?
 
Последнее редактирование:

hell0w0rd

Продвинутый новичок
Если вместо
INIT_CLASS_ENTRY(e, rVariable->obj_qcn, NULL);
захардкодить имя класса вот так:
INIT_CLASS_ENTRY(e, "Delphi\\Vcl\\Forms\\TForm", NULL);
То результат будет такой (но уже без сегфолтов):
Во втором случае ты передаешь const char*, а в первом - char*. Возможно проблема в этом. Еще возможно проблема в том, что у rVariable->obj_qcn на конце нет \0.
 

vGhost

Новичок
Во втором случае ты передаешь const char*, а в первом - char*. Возможно проблема в этом.
Проблема что не один из этих вариантов не работает и я не могу понять как правильно надо :(
Даже если я делаю как советуют через макросы (вариант который в области MINIT работает!) то:

Код:
            INIT_CLASS_ENTRY(e, ZEND_NS_NAME("Delphi\\Vcl\\Forms", "TForm"), NULL);

// вот тут честно выводится Delphi\Vcl\Forms\TForm

            php_printf("\n\n %s\n \n\n", e.name);
            ce = zend_register_internal_class_ex(&e, c2dw_class_entry, NULL TSRMLS_CC);
            zend_hash_update(ce_array, rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, &ce, sizeof(zend_class_entry *), NULL);
Но вот объект создаётся вот таким вот:
Код:
к Object
(
    [─э╛ lass] =>
)
Так что я думаю что проблема не тут, а где то где я забыл выделить память или ещё чего, но вот понять что именно и где, я что то не понимаю (не пиннать сильна, пожалуйста, я на си программирую всего 9 дней, а до этого только что это язык такой знал..)

Еще возможно проблема в том, что у rVariable->obj_qcn на конце нет \0.
Была у меня и такая мысль, что он "терялся" при получении из внешней dll (rVariable - структура получаемая из подключаемой dll), делал я и вот такой вариант:

Код:
            int vlen = strlen(rVariable->obj_qcn);
            char *subbuff = malloc(vlen + 1);

            memcpy(subbuff, rVariable->obj_qcn, vlen);
            subbuff[vlen] = '\0';
            php_printf("\n\n %s\n \n\n", subbuff);

            INIT_CLASS_ENTRY(e, subbuff, NULL);
            php_printf("\n\n %s\n \n\n", e.name);
            ce = zend_register_internal_class_ex(&e, c2dw_class_entry, NULL TSRMLS_CC);
            zend_hash_update(ce_array, rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, &ce, sizeof(zend_class_entry *), NULL);
В итоге php_printf от subbuff показывал "Delphi\Vcl\Forms\TForm", а тот что выводит e.name выводил уже "Del"

Так что дело врятли в этом :( Где то я ещё забыл память выделить или ещё чего, но вот где?
 

WMix

герр M:)ller
Партнер клуба
кстати ты очень смело память выделяешь
Код:
// char *subbuff = malloc(vlen + 1);
char *subbuff = (char*)malloc(sizeof(char)*(vlen+1));
 

vGhost

Новичок
кстати ты очень смело память выделяешь
Код:
// char *subbuff = malloc(vlen + 1);
char *subbuff = (char*)malloc(sizeof(char)*(vlen+1));
Вообще я это во всяких примерах часто именно так такое встречал..
Ммм, попутно вопрос, а char разве бывает более одного байта или я чёто не понимаю? Если не сложно объясните в чём разница с тем что у меня или ссылку где об этом рассказывается..


По теме создания классов у меня не большой прогресс, код преобразовался вот в такой вот вариант:
Код:
void process_result(TVariable *rVariable, void ***tsrm_ls, zval *return_value) {
    if (rVariable->error_code > 0) {
        zend_error(rVariable->error_code, rVariable->error_msg, "");
    } else if (rVariable->type == IS_OBJECT) {
        int found;
        zend_class_entry e;
        zend_class_entry* ce;
        zend_class_entry** fce;

        found = zend_hash_find(CG(class_table), rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, (void **)&fce);
        if (found == FAILURE) {
            zend_class_entry **parent = NULL;

            php_printf("\n\n CLASS %s NOT EXISTS, create it! \n\n", rVariable->obj_qcn);

            INIT_CLASS_ENTRY(e, "", NULL);
            e.name = (char*)malloc(sizeof(char)*(strlen(rVariable->obj_qcn)+1));
            memcpy(e.name, rVariable->obj_qcn, strlen(rVariable->obj_qcn) );
            e.name_length = strlen(rVariable->obj_qcn);

            ce = (zend_class_entry*)malloc(sizeof(zend_class_entry));
            *ce = e;
            zend_initialize_class_data(ce, 0 TSRMLS_CC);

            zend_hash_add(CG(class_table), rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, (void **)&ce, sizeof(zend_class_entry *), NULL);
            //zend_hash_update(CG(class_table), rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, (void **)&ce, sizeof(zend_class_entry *), NULL);

            if (
                (FAILURE == zend_lookup_class("Delphi\\C2DelphiWrapper", sizeof("Delphi\\C2DelphiWrapper") - 1, &parent TSRMLS_CC))
                && !parent
            ) {
                zend_error(E_ERROR, "Error set parrent for %s class", ce->name);
            } else {
                zend_do_inheritance(ce, *parent TSRMLS_CC);
            }
        } else {
            php_printf("\n\n CLASS %s FOUND \n\n", rVariable->obj_qcn);
            ce = *fce;
        }

        if(object_init_ex(return_value, ce) != SUCCESS) {
            zend_error(E_ERROR, "Error creating %s object instance", ce->name);
        } else {
            add_property_long(return_value, "self", rVariable->obj_id );
            add_property_string(return_value, "class", rVariable->obj_qcn, 1);
            add_property_string(return_value, "name", rVariable->obj_name, 1);
        }
    } else if (rVariable->type == IS_ARRAY) {
        zend_error(E_ERROR, "not mplemented yet", "");
    } else {
        *return_value = **(rVariable->result_array[0]);
    }
}
Создание происходит нормально, так что часть проблемы таки я решил, когда этот код выполняется несколько раз для одного объекта, первый раз в консоли мы видим :
CLASS Delphi\Vcl\Forms\TForm NOT EXISTS, create it!
А при всех последующих для того же объекта уже:
CLASS Delphi\Vcl\Forms\TForm FOUND
Объекты создаются нормально и всё работает.

Осталось последняя проблема, когда пых завершает работу и приступает к очистке ресурсов, происходит сегфолт. Мне удалось понять что происходит он в момент когда тот пытается удалить элемент из хэш таблицы.
Если я не добавляю свой новый класс в таблицу, закомментировав строку:
zend_hash_add(CG(class_table), rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, (void **)&ce, sizeof(zend_class_entry *), NULL);
То никаких сегфолтов не будет, но и класс будет создаваться для каждого объекта заново (всегда будет выводить CLASS Delphi\Vcl\Forms\TForm NOT EXISTS, create it!). Далее снова магия мне не ясная. По мануалам удаление элемента из хэш таблицы выглядит так:
zend_hash_del(my_table, "my_var", sizeof("my_var"));
но у меня этот код вызывает сегфолт, т.е. допустим я добавил элемент:
zend_hash_add(CG(class_table), rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, (void **)&ce, sizeof(zend_class_entry *), NULL);
Следом вызываю
zend_hash_del(CG(class_table), rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1);
И получаю сегфолт.
Собственно вопрос, как правильно добавлять элемент в хэш таблицу чтоб от его удаления не падало ничё?


Upd:
Вместо вот этой части:
Код:
            ce = (zend_class_entry*)malloc(sizeof(zend_class_entry));
            *ce = e;
            zend_initialize_class_data(ce, 0 TSRMLS_CC);

            //zend_hash_add(CG(class_table), rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, (void **)&ce, sizeof(zend_class_entry *), NULL);
            zend_hash_update(CG(class_table), rVariable->obj_lqcn, strlen(rVariable->obj_lqcn) + 1, (void **)&ce, sizeof(zend_class_entry *), NULL);

            if (
                (FAILURE == zend_lookup_class("Delphi\\C2DelphiWrapper", sizeof("Delphi\\C2DelphiWrapper") - 1, &parent TSRMLS_CC))
                && !parent
            ) {
                zend_error(E_ERROR, "Error set parrent for %s class", ce->name);
            } else {
                zend_do_inheritance(ce, *parent TSRMLS_CC);
            }
можно было бы написать вот так:
Код:
ce = zend_register_internal_class_ex(&e, c2dw_class_entry, NULL TSRMLS_CC);
Но этот вариант обладает той же проблемой с хэш таблицей.
 
Последнее редактирование:

vGhost

Новичок
Нет, про существование типа WideChar я из других ЯП в курсе что бывает такое, я спросил про конкретно данную ситуацию, про тип char который таки исходя из этой статьи всё таки не бывает более одного байта. Так что получается разницы между malloc(sizeof(char)*(vlen+1)); и malloc(vlen+1); по сути никакой нет т.к. sizeof(char) всегда будет 1. Но за статью спасибо, теперь знаю про то как описываются многобайтовые кодировки в сях.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
В общем случае, тут нужно помнить о том, что в Си - sizeof('a') == sizeof(int), а в C++ - sizeof('a') == sizeof(char), но конкретно в этом случае sizeof(char) == 1 прям в стандарте описан.
 

Absinthe

жожо
Вероятно, имелось ввиду следующее:
Код:
char a = 'a'; sizeof (a); // 1
sizeof('a'); // 4
 

vGhost

Новичок
В общем случае, тут нужно помнить о том, что в Си - sizeof('a') == sizeof(int), а в C++ - sizeof('a') == sizeof(char), но конкретно в этом случае sizeof(char) == 1 прям в стандарте описан.
Так, стоп, а вот это мне взорвало мосг. int - 4 байта в 32 битных вариантах и 8 байт в 64 битных. Каким образом sizeof('a') станет == sizeof(int) ??
 

vGhost

Новичок
В С символьная константа = int, в С++ = char
Тогда мне теперь не понятно как вот это работает:
subbuff[vlen] = '\0';
Если я в 1 байт пытаюсь засунуть 4. Тут какое то "магическое" конвертирование случается или он затирает 3 следующих байта после окончания моей строки?
 

флоппик

promotor fidei
Команда форума
Партнер клуба
ну, по логике, он просто отбрасывает потом, т.к. строки терминируются NUL. *задумался*
 

vGhost

Новичок
ну, по логике, он просто отбрасывает потом, т.к. строки терминируются NUL. *задумался*
Тогда не понятна получается. Ну с NULL хрен с ним, там всё нули, а как дела обстоят если скажем: subbuff[vlen-1] = 'a'; ? Ведь если 'a' 4 байта, то я так подозреваю скорей всего это что то типа 0x00000061, отбросит он "потом" что останется? 0x00? Или как всё таки оно работает?
 

vGhost

Новичок
Срабатывает приведение типов.
Чтож, это радует и не радует одновременно :D Радует с той точки зрения что я тут уж начал думать что я там в куче мест буферы переполняю, залезаю на другие данные, ан нет, с этим всё ровно значит, хотя бы тут багов не наделал. Не радует что проблема сегфолта не в этом и её придётся дальше искать :(
 

vGhost

Новичок
В общем я решил проблему, хотя, правильнее наверное сказать подобрал брутфорсом решение, тупо перебирая все возможные варианты. Изменения, как мне кажется, совершенно не значительные, но этот код, на данном этапе, работает, а предыдущие нет. И честно говоря я так и не понял почему. Если у кого то есть на этот счёт объяснения, с радостью выслушаю.
( гуи дифф для удобности просмотра изменений: http://prntscr.com/4ee06w )

Собственно рабочий вариант кода:
Код:
void process_result(TVariable *rVariable, void ***tsrm_ls, zval *return_value) {
    if (rVariable->error_code > 0) {
        zend_error(rVariable->error_code, rVariable->error_msg, "");
    } else if (rVariable->type == IS_OBJECT) {
        int found;
        zend_class_entry e;
        zend_class_entry* ce;
        zend_class_entry** fce;
        char *obj_lqcn;
        int name_length = strlen(rVariable->obj_lqcn);

        obj_lqcn = estrndup(rVariable->obj_lqcn, name_length);

        found = zend_hash_find(CG(class_table), obj_lqcn, name_length, (void **)&fce);
        if (found == FAILURE) {
            zend_class_entry **parent = NULL;
            char *obj_qcn = estrndup(rVariable->obj_qcn, name_length);

            INIT_CLASS_ENTRY(e, obj_qcn, NULL);
            e.name = obj_qcn;
            e.name_length = strlen(obj_qcn);

            ce = (zend_class_entry*)malloc(sizeof(zend_class_entry));
            *ce = e;
            zend_initialize_class_data(ce, 0 TSRMLS_CC);

            zend_hash_add(CG(class_table), obj_lqcn, name_length, (void **)&ce, sizeof(zend_class_entry *), NULL);

            if (
                (FAILURE == zend_lookup_class("Delphi\\C2DelphiWrapper", sizeof("Delphi\\C2DelphiWrapper") - 1, &parent TSRMLS_CC))
                && !parent
            ) {
                zend_error(E_ERROR, "Error set parrent for %s class", ce->name);
            } else {
                zend_do_inheritance(ce, *parent TSRMLS_CC);
            }
        } else {
            ce = *fce;
        }

        if(object_init_ex(return_value, ce) != SUCCESS) {
            zend_error(E_ERROR, "Error creating %s object instance", ce->name);
        } else {
            add_property_long(return_value, "self", rVariable->obj_id );
            add_property_string(return_value, "class", rVariable->obj_qcn, 1);
            add_property_string(return_value, "name", rVariable->obj_name, 1);
        }
    } else if (rVariable->type == IS_ARRAY) {
        zend_error(E_ERROR, "not mplemented yet", "");
    } else {
        *return_value = **(rVariable->result_array[0]);
    }
}
 

fixxxer

К.О.
Партнер клуба
А что за структура TVariable? В каком виде в ней строки? Точно ли null terminated?

Спрашиваю, потому что вижу слово Delphi, а в паскале отродясь не было null-terminated строк.
 

vGhost

Новичок
А как бы он тогда определял длину строки? Ведь из dll от строки приходит только адрес первого символа. Если бы там не было бы \0 он бы получив указатель ушёл бы читать далеко и на долго.
Но по сути вопроса вот:
Код:
  PVariable = ^TVariable;
  TVariable = record
    obj_set_parent: integer;
    obj_id:        integer;
    vtype:          integer;
    error_code:    integer;
    result_count:  integer;
    error_msg:      PAnsiChar;
    obj_qcn:        PAnsiChar;
    obj_lqcn:      PAnsiChar;
    obj_name:      PAnsiChar;
    debug_notice:  PAnsiChar;
    results:        pzval_array;
  end;
Вызываемая из dll процедура возвращает PVariable - указатель на структуру TVariable



Спрашиваю, потому что вижу слово Delphi, а в паскале отродясь не было null-terminated строк.
Ну здрасте приехали! А pChar pAnsiChar pWideChar это какие строки?
 

fixxxer

К.О.
Партнер клуба
А как бы он тогда определял длину строки?
Ну, мало ли где нули завалялись. :) Это было предположение.

Это уже deplhi-специфика, тут я не в теме (ну, теперь уже в теме =) Классически в паскале не было.
 
Сверху