Указание параметров функции

В PHP есть механизм для принудительного применения типов параметров функции и для принятия параметров как по ссылке, так и по значению. В предыдущих примерах мы еще не использовали этот механизм, и мы оставили его для реализации функций для проверки объекта «Php::Parameters» (который является std::vector объектов Php::Value) и для проверки соответствует ли количество параметров правильному типу.

Тем не менее, метод 'Extension::add ()' принимает третий необязательный параметр, который можно использовать для указания количества поддерживаемых параметров, если параметры передаются по ссылке или по значению, и каков тип параметров :

#include <phpcpp.h>

void example(Php::Parameters &params)
{
}

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        myExtension.add<example>("example", {
            Php::ByVal("a", Php::Type::Numeric),
            Php::ByVal("b", "ExampleClass"),
            Php::ByVal("c", "OtherClass")
        });
        return myExtension;
    }
}

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

Можете ли вы действительно применять скалярные параметры?

Вы можете быть удивлены, увидев, что мы указали первый параметр типа Numeric. В конце концов, в PHP нет официального способа принудительного применения типа скалярного параметра. Когда вы пишете функцию в PHP, можно обеспечить, чтобы функция получала объект или массив, но не то, что вы хотите получить строку или целое число.

<?php
// пример того, как вы можете обеспечить, чтобы функция могла быть вызвана только с объектом
function example1(MyClass $param)
{
    ...
}

// другой пример для принудительного применения параметра массива
function example2(array $param)
{
    ...
}

// это невозможно (пока?) возможно в PHP
function example3(int $param)
{
    ...
}

То же самое верно для встроенных функций. Хотя основной PHP-движок и PHP-CPP-библиотека дают возможность указать, что ваша функция принимает параметры типа «Php :: Type :: Numeric» или типа «Php :: Type :: String», этот параметр далее полностью игнорируются. Возможно, это изменится в будущем (будем надеяться), но пока имеет смысл указывать только тип параметра для объектов и массивов. Однако мы решили по-прежнему предлагать эту функцию в PHP-CPP, потому что она также предлагается основным движком PHP, так что мы готовы к будущим версиям PHP. Но пока спецификация числового параметра в нашем примере не имеет смысла.

Чтобы вернуться к нашему примеру, теперь из пользовательского пространства PHP можно выполнить следующие вызовы функций:

<?php
// правильный вызов, параметры являются числовыми и два объекта правильного типа
example(12, new ExampleClass(), new OtherClass());

// также действительный, первый параметр не является числовым, а массивом,
// но движок Zend не проверяет это, даже если он указан
example(array(1,2,3), new ExampleClass(), new OtherClass());

// неверное, неправильное количество параметров
example(12, new ExampleClass());

// неверно, неправильные объекты
example(12, new DateTime(), new DateTime());

// неверно, «x» и «z» не являются объектами
example("x", "y", "z");

Механизм PHP вызовет ошибку, если ваша функция вызывается с неправильными параметрами и не будет выполнять фактический вызов функции native.

Описание класса Php::ByVal

Класс Php::ByVal, который мы показали, может быть построен двумя способами. Давайте посмотрим на первый конструктор из заголовочного файла C ++:

/**
 *  Конструктор
 *  @param  name        Имя параметра
 *  @param  type        Тип параметра
 *  @param  required    Требуется ли этот параметр?
 */
ByVal(const char *name, Php::Type type, bool required = true);

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

Более интересен параметр Php::Type. Поддерживаются следующие типы:

Php::Type::Null
Php::Type::Numeric
Php::Type::Float
Php::Type::Bool
Php::Type::Array
Php::Type::Object
Php::Type::String
Php::Type::Resource
Php::Type::Constant
Php::Type::ConstantArray
Php::Type::Callable

На практике это имеет значение только в том случае, если вы указываете тип Php::Type::Array или Php::Type::Object как тип, потому что все остальные типы не применяются базовым механизмом PHP.

Окончательный параметр (помните, что он был вызван «required»?) Можно использовать, чтобы установить, является ли параметр необязательным или нет. Если вы установите значение true, PHP вызовет ошибку, когда ваша функция будет вызвана без этого параметра. Этот параметр работает только для конечных параметров, поскольку (конечно) невозможно пометить первый параметр как необязательный и все последующие параметры по мере необходимости.

Если вы пишете функцию, которая принимает объект как параметр, вы можете использовать вторую форму конструктора Php::ByVal:

/**
 *  Конструктор
 *  @param  name        Имя параметра
 *  @param  classname   Название класса
 *  @param  nullable    Может ли оно быть нулевым?
 *  @param  required    Требуется ли этот параметр?
 */
ByVal(const char *name, const char *classname, bool nullable = false, bool required = true);

Этот альтернативный конструктор также имеет параметр «name» и «required», но параметр Php::Type, который был доступен до этого, теперь заменяется параметром «classname» и «nullable». Этот конструктор может использоваться для функций, которые принимают объект определенного типа в качестве параметра. Например, взгляните на следующий код в PHP:

<?php
function example1(DateTime $time) { ... }
function example1(DateTime $time = null) { Php::Value time = params[0]; ... }

Это будет идентично следующему коду в C ++:

#include <phpcpp.h>;

void example1(Php::Parameters &params) { Php::Value time = params[0]; ... }
void example2(Php::Parameters &params) { Php::Value time = params[0]; ... }

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        myExtension.add<example1>("example1", { Php::ByVal("time", "DateTime", false); });
        myExtension.add<example2>("example2", { Php::ByVal("time", "DateTime", true); });
        return myExtension;
    }
}

Параметры по ссылке

По имени класса Php::ByVal вы, возможно, пришли к выводу, что также должен быть класс Php::ByRef, и вы не могли бы быть более правым. Существует действительно класс Php::ByRef. Если вы создаете функцию, которая принимает параметр по ссылке (и которая может таким образом возвращать значение через параметр), вы также можете указать это.

Класс Php::ByRef имеет точно такую ​​же подпись, что и класс Php::ByVal, и может использоваться точно так же. Разница заключается в том, что ByRef также проверяет, пытается ли кто-либо вызвать вашу функцию с литералом вместо переменной - что невозможно для переменных по ссылке. Если это произойдет, произойдет сбой, и функция не будет вызвана. Давайте покажем пример. Следующее расширение создает функцию, которая меняет содержимое двух переменных.

#include <phpcpp.h>

void swap(Php::Parameters &params)
{
    Php::Value temp = params[0];
    params[0] = params[1];
    params[1] = temp;
}

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        myExtension.add<swap>("swap", {
            Php::ByRef("a", Php::Type::Numeric),
            Php::ByRef("b", Php::Type::Numeric)
        });
        return myExtension;
    }
}

И давайте посмотрим, как эта функция теперь может быть вызвана:

<?php
// define two variables
$a = 1;
$b = 2;

// swap the variables
swap($a, $b);

// invalid, literals cannot be passed by reference
swap(10,20);
?>

Резюме

При добавлении собственных функций в объект расширения можно указать необязательный третий параметр со списком объектов Php::ByVal и Php::ByRef с именами и типами принимаемых параметров. Внутренне, PHP-движок запускает проверку прямо перед каждым вызовом функции, чтобы убедиться, что переданные параметры совместимы со спецификацией параметра, которую вы дали, и вызовет ошибку, если они не являются.

Указание параметров необязательно. Если вы решите оставить эту спецификацию, вам придется проверить внутри функции, достаточно ли параметров и правильны ли типы.

Следующая.