C ++ и PHP поддерживают лямбда-функции или анонимные функции (в мире C ++ наиболее часто используется слово лямбда, люди PHP говорят об «анонимных функциях»). С PHP-CPP вы можете передавать эти функции с одного языка на другой. Вы можете вызвать анонимную функцию PHP из вашего кода на C ++, а наоборот - вызвать lambda из C ++ из PHP-скрипта.

Вызов анонимных функций PHP из C ++

Начнем с очень простого примера в PHP. В PHP вы можете создавать анонимные функции и назначать их переменной (или передавать их непосредственно функции).

<?php
// анонимная функция PHP, хранящаяся в переменной $f
$f = function($a, $b) {

    // вернуть сумму параметров
    return $a + $b;
};

// передать функцию другой функции
other_function($f);

// или передать анонимную функцию, не присваивая ее переменной
other_function(function() {

    // вернуть произведение параметров
    return $a * $b;
});

?>

Вышеприведенный код должен быть знаком большинству PHP-программистов. Конечно, «other_function» может быть реализована в пространстве пользователя PHP, но чтобы продемонстрировать, как это сделать с PHP-CPP, мы собираемся построить его с помощью C ++. Подобно всем другим функциям, которые вы видели в предыдущих примерах, такая функция функций C ++ получает в качестве своего параметра объект Php::Parameters, который является std::vector объектов Php::Value.

#include <phpcpp.h>
/**
 *  Собственная функция, вызываемая из PHP
 *
 *  Эта функция получает один параметр, содержащий вызываемую анонимную
 *  функцию PHP.
 *
 *  @param  params      Параметры, передаваемые функции
 */
void other_function(Php::Parameters &params)
{
    // убедиться, что функция действительно вызывалась хотя бы с одним параметром
    if (params.size() == 0) return nullptr;

    // эта функция вызывается из пользовательского пространства PHP и вызывается
    // с анонимной функцией в качестве первого параметра
    Php::Value func = params[0];

    // класс Php:: Value реализовал оператор (), который позволяет нам 
    // использовать объект так же, как если бы это была реальная функция
    Php::Value result = func(3, 4);

    // @todo сделать что-нибудь с результатом
}

/**
 *  Переключиться на контекст C, потому что механизм Zend ожидает,
 *  что get_module) будет иметь сигнатуру функции стиля C
 */
extern "C" {
    /**
     *  Функция запуска, которая автоматически вызывается механизмом Zend 
     *  при запуске PHP, и которая должна возвращать детали расширения
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() 
    {
        // объект расширения
        static Php::Extension extension("my_extension", "1.0");

        // добавить функцию примера, чтобы ее можно было вызвать из PHP-скриптов
        extension.add<other_function>("other_function");

        // вернуть детали расширения
        return extension;
    }
}

Это так просто. Но и наоборот. Представьте, что у нас есть функция в PHP-пространстве кода пользователя, которая принимает функцию обратного вызова. Следующая функция представляет собой простую версию функции PHP array_map ():

<?php
// функция, которая выполняет итерацию по массиву и вызывает функцию
// для каждого элемента этого массива, возвращает новый массив
// с каждым элементом, замененным результатом обратного вызова
function my_array_map($array, $callback) {

    // переменная начального результата
    $result = array();

    // цикл по массиву
    foreach ($array as $index => $item) {

        // вызов обратного вызова по элементу
        $result[$index] = $callback($item);
    }

    // готово
    return $result;
}
?>

Представьте, что мы хотим называть эту функцию PHP из вашего кода на C++, используя функцию лямбда C++ в качестве обратного вызова. Это возможно и легко:

#include <phpcpp.h>
/**
 *  Собственная функция, вызываемая из PHP
 */
void run_test()
{
    // создание анонимной функции
    Php::Function multiply_by_two([](Php::Parameters &params) -> Php::Value {

        // убедитесь, что функция действительно вызывалась хотя бы с одним параметром
        if (params.empty()) return nullptr;

        // в функцию передается один параметр
        Php::Value param = params[0];

        // умножьте параметр на два
        return param * 2;
    });

    // функция теперь вызывается
    Php::Value four = multiply_by_two(2);

    // объект Php::Function - это производное значение Php::Value, и его значение
    // также может быть сохранено в обычном объекте Php::Value, 
    // тогда он все равно будет функцией обратного вызова, тогда
    Php::Value value = multiply_by_two;

    // объект значения теперь также содержит функцию
    Php::Value six = value(3);

    // create an array
    Php::Value array;
    array[0] = 1;
    array[1] = 2;
    array[2] = 3;
    array[3] = 4;

    // вызвать функцию пользовательского пространства
    Php::Value result = Php::call("my_array_map", array, multiply_by_two);

    // @todo что-то делать с переменной результата (которая теперь
    // содержит массив со значениями 2, 4, 6 и 8).
}

/**
 *  Переключиться на C-контекст, потому что движок Zend ожидает,
 *  что get_module () будет иметь подпись функции стиля C
 */
extern "C" {
    /**
     *  Функция запуска, которая автоматически вызывается механизмом Zend
     *  при запуске PHP, и которая должна возвращать детали расширения
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() 
    {
        // объект расширения
        static Php::Extension extension("my_extension", "1.0");

        // добавить функцию примера, чтобы ее можно было вызвать из PHP-скриптов
        extension.add<run_test>("run_test");

        // вернуть детали расширения
        return extension;
    }
}

В примере мы назначили функцию лямбда C++ объекту Php::Function. Класс Php::Function получен из класса Php::Value. Единственная разница между Php::Value и Php::Function заключается в том, что конструктор Php::Function принимает функцию. Несмотря на эту разницу, оба класса полностью идентичны. На самом деле мы предпочли бы, чтобы функции C++ напрямую привязывались к объектам Php::Value и пропускали конструктор Php::Function, но это невозможно из-за вызова неоднозначностей.

Класс Php::Function можно использовать так, как если бы это был обычный объект Php::Value: вы можете назначить его другим объектам Php::Value, и вы можете использовать его в качестве параметра при вызове функций PHP в пространстве пользователя. В приведенном выше примере мы делаем именно это: мы вызываем функцию my_iterate () пользовательского пространства с помощью нашей собственной функции 'multiply_by_two' C ++.

Подпись функции C ++

Вы можете передавать различные типы функций C++ в конструктор Php::Function, если они совместимы со следующими двумя сигнатурами функций:

Php::Value function();
Php::Value function(Php::Parameters &params);

Внутри класса Php::Function используется объект std::function C ++ для хранения функции, поэтому все, что может быть сохранено в таком объекте std :: function, может быть присвоено классу Php::Function.

Далее.