|
Вы можете вызывать пользовательские функции из ваших модулей, что очень удобно
при реализации callbacks/обратных вызовов; например, для прохода по массиву,
поиска или просто для программ на базе событий.
Пользовательские функции можно вызывать функцией call_user_function_ex(). Необходимы: hash-значение для таблицы функции, доступ к которой вы хотите
получить, указатель на объект (если хотите вызвать метод), имя функции, return-значение, количество аргументов,
массив аргументов и флаг, указывающий, хотите ли вы использовать zval-сепарацию.
ZEND_API int call_user_function_ex(HashTable *function_table, zval *object,
zval *function_name, zval **retval_ptr_ptr,
int param_count, zval **params[],
int no_separation); |
Заметьте, что вы не должны специфицировать и function_table, и object;
нужен только один из них. Если вы хотите вызвать метод, вы должны предоставить
объект, который содержит этот метод, при этом call_user_function() автоматически устанавливает таблицу функции на таблицу функции этого объекта.
Иначе вам необходимо специфицировать только function_table и можно установить object
в NULL. Обычно по умолчанию таблица функции это таблица "корневой" функции, содержащая
вхождения всех функций. Эта таблица функции является частью глобалов
компилятора, и доступ к ней может быть обеспечен макросом CG. Для введения глобалов компилятора в вашу функцию, вызовите однократно макрос
CLS_FETCH. Имя функции специфицируется в zval-контейнере. На первый взгляд это может показаться необычным, но это довольно
логичный шаг, поскольку в большинстве случаев вы будете принимать имена
функций как параметры вызывающих функций в вашем скрипте, которые в свою
очередь также содержаться в zval-контейнерах. Таким образом, вы должны лишь передать ваши аргументы этой
функции. Этот zval обязан иметь тип IS_STRING.
Следующий аргумент это указатель на return-значение. Вы не должны выделять
память для этого контейнера; функция сделает это сама. Однако вы должны
уничтожить этот контейнер (используя zval_dtor()) впоследствии!
Затем идёт целочисленный parameter_count и массив, содержащий все необходимые
параметры. Последний аргумент специфицирует, должна ли функция выполнять zval-сепарацию -
он должен всегда быть установлен в 0. Если он установлен в 1, функция потребляет меньше памяти, но терпит неудачу, если любой из параметров
требует сепарации. Листинг 9.16 и Рисунок 9.11 показывают вызов пользовательской функции. Этот код
вызывает функцию, которая предоставлена как аргумент, и непосредственно
передаёт return-значение этой функции как своё собственное return-значение.
Обратите внимание на использование вызовов конструктора и деструктора в конце -
это может быть и не обязательно здесь (так как они являются раздельными
значениями, и присвоение проходит безопасно), но так 100-процентно надёжнее.
Рисунок 39-1. Листинг 9.16. Вызов пользовательской функции.
zval **function_name;
zval *retval;
if((ZEND_NUM_ARGS() != 1) || (zend_get_parameters_ex(1, &function_name) != SUCCESS))
{
WRONG_PARAM_COUNT;
}
if((*function_name)->type != IS_STRING)
{
zend_error(E_ERROR, "Функция requires string argument");
}
CLS_FETCH();
if(call_user_function_ex(CG(function_table), NULL, *function_name, &retval, 0, NULL, 0) != SUCCESS)
{
zend_error(E_ERROR, "Функция call failed");
}
zend_printf("We have %i as type<br>", retval->type);
*return_value = *retval;
zval_copy_ctor(return_value);
zval_ptr_dtor(); |
<?php
dl("call_userland.so");
function test_function()
{
print("We are in the test function!<br>");
return("hello");
}
$return_value = call_userland("test_function");
print("Return value: \"$return_value\"<br>");
?> |
| |