Русская документация PHP-CPP

Определение параметров функции

Одним из не приятных моментов в php является осутствие статической типизации у параметров функций и методов. Однако ядро php содержит такую возможность. Возможность защиты типов параметров фукций и методов классов заложена и в PHP-CPP. Так же возможно указать будут ли приниматься аргументы функции по ссылке или по значению.

Класс Php::Parameters наследует std::vector. Это означает что вы можете использовать все возможности std::vector для доступа к объектам класса Php::Parameters. В частности, вы можете узнать количиство аргументов, поступивших в функцию:

    void example(Php::Parameters &params)
    {
        Php::out << params.size() << std::endl;
    }
можете итерировать параметры:
    void example(Php::Parameters &params)
    {
        for(auto &it: params) {
            Php::out << it << std::endl;
        }
        
    }
иметь возможность доступа по индексу. И все другие возможности std::vector доступны для экземпляров класса Php::Parameters.

Метод Extension::add() позволяет указать третий необязательный параметр. Мы еще не упомянали о нем в предыдущих примерах. Теперь, рассматривая реализацию класса Php::Parameters мы можем рассказать об этих и других возможностях библиотеки. Третий необязательный параметр метода Extension::add() позволяет указать вам передаются ли аргументы в функцию по ссылке или значению, а так же указать типы принимаемых аргументов:

#include <phpcpp.h>
void sample(Php::Parameters &params)
{
}

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

В приведенном выше, примере мы зарегистрировали c++ функцию sample, которая будет доступна для вызова из php скриптов под именем example. PHP-прототип этой функции предполагает наличие трех аргументов: целочисленный аргумент (Php::Type::Numeric); аргумент, являющийся экземпляром класса (или интерфейса) ExampleClass. Тип третьего аргумента не уточняется.

В момент вызова вашей функции example() ядро PHP проверит соответствие типов переданных аргументов заданному вами прототипу. В случае ошибки, ядро PHP сгенерирует предупреждение и прервет выполнение скрипта. Таким образом, вы можете быть уверены что экземпляр Php::Parameters переданный в вашу c++ функцию sample(Php::Parameters &params) будет содержать переменные в точности тех типов которые вы описали.

Неужели скалярная параметриация функций возможна?

Возможно, рассматривая приведенный выше пример, вы были удевлены тому, что мы определил прототип php-функции используя скалярный целочисленный тип. На самом деле ядро php содержит такую возможность. Когда мы пишем функции на php, мы можем определить прототипы функций используя только нескалярные типы. Такие как классы, интерфейсы или массивы. Но мы не можем в чистом php определить функцию, принимающую только строки или, скажем, логические (bool) значения.

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

// Другой пример функции принимающей только массивы.
function example2(array $param) { /*...*/}

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

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

Посколку PHP-CPP использует вызовы напрямую к ядру PHP, минуя стадию интерпретатора, уже сейчас возможно явно указывать типы параметров (в том числе и скалярные) функций и методов классов.

Вернемся к примеру из начала этой главы и рассмотрим следующие вызовы функции example() определенной выше. Если бы PHP поддерживал статическую типизацию параметров функций для скалярных типов, то протоип функции выглядел бы таким образом:

function example(int $a, ExampleClass $b, $c) {
    //...
}

<?php
// Корректный вызов. Переданные параметры соответствуют прототипу.
example(12, new ExampleClass(), new OtherClass());

// Несоответствие типа первого параметра
example('a', new ExampleClass(), new OtherClass());

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

// Несоответствие типа второго параметра
example(12, new DateTime(), new DateTime());
?>

Если функция вызывается с неверными параметрами, то PHP будет генерировать ошибку, а сам вызов не будет осуществлен.

Объяснение возможностей класса Php::ByVal

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

/**
 *  Constructor
 *  @param  name        Имя параметра
 *  @param  type        Тип параметра
 *  @param  required    Это обязательный параметр?
 */
ByVal(const char *name, Php::Type type, bool required = true);

Первый параметр всегда определяет имя переменной. Это немного странно, потому что PHP, в отличии от многих других языков прогрвммирования, не поддерживает концепцию именованных аргументов. Внутри 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

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

Если вы напишите функцию, которая должна принимает в качестве аргумента объект класса, то вам пригодится второй конструктор C++ класса Php::ByVal:

/**
 *  Constructor
 *  @param  name        Имя параметра
 *  @param  classname   Имя класса
 *  @param  nullable    Может быть NULL?
 *  @param  required    Это обязательный параметр?
 */
ByVal(const char *name, const char *classname, bool nullable = false, bool required = true);

type Этот альтернативный конструктор так же имеет параметры name и required. Но вместо параметра 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, и может использоваться в точности как и Php::ByVal. Разница состоит лишь в том, что в случае ByRef аргументы в php функцию передаются по ссылке. В частности php будет проверять возможность осуществления такой передачи и выдавать ошибки если аргумент не может быть передан в функцию по сслке.

В следующем примере мы рассматриваем простейшее расширение, которое создает функцию, меняющую местами ссвои ааргументы.

#include <phpcpp.h>

void swap(Php::Parameters &params) 
{
    std::swap(params[0], params[1]);
}
    
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
$a = 1;
$b = 2;

// поменяем местами значения переменных
swap($a, $b); // теперь $a == 2, $b == 1

// Ошибка, литералы не могут быть переданы по ссылке
swap(10,20);
?>

Итог

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

Вы так же можете определить функцию в своем расширении не указывая спецификацию ее аргументов. В этом случае следить за каличеством аргументов функции и соответствием типов передаваемых ее параметров должна сама функция.

Установка PHP-CPP Загрузка расширений Ваше первое расширение Вывода и ошибок Функции Параметры Вызов функций и методов Классы и объекты Конструкторы и деструкторы Наследование Магические методы Магические интерфейсы Генерация исключений Специальные возможности Поля классов Работа с переменными ini записи Extension callbacks Пространства имен