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

Специальные возможности

Один из вопросов, который мы задали себе проектируя библиотеку был — должны ли мы следовать духу PHP или духу C++ в тех возможностях, которые дает библиотека.

В PHP скриптах вы должны использовать магические методы и магические интерфейсы, что бы обогощать возможности создаваемых вами классов и добиваться от них нужного вам в определенных ситуациях поведения. С++ обладает богатыми возможностями перегрузки функций и операторов, которые позволяют добиться аналогичных результатов. К примеру, PHP-метод __invoke() — суть перегрузка оператора operator () в С++. Вопрос который мы задавали себе состоял в том, должны ли мы автоматически подключать метод __invoke() как только обнаружим перегруженный оператор operator () или же давть пользователю возможность явно определить в своем классе метод __invoke(). То же односится и к другим операторам таким как int(), double(), operator== () и т.п.

Мы решили следовать PHP конвенции и использовать магические методы и магические интерфейсы в C++. Хотя надо признать, что код с перегруженными операторами выглядел бы куда более эллегантно и привычно чем код классов содержащих методы начинающиеся с двух символов подчеркивания __ в открытом интерфейсе. На это у нас было несколько причин. Во-первых такой подход облегчает портирорование PHP кода в PHPCPP. Во-вторых, такой подход позволяет начать использовать библиотеку даже начинающим C++ программистам. И, наконец, не все магические методы могут быть выражены в терминах перегрузки операторов и функций (например __isset()). По этому нам пришлось бы использовать магические методы в любом случае. По этой причине кажется вполне логичным, раз уж мы не можем полностью отказаться от использования методов с двойным подчеркиванием, идти до конца и реализовывать остальные специальные возможности в том же духе.

Кроме магических методов и интерфейсов предоставляемых в распоряжение PHP-программистам, Zend-API имеет дополнительные возможности доступные только при написании расширений. Все эти возможности доступны и при использовании библиотеки PHP-CPP. Это означает, что если вы используете PHP-CPP для написания кода своей программы, вам становятся доступны инструменты, не достижимые при использовании чистого PHP.

Преобразование к скалярным типам

По мимо преобразования объекта к строке с помощью метода __toString(), ядро PHP также поддерживает методы преобразования к другим скалярным типам. По какой то таинственной причине Эти возможности доступны только разработчикам расширений и недоступны PHP-программистам. Это не эксперементальная возможность, т.к. методы преобразования к скалярным типам заложены в Zend ZPI и могут быть использованы при написании любого расширения даже без использования PHP-CPP. Разумеется в нашей библиотеке мы не могли обойти стороной эту возможность.

Что бы максимально соответствовать уже сложившейся парадигме написания PHP кода мы выбрали для методов преобразования к оставшимся скалярным типам наиболее интуитивные имена: __toInteger(), __toFloat() и __toBool().

#include <phpcpp.h>

/**
 *  A sample class, with methods to cast objects to scalars
 */
class MyClass : public Php::Base
{
public:
    /**
     *  C++ constructor and C++ destructpr
     */
    MyClass() {}
    virtual ~MyClass() {}

    /**
     *  Преобразование к строке
     *
     *  Обратите внимание, что в качестве возвращаемого типа сейчас используется const char*,
     *  а не Php::Value. Наличие в классе метода __toString выявляется на этапе компиляции.
     *  И возвращаемый тип не являясь частью сигнатуры метода может быть любым.
     *  Лишь бы он бок быть преоббразован к типу Php::Value.
     *
     *  @return const char *
     */
    const char *__toString()
    {
        return "abcd";
    }
    
    /**
     *  Преобразование к целому
     *  @return long
     */
    long __toInteger()
    {
        return 1234;
    }
    
    /**
     *  Преобразование к числу с плавоющей точкой
     *  @return double
     */
    double __toFloat()
    {
        return 88.88;
    }
    
    /**
     *  Преобразование к логическому типу
     *  @return bool
     */
    bool __toBool()
    {
        return true;
    }
};

/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        
        // extension object
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows 
        // which methods are accessible
        Php::Class<MyClass> myClass("MyClass");
        
        // add the class to the extension
        myExtension.add(std::move(myClass));
        
        // return the extension
        return myExtension;
    }
}

Наши магические методы преобразования к скалярным типам выявляются автоматически на этапе компиляции. Что бы они работали достаточно просто определить их как методы C++ класса. Как то специально сообщать о них ядру PHP не нужно. Они вызываются автоматически, как только в клиентском коде созданный на основе объявленного класса объект преобразуется к соответствующему скалярному типу. Сказанное иллюстрирует следующий пример:

<?php
$object = new MyClass();

echo (string)$object, PHP_EOL;
echo (int)$object, PHP_EOL;
echo (bool)$object, PHP_EOL;
echo (float)$object, PHP_EOL;
результат:
abcd
1234
88.88
1

Сравнение объектов

Во внутреннем представлении ядра PHP с каждым PHP-объектом может быть связана специальная функция, которая вызывается каждый раз, когда в пользовательском коде сравниваются два объекта. Под сравнением подразумевается сравнение на равенство и порядковые операторы сравнения: ==, !=, <, >, <=, >=. Если вы реализуете в своем C++ классе метод __compare(), то библиотека PHP-CPP автоматически свяжет этот метод с функцией объекта, вызываемой при попытке сравнить объекты. Другими словами, если вам нужен оператор сравнения для вашего PHP-класса реализуете в C++ классе метод __compare().

int __compare(const MyClass &that) const;
Возвращает 0, если сравниваемые объекты равны (оператор == возвращает истину); 1, если оператор > возвращает истину; -1, если оператор < возвращает истину. Следующий пример иллюстрирует сказанное:
#include <phpcpp.h>
/**
 *  A sample class, that shows how objects can be compared
 */
class MyClass : public Php::Base
{
private:
    /**
     *  Internal value of the class
     *  @var    int
     */
    int _value;

public:
    /**
     *  C++ constructor
     */
    MyClass() 
    {
        // start with random value
        _value = rand();
    }
    
    /**
     *  C++ destructor
     */
    virtual ~MyClass() {}

    /**
     *  Cast the object to a string
     *  @return std::string
     */
    std::string __toString()
    {
        return std::to_string(_value);
    }
    
    /**
     *  Compare with a different object
     *  @param  that
     *  @return int
     */
    int __compare(const MyClass &that) const
    {
        return _value - that._value;
    }
};

/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        
        // extension object
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows 
        // which methods are accessible
        Php::Class<MyClass> myClass("MyClass");
        
        // add the class to the extension
        myExtension.add(std::move(myClass));
        
        // return the extension
        return myExtension;
    }
}

<?php
$object1 = new MyClass();
$object2 = new MyClass();
$object3 = new MyClass();

// Сравниваем объекты
if ($object1 < $object2) {
    echo "$object1 меньше, чем $object2\n";
} 
if ($object1 >= $object2) {
    echo "$object1 больше или равен $object2\n";
}
if ($object1 == $object3) {
    echo "$object1 равен $object3\n";
} 
if ($object1 != $object3) {
    echo "$object1 is не равен $object3\n";
}

Результат:

151717746 меньше, чем 1699622247
1699622247 не равен 627198306

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