В большинстве распространенных функций в Zephir используются внутренние оптимизаторы. Оптимизатор работает как перехватчик для вызовов функций. Оптимизатор заменяет вызов функции в пользовательском пространстве PHP прямыми Си-вызовами, которые выполняются быстрее и имеют более низкие накладные расходы, повышающие производительность.

Чтобы создать оптимизатор, вам нужно создать класс в каталоге «optimizers», необходимо использовать следующее соглашение (вы можете настроить название этого каталога в файле config.json; см. ниже). Следующие соглашения по именованию являются обязательными:

Функция в ZephirНазвание класса оптимизатораПуть оптимизатораФункция в C
calculate_pi CalculatePiOptimizer optimizers/CalculatePiOptimizer.php my_calculate_pi

Обратите внимание, что оптимизатор написан на PHP, а не на Zephir. Он используется во время компиляции, чтобы программно генерировать соответствующий код C для вашего расширения для вызова. Это оправдано для проверки того, что Аргументы и возвращаемые типы соответствуют тому, что функция C фактически требует, предотвращая zephir от генерации недопустимого кода C.

Это основная структура для ‘optimizer’:

<?php

namespace Zephir\Optimizers\FunctionCall;

use Zephir\Call;
use Zephir\CompilationContext;
use Zephir\Compiler\CompilerException;
use Zephir\Optimizers\OptimizerAbstract;

class CalculatePiOptimizer extends OptimizerAbstract
{

    public function optimize(array $expression, Call $call, CompilationContext $context)
    {
        //...
    }

}

Реализация оптимизаторов в большой степени зависит от типа кода, который вы хотите сгенерировать. В нашем примере мы собираемся заменить вызов этой функции вызовом c-функции. В Zephir код, используемый для вызова этой функции:

let pi = calculate_pi(1000);

Таким образом, оптимизатор будет ожидать только один параметр, мы должны подтвердить это, чтобы избежать проблем позже:

<?php

public function optimize(array $expression, Call $call, CompilationContext $context)
{

    if (!isset($expression['parameters'])) {
        throw new CompilerException("'calculate_pi' требуется один параметр");
    }

    if (count($expression['parameters']) < 2) {
        throw new CompilerException("'calculate_pi' требуется один параметр");
    }

    //...
}

Есть только что вызванные функции и они не возвращают никакого значения, наша функция возвращает значение, которое является вычисленным значением PI. Поэтому мы должны знать, что тип переменной, используемой для получения этого вычисленного значения, - ОК:

<?php

public function optimize(array $expression, Call $call, CompilationContext $context)
{

    if (!isset($expression['parameters'])) {
        throw new CompilerException("'calculate_pi' требуется один параметр");
    }

    if (count($expression['parameters']) < 2) {
        throw new CompilerException("'calculate_pi' требуется один параметр");
    }

    /**
     * Обработка возвращаемого символа
     */
    $call->processExpectedReturn($context);

    $symbolVariable = $call->getSymbolVariable();
    if ($symbolVariable->isNotDouble()) {
        throw new CompilerException("Рассчитанные значения PI могут быть сохранены только в двойных переменных", $expression);
    }

    //...
}

Мы проверяем, будет ли возвращаемое значение храниться в типе переменной ‘double’, если не выбрано исключение компилятора.

Следующее, что нам нужно сделать, это обработать параметры, переданные функции:

<?php

$resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression);

Как хорошая практика в Zephir важна для создания функций, которые не изменяют их параметры, если вы изменяете переданные параметры, Zephir нужно будет распределить память для переданных констант, и вам придется использовать getResolvedParams вместо getReadOnlyResolvedParams.

Код, возвращаемый этими методами, является допустимым C-кодом, который может использоваться в принтере кода для генерации вызова c-функции:

<?php

//Generate the C-code
return new CompiledExpression('double', 'calculate_pi( ' . $resolvedParams[0] . ')', $expression);

Все оптимизаторы должны возвращать экземпляр CompiledExpression, это сообщит компилятору тип, возвращаемый кодом и связанным с ним C-кодом.

Полный код оптимизатора:

<?php

class CalculatePiOptimizer extends OptimizerAbstract
{

    public function optimize(array $expression, Call $call, CompilationContext $context)
    {

        if (!isset($expression['parameters'])) {
            throw new CompilerException("'calculate_pi' требуется один параметр");
        }

        if (count($expression['parameters']) < 2) {
            throw new CompilerException("'calculate_pi' требуется один параметр");
        }

        /**
         * Обработка возвращаемого символа
         */
        $call->processExpectedReturn($context);

        $symbolVariable = $call->getSymbolVariable();
        if ($symbolVariable->isNotDouble()) {
            throw new CompilerException("Рассчитанные значения PI могут быть сохранены только в двойных переменных", $expression);
        }

        $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression);

        return new CompiledExpression('double', 'my_calculate_pi( ' . $resolvedParams[0] . ')', $expression);
    }

}

Код, реализующий функцию "my_calculate_pi", написан на языке C и должен компилироваться вместе с расширением.

Этот код должен быть помещен в каталог ext/, где бы вы ни находились; просто убедитесь, что эти файлы не конфликтуют с файлами, сгенерированными Zephir.

Этот файл должен содержать заголовки Zend Engine и реализацию функции на языке C:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ext.h"

double my_calculate_pi(zval *accuracy) {
    return 0.0;
}

Этот файл необходимо добавить в специальный раздел файла config.json :

"extra-sources": [
    "utils/pi.c"
]

Наконец, вам нужно будет указать, где Zephir может найти ваш оптимизатор, используя опцию конфигурации optimizer-dirs.

"optimizer-dirs": [
    "optimizers"
]

Проверьте полный исходный код этого примера здесь.

Содержание.

Предыдушая  Следующая