Расширения PHP: несколько интересных особенностей

в 17:34, , рубрики: c++, php, метки: ,

Писать на C++ расширения для PHP приходится не так чтобы очень часто, но когда приходится — обнаруживается, что публикаций и документации на эту тему не так уж и много. Особенно, если копнуть поглубже. Опишу несколько интересных моментов, которые мне пришлось выяснять «на своей шкуре».

Неожиданные сложности подстерегают нас при вызове из кода расширения (которое, напомню, мы пишем на C++) PHP-функций. Для этого предусмотрена функция call_user_function_ex, пример использования которой найти не так уж сложно; проблемы возникают, если вынести ее вызов из C++'ной функции, которая вызывается из PHP. Объявляются такие функции следующим образом:

ZEND_FUNCTION(MyFunction) { … }

После вынесения вызова call_user_function_ex за ее пределы, наше расширение перестает компилироваться.
Разгадка (и решение проблемы) кроются, конечно же, в определении макроса ZEND_FUNCTION, который добавляет к определению функции пару параметров. Нам ничего не остается, кроме как передать их той функции, откуда мы хотим обратиться к PHP. Выглядеть это будет так:

ZEND_FUNCTION(MyFunction) {
...
char *result = myInnerFunction ( CG ( function_table ), tsrm_ls );
...
}

char *myInnerFunction ( HashTable *function_table, void ***tsrm_ls ) { 
...
call_user_function_ex(...);
...
}

Еще одна интересная особенность работы с PHP'шными структурами из C++ связана с тем, какие средства работы с ассоциативными массивами предоставили нам разработчики PHP. В PHP мы привыкли легко оперировать такими массивами, создавать многоэтажные конструкции. В C++ работа с ними превратится в кошмар: конечно, соответствующие методы API предусмотрены, но назвать их слишком удобными не повернется язык. Например, цикл, пробегающий по одному уровню массива, будет выглядеть так:

for (zend_hash_internal_pointer_reset_ex (arr_hash, &pointer); zend_hash_get_current_data_ex (arr_hash, (void**) &data, &pointer ) == SUCCESS; zend_hash_move_forward_ex (arr_hash, &pointer)) { ... }

Если в C++ нужно работать с многоэтажным массивом, то по нему при помощи этих средств, прямо скажем, не набегаешься.
Что можно предложить взамен? Достаточно простой и понятный вариант — развернуть многоэтажный ассоциативный массив в два простых двумерных. Один из них будет содержать «упакованные» ключи, второй — значения. Упаковать ключи можно в строки, где ключи разных уровней будут разделены каким-нибудь символом. Представим себе вот такой массив в PHP:

$a = Array ( "user" => Array ( Array ( "name" => "Ivanov", "age" => 28), Array ( "name" => "Petrov", "age" => 23) ) );

В C++ можно будет быстро находить в нем значения, если упаковать его вот таким образом:

string keys[] = { "user_0_name", "user_0_age", "user_1_name", "user_1_age" };
string values[] = { "Ivanov", "28", "Petrov", "23" };

Автор: SergeIndex

Источник

Поделиться

* - обязательные к заполнению поля