Статьи

Разработка расширений PHP с использованием PHP-CPP: объектно-ориентированный код

В моей первой части, посвященной созданию расширения PHP с помощью PHP-CPP , мы рассмотрели некоторые основы использования PHP-CPP для создания нашего первого скелетного расширения PHP и рассмотрели некоторые важные термины.

В этой части мы доработаем его функции ОО. Мы будем имитировать класс комплексных чисел (в форме 3+4i

Мы начнем с изменения пустых файлов проекта, предоставляемых PHP-CPP, и изменим следующие два файла в соответствии с нашей новой библиотекой (в данном случае complex.so

  • Переименуйте yourtextension.inicomplex.iniextension=complex.so
  • Измените несколько строк в Makefile
 NAME                =   complex
INI_DIR             =   /etc/php5/cli/conf.d

Теперь мы переходим к созданию нашего класса Complexmain.cpp

C ++ конструктор и PHP конструктор

В классе C ++ есть конструкторы и деструкторы. Конструкторы отображаются в привычной форме без типа возврата и имени класса в качестве имени функции, с необязательными различными наборами параметров (в их перегруженных версиях), в то время как деструкторы не имеют типа возврата, параметров, совместно используют имя класса с префиксом наклона ( ~

 class Complex
{
    Complex();
    // We can have many other overloaded versions of the constructor 
    ~Complex();
} 

Мы также знаем, что в PHP у нас есть несколько «магических» методов, в частности, __construct()__destruct()

PHP-CPP поддерживает этот вид PHP-подобного магического метода для конструкторов / деструкторов. Это делает нашу реализацию расширения PHP с классом проще.

( Официальная документация PHP-CPP содержит подробные объяснения различий между этими двумя «конструкторами / деструкторами». Пожалуйста, прочтите это и другие связанные документы для более подробной информации.)

Моя личная точка зрения заключается в том, что, поскольку мы в конечном итоге будем использовать класс (написанный на C ++) в сценарии PHP, мы должны реализовать в нашем классе и конструкторы / деструкторы C ++, и магические конструкторы / деструкторы, подобные PHP. Фрагмент кода показан ниже:

 class Complex : public Php::Base {
private:
    double r = 0, i = 0;

public:
    /**
     *  C++ constructor and destructor
     */
    Complex() {
    }

    virtual ~Complex() {
    }

    Php::Value getReal() {
        return r;
    }

    Php::Value getImage() {
        return i;
    }

    void __construct(Php::Parameters &params) {
        if (params.size() == 2) {
            r = params[0];
            i = params[1];
        } else {
            r=0;
            i=0;
        }
    }

В этом сегменте кода некоторые вещи требуют дальнейшей проработки:

  1. Любой класс, который объявлен в нашей библиотеке, ДОЛЖЕН наследоваться от Php::Base Это требование PHP-CPP. Php::Base (Заголовочный файл для класса Php::Basebase.h
  2. Мы объявили как конструктор / деструктор C ++, так и PHP-подобный магический конструктор. Последний также параметризован, так что мы можем установить вещественную часть / часть изображения комплексного числа с помощью параметров, переданных из PHP во время создания объекта.
  3. Поскольку действительная часть / часть изображения комплексного числа, определенного в нашем классе, является privategetReal()getImage() PHP-CPP также поддерживает свойства в классе. Пожалуйста, обратитесь к этому документу для более подробной информации.
  4. Наконец, так как это довольно простой класс, мы не добавляем код в конструктор / деструктор C ++. Вся работа по инициализации выполняется в PHP-подобном конструкторе.

Функция-член, которая возвращает мод комплексного числа

Мод комплексного числа определяется как расстояние до исходной точки в системе координат Декарта. Он рассчитывается по следующей формуле:

 mod=sqrt(r*r+i*i)

Эта функция обсуждается в первую очередь, поскольку она очень проста: она использует только члены класса для обработки и возвращает скалярное значение.

Реализация этой функции, таким образом, проста.

 Php::Value mod() const {
    return (double)sqrt(r * r + i * i);
}

Вам может потребоваться #include <cmath>

Мы рассмотрим, как зарегистрировать эту функцию, чтобы она была доступна PHP позже.

Как и сигнатуры обычных функций, которые мы обсуждали в первой части, PHP-CPP поддерживает только 8 сигнатур для функции-члена:

 // signatures of supported regular methods
void        YourClass::example1();
void        YourClass::example2(Php::Parameters &params);
Php::Value  YourClass::example3();
Php::Value  YourClass::example4(Php::Parameters &params);
void        YourClass::example5() const;
void        YourClass::example6(Php::Parameters &params) const;
Php::Value  YourClass::example7() const;
Php::Value  YourClass::example8(Php::Parameters &params) const;

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

В этой функции mod()

Функция-член, выполняющая сложение

Создание функции с добавлением двух комплексных чисел немного сложнее.

В традиционном C ++ у нас есть 3 варианта:

  1. Переопределите оператор +c+d К сожалению, это невозможно в PHP, так как PHP не поддерживает переопределение операторов.
  2. Функция-член этого класса, называемая « add Таким образом, сложение будет сделано так: c->add(d)
  3. Обычная функция с двумя параметрами и возвращает новый комплекс, например: complex_add(c, d)

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

     Php::Value add(Php::Parameters &params)
    {
        Php::Value t=params[0];
        Complex *a=(Complex *)t.implementation();

        r+=(double)a->getReal();
        i+=(double)a->getImage();

        return this;
    }``

Сама функция add

Во-первых, обратите внимание на сигнатуру функции. Он использует 4-ю форму, поддерживаемую PHP-CPP. Он возвращает Php::ValuePhp::Parameters

Затем первая строка назначает первый параметр, который передается в функцию ( params[0]Php::Valuet

Как мы увидим позже при регистрации функции, мы можем быть уверены, что эта переменная, хотя и назначена типу Php::ValueComplex Таким образом, мы можем безопасно привести эту переменную к указателю на экземпляр класса Complex. Это именно то, что делает 2-я строка:

 Complex *a=(Complex *)t.implementation();

Метод implementation()Php::Valuevalue.h Согласно объяснению этой функции, которое я цитирую ниже:

  /**
     *  Retrieve the original implementation
     * 
     *  This only works for classes that were implemented using PHP-CPP,
     *  it returns nullptr for all other classes
     * 
     *  @return Base*
     */ 

он выполняет критическую задачу преобразования Php::Value

Это также объясняет, почему мы должны извлекать наши собственные классы из Php::BasePhp::Base

Как ни странно, такая важная функция не объясняется в текущем наборе документов PHP-CPP. Я получил этот совет от одного из авторов PHP-CPP (Эмиэль, спасибо!) Через обмен электронной почтой.

Когда эта implementationadd

Волшебная функция для отображения комплексного числа в более дружественной форме

PHP-CPP поддерживает PHP «магические методы» . Мы видели __construct()__toString()3+4i (Вам может понадобиться #include <sstream>

     Php::Value __toString()
    {
        std::ostringstream os;

        os<<r;
        if(i>=0)
            os<<'+';

        os<<i<<'i';

        return os.str();
    } 

Реализация этой функции проста.

Регистрация всех функций, созданных на данный момент

Для этой версии класса Mimicked Complex я реализовал только несколько приведенных выше функций. Заинтересованным пользователям предлагается расширить этот класс, чтобы добавить больше функциональности (например, базовые арифметические операции). Код для класса Complex и демонстрационная часть в части 1 могут быть клонированы из Github .

Чтобы сделать эти функции видимыми (вызываемыми) из скрипта PHP, нам нужно зарегистрировать их. Регистрация класса и его методов немного отличается от регистрации обычной функции:

 extern "C" {

    PHPCPP_EXPORT void *get_module() {
        // create static instance of the extension object
        static Php::Extension myExtension("complex", "1.0");

        // description of the class so that PHP knows which methods are accessible
        Php::Class<Complex> complex("Complex");

        // register the methods, and specify the parameters
        complex.method("mod", &Complex::mod, {});
        complex.method("__construct", &Complex::__construct);
        complex.method("add", &Complex::add, {
            Php::ByVal("op", "Complex", false, true)
        });

        // add the class to the extension
        myExtension.add(std::move(complex));

        // return the extension
        return myExtension;
    }
} 

В этой функции get_module()Php::ClasscomplexComplex Затем мы регистрируем функции-члены с помощью complex.method()

В последнем complex.method()add Чтобы узнать больше о том, как зарегистрировать класс и объявить параметры метода, пожалуйста, обратитесь к Параметрам и Классам и объектам .

Наконец, мы переместили complex

Компиляция, установка и тестирование

Теперь мы можем скомпилировать и установить это расширение complex.so

 make && sudo make install

Если все пройдет гладко, расширение будет установлено и готово к использованию.

Чтобы проверить расширение, давайте напишем несколько строк кода PHP:

 <?php

$c=new Complex(-3,-4);
echo $c->mod()."\n"; // First echo

$d=new Complex(4,3);

echo $c->add($d)->mod()."\n"; // Second echo

echo ((string)$d."\n"); // Third echo

$f=new \DateTime();
$g=$d->add($f); // Fourth echo but displays an error

Первое echo5

Второй echo1.41421... Так как мы вернули указатель thisaddmod()

Третий echo4+3i Это ожидаемо, так как мы пытаемся определить комплексное число с помощью нашего магического метода __toString() Он преобразует комплексное число в строку, используя реализацию, которую мы определили.

В четвертом echoComplexDateTime Это показывает строгую проверку типов в PHP-CPP при передаче объекта: тип объекта должен соответствовать зарегистрированному типу.

Обтекание в пространстве имен

Давайте свернем класс, который мы только что создали, в пространство имен для лучшей инкапсуляции.

Этот процесс удивительно прост и легок. Нам вообще не нужно менять объявление / реализацию класса. Все, что нам нужно сделать, это изменить процесс регистрации в функции get_module()

 extern "C" {

    PHPCPP_EXPORT void *get_module() {
        // create static instance of the extension object and the namespace instance
        static Php::Extension myExtension("complex", "1.0");

        Php::Namespace myNamespace("trComplex");

        // description of the class so that PHP knows which methods are accessible
        Php::Class<Complex> complex("Complex");

        // register the methods, and specify the parameters
        complex.method("mod", &Complex::mod, {});
        complex.method("__construct", &Complex::__construct);
        complex.method("add", &Complex::add, {
            Php::ByVal("op", "trComplex\\Complex", false, true)
        });

        // add the class to namespace
        myNamespace.add(std::move(complex));

        // add the namespace to the extension
        myExtension.add(std::move(myNamespace));

        // return the extension
        return myExtension;
    }

Вместо того, чтобы добавлять класс непосредственно в расширение, мы добавили еще один слой пространства имен (в данном случае названный trComplex Класс и регистрация его членов остаются практически идентичными, за исключением того, что в объявлении параметра функции addtrComplex\\ComplexComplex Затем мы переместили класс в пространство имен, а пространство имен в расширение.

Чтобы протестировать новое пространство имен и класс, мы можем изменить фрагменты тестирования:

 <?php

$c=new trComplex\Complex(-3,-4);
echo $c->mod()."\n";

$d=new trComplex\Complex(4,3);

echo $c->add($d)->mod()."\n";

echo ((string)$d."\n");

$f=new \DateTime();
$g=$d->add($f); //Error!

Вместо того, чтобы просто использовать ComplextrComplex\Complex Вывод будет таким же, как и в предыдущем демо. Это показывает, что пространство имен PHP-CPP идентично естественному пространству имен PHP.

Последние мысли

PHP-CPP очень мощный и простой в использовании. Я надеюсь, что после этих двух частей обсуждения пользователи найдут многообещающую библиотеку, которая поможет разрабатывать расширения PHP с использованием знакомого синтаксиса и семантики C ++.

Однако есть несколько областей для улучшения.

Прежде всего, его документация требует более глубоких объяснений API. Например, мы видим ранее, что метод implementation() Это не должно быть так. Пользователям нужны пошаговые инструкции и демонстрации (которые теперь находятся в его официальной документации), но также нужна полная ссылка на API.

Во-вторых, мы надеемся, что его основной релиз 1.0 появится как можно скорее. В моем тестировании всплыли некоторые забавные ошибки сегментации, и после обновления до последней версии эти ошибки исчезли; но нам нужна стабильная версия 1.0.

В-третьих, даже несмотря на то, что в последнем echotry...catch Это немного раздражает.

В этой части 2 по PHP-CPP мы рассмотрели несколько важных аспектов: функции класса и члена, магические методы, инкапсуляцию пространства имен и т. Д.

Я бы порекомендовал PHP-CPP в качестве инструмента для разработки расширений PHP. Его преимущества очевидны: быстрое обучение, знакомый синтаксис C ++, поддержка магических методов PHP, функции OO и т. Д.

Не стесняйтесь комментировать и дайте нам знать ваши мысли!