Учебник РНР
Назад Вперёд

Глава 39. Вызов пользовательских функций

Вы можете вызывать пользовательские функции из ваших модулей, что очень удобно при реализации 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>");
?>


НазадОглавление Вперёд
Функции старта и остановаВверх Поддержка файла инициализации