Публикую топик, за который получил инвайт на Хабр =)
Давно подумывал поделиться опытом разработки расширений для PHP, но все время забывал =)
Сейчас, увидев хабратопик об основах создания расширений для PHP в VS2008, решил наконец это сделать.
Поскольку основы были изложены в этом топике, я сразу перейду к более тонким моментам.
Если необходимо вывести текст, вместо стандартной функции printf() следует пользоваться функцией zend_printf(). Если вызвавший нас скрипт запущен для обработки HTTP-запроса, выводимая через zend_printf() информация будет отправлена напрямую клиенту. При запуске же php в режиме standalone — выведет текст на экран.
Поскольку начиная с PHP 5.3.0 передача аргумента функции по ссылке при вызове функции — deprecated, аргумент следует объявлять как передаваемый по ссылке в объявлении функции. В случае, когда функцию мы объявляем в PHP — все просто — достаточно перед именем аргумента поставить амперсанд, например вот так:
Если же функцию мы объявляем в расширении — все несколько сложнее.
Для начала следует описать аргументы функции:
Параметры макроса ZEND_BEGIN_ARG_INFO_EX():
name — Имя структуры, описывающей данный список аргументов. Должно быть уникальным. Например, для функции ReadData() структуру можно назвать arginfo_readdata.
required_num_args — Количество обязательных аргументов функции.
pass_rest_by_reference — Следует ли остальные (необязательные) аргументы передавать по ссылке (0 — нет, 1 — да).
return_reference — Передается ли возвращаемое значение по ссылке.
Параметры макроса ZEND_ARG_INFO():
pass_by_ref — Передавать ли этот аргумент по ссылке.
name — Имя аргумента.
Все аргументы перечисляются в том же порядке, в каком они передаются функции.
Пример объявления той же функции ReadData:
После этого при объявлении функции достаточно указать в качестве второго параметра макроса PHP_FE() название нашей структуры описания аргументов. Например:
zval — это структура, содержащая представление PHP-переменной. В ней может содержаться как скалярное значение (число, строка, и т.п.), так и массив или handle ресурса.
Если мы хотим принять в качестве аргумента функции расширения или вернуть из функции массив — нам придется работать именно с этим zval.
К примеру мы хотим получить в качестве аргумента функции ассоциативный массив и получить из него значения по некоторым индексам.
Для этого следует: объявить указатель на zval; при вызове функции zend_parse_parameters указать тип аргумента «a»; передать ей же указатель на объявленный указатель на zval.
Сразу пример кода:
Продолжим функцию из предыдущего примера, получим из переданного нам массива значения по индексам x и y, после чего сформируем и выведем строку вида «x = ..., y = ...». В данном примере эти значения будут типа long, однако с незначительными переделками можно будет так же получать и другие типы.
В данном примере широко используются макросы Zend.
Z_TYPE_*() возвращает тип, содержащийся в переданном ему zval. Значения, как можно догадаться, имеют вид IS_тип. Например, IS_ARRAY или IS_STRING.
Z_ARRVAL_*() возвращает указатель на структуру HashTable, которая представляет собой внутреннее представление массива PHP.
Z_LVAL_*() возвращает число, содержащееся в переданном zval.
* — может быть P или PP, в зависимости от того, передаем мы указатель на zval или указатель на указатель на zval.
Функция zend_hash_find(zval *) ищет в переданном HashTable указанный индекс.
Остальное должно быть понятно.
Приведенный код разумеется некрасив и неоптимален, он тут исключительно для примера =)
Не знаю, насколько интересна эта тема хабрасообществу, поэтому пока не буду расписывать дальше.
Если тема окажется интересной, буду рад рассказать о других тонкостях создания функций в расширениях.
Буду рад услышать любые замечания по топику и по стилю, все таки это мой первый хабратопик =)
Давно подумывал поделиться опытом разработки расширений для PHP, но все время забывал =)
Сейчас, увидев хабратопик об основах создания расширений для PHP в VS2008, решил наконец это сделать.
Поскольку основы были изложены в этом топике, я сразу перейду к более тонким моментам.
Вывод текста
Если необходимо вывести текст, вместо стандартной функции printf() следует пользоваться функцией zend_printf(). Если вызвавший нас скрипт запущен для обработки HTTP-запроса, выводимая через zend_printf() информация будет отправлена напрямую клиенту. При запуске же php в режиме standalone — выведет текст на экран.
Передача параметров функции по ссылке
Поскольку начиная с PHP 5.3.0 передача аргумента функции по ссылке при вызове функции — deprecated, аргумент следует объявлять как передаваемый по ссылке в объявлении функции. В случае, когда функцию мы объявляем в PHP — все просто — достаточно перед именем аргумента поставить амперсанд, например вот так:
function ReadData($id,&$data)
Если же функцию мы объявляем в расширении — все несколько сложнее.
Для начала следует описать аргументы функции:
ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)
ZEND_ARG_INFO(pass_by_ref, name)
...
ZEND_END_ARG_INFO()
Параметры макроса ZEND_BEGIN_ARG_INFO_EX():
name — Имя структуры, описывающей данный список аргументов. Должно быть уникальным. Например, для функции ReadData() структуру можно назвать arginfo_readdata.
required_num_args — Количество обязательных аргументов функции.
pass_rest_by_reference — Следует ли остальные (необязательные) аргументы передавать по ссылке (0 — нет, 1 — да).
return_reference — Передается ли возвращаемое значение по ссылке.
Параметры макроса ZEND_ARG_INFO():
pass_by_ref — Передавать ли этот аргумент по ссылке.
name — Имя аргумента.
Все аргументы перечисляются в том же порядке, в каком они передаются функции.
Пример объявления той же функции ReadData:
ZEND_BEGIN_ARG_INFO_EX(arginfo_readdata,0,0,2)
ZEND_ARG_INFO(0,id)
ZEND_ARG_INFO(1,data)
ZEND_END_ARG_INFO()
После этого при объявлении функции достаточно указать в качестве второго параметра макроса PHP_FE() название нашей структуры описания аргументов. Например:
PHP_FE(readdata,arginfo_readdata)
Что такое zval и с чем его едят
zval — это структура, содержащая представление PHP-переменной. В ней может содержаться как скалярное значение (число, строка, и т.п.), так и массив или handle ресурса.
Если мы хотим принять в качестве аргумента функции расширения или вернуть из функции массив — нам придется работать именно с этим zval.
Получаем zval
К примеру мы хотим получить в качестве аргумента функции ассоциативный массив и получить из него значения по некоторым индексам.
Для этого следует: объявить указатель на zval; при вызове функции zend_parse_parameters указать тип аргумента «a»; передать ей же указатель на объявленный указатель на zval.
Сразу пример кода:
PHP_FUNCTION(func)
{
zval *arr;
char buf[128];
unsigned long x,y;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr)==FAILURE) {
WRONG_PARAM_COUNT;
}
...
}
* This source code was highlighted with Source Code Highlighter.
Получаем значения из массива
Продолжим функцию из предыдущего примера, получим из переданного нам массива значения по индексам x и y, после чего сформируем и выведем строку вида «x = ..., y = ...». В данном примере эти значения будут типа long, однако с незначительными переделками можно будет так же получать и другие типы.
PHP_FUNCTION(func)
{
zval *arr,**val;
char buf[128];
unsigned long x,y;
HashTable *hash;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr)==FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_P(arr)!=IS_ARRAY) {
zend_error(E_ERROR,"%s(): Supplied argument is not an array.",get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
hash=Z_ARRVAL_P(arr);
if (zend_hash_find(hash,"x",1,(void **)&val)!=SUCCESS) {
zend_error(E_ERROR,"%s(): There is no x index.",get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
if (Z_TYPE_PP(val)!=IS_LONG) {
zend_error(E_ERROR,"%s(): Wrong type of x.",get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
x=Z_LVAL_PP(val);
if (zend_hash_find(hash,"y",1,(void **)&val)!=SUCCESS) {
zend_error(E_ERROR,"%s(): There is no y index.",get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
if (Z_TYPE_PP(val)!=IS_LONG) {
zend_error(E_ERROR,"%s(): Wrong type of y.",get_active_function_name(TSRLM_C));
RETURN_BOOL(0);
}
y=Z_LVAL_PP(val);
zend_printf("x = %u, y = %u\n",x,y);
}
* This source code was highlighted with Source Code Highlighter.
В данном примере широко используются макросы Zend.
Z_TYPE_*() возвращает тип, содержащийся в переданном ему zval. Значения, как можно догадаться, имеют вид IS_тип. Например, IS_ARRAY или IS_STRING.
Z_ARRVAL_*() возвращает указатель на структуру HashTable, которая представляет собой внутреннее представление массива PHP.
Z_LVAL_*() возвращает число, содержащееся в переданном zval.
* — может быть P или PP, в зависимости от того, передаем мы указатель на zval или указатель на указатель на zval.
Функция zend_hash_find(zval *) ищет в переданном HashTable указанный индекс.
Остальное должно быть понятно.
Приведенный код разумеется некрасив и неоптимален, он тут исключительно для примера =)
Заключение
Не знаю, насколько интересна эта тема хабрасообществу, поэтому пока не буду расписывать дальше.
Если тема окажется интересной, буду рад рассказать о других тонкостях создания функций в расширениях.
Буду рад услышать любые замечания по топику и по стилю, все таки это мой первый хабратопик =)