В моей первой части, посвященной созданию расширения PHP с помощью PHP-CPP , мы рассмотрели некоторые основы использования PHP-CPP для создания нашего первого скелетного расширения PHP и рассмотрели некоторые важные термины.
В этой части мы доработаем его функции ОО. Мы будем имитировать класс комплексных чисел (в форме 3+4i
Мы начнем с изменения пустых файлов проекта, предоставляемых PHP-CPP, и изменим следующие два файла в соответствии с нашей новой библиотекой (в данном случае complex.so
- Переименуйте
yourtextension.ini
complex.ini
extension=complex.so
- Измените несколько строк в
Makefile
NAME = complex
INI_DIR = /etc/php5/cli/conf.d
Теперь мы переходим к созданию нашего класса Complex
main.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 ¶ms) {
if (params.size() == 2) {
r = params[0];
i = params[1];
} else {
r=0;
i=0;
}
}
В этом сегменте кода некоторые вещи требуют дальнейшей проработки:
- Любой класс, который объявлен в нашей библиотеке, ДОЛЖЕН наследоваться от
Php::Base
Это требование PHP-CPP.Php::Base
(Заголовочный файл для классаPhp::Base
base.h
- Мы объявили как конструктор / деструктор C ++, так и PHP-подобный магический конструктор. Последний также параметризован, так что мы можем установить вещественную часть / часть изображения комплексного числа с помощью параметров, переданных из PHP во время создания объекта.
- Поскольку действительная часть / часть изображения комплексного числа, определенного в нашем классе, является
private
getReal()
getImage()
PHP-CPP также поддерживает свойства в классе. Пожалуйста, обратитесь к этому документу для более подробной информации. - Наконец, так как это довольно простой класс, мы не добавляем код в конструктор / деструктор 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 ¶ms);
Php::Value YourClass::example3();
Php::Value YourClass::example4(Php::Parameters ¶ms);
void YourClass::example5() const;
void YourClass::example6(Php::Parameters ¶ms) const;
Php::Value YourClass::example7() const;
Php::Value YourClass::example8(Php::Parameters ¶ms) const;
Любая функция с сигнатурой, отличной от описанной выше, все еще может появиться в объявлении класса, но НЕ будет в состоянии быть зарегистрированной и НЕ будет видна сценарию PHP.
В этой функции mod()
Функция-член, выполняющая сложение
Создание функции с добавлением двух комплексных чисел немного сложнее.
В традиционном C ++ у нас есть 3 варианта:
- Переопределите оператор
+
c+d
К сожалению, это невозможно в PHP, так как PHP не поддерживает переопределение операторов. - Функция-член этого класса, называемая «
add
Таким образом, сложение будет сделано так:c->add(d)
- Обычная функция с двумя параметрами и возвращает новый комплекс, например:
complex_add(c, d)
Вариант 3 обычно считается худшим вариантом, и мы увидим, как создать такую функцию сложения, используя вариант 2.
Php::Value add(Php::Parameters ¶ms)
{
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::Value
Php::Parameters
Затем первая строка назначает первый параметр, который передается в функцию ( params[0]
Php::Value
t
Как мы увидим позже при регистрации функции, мы можем быть уверены, что эта переменная, хотя и назначена типу Php::Value
Complex
Таким образом, мы можем безопасно привести эту переменную к указателю на экземпляр класса Complex. Это именно то, что делает 2-я строка:
Complex *a=(Complex *)t.implementation();
Метод implementation()
Php::Value
value.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::Base
Php::Base
Как ни странно, такая важная функция не объясняется в текущем наборе документов PHP-CPP. Я получил этот совет от одного из авторов PHP-CPP (Эмиэль, спасибо!) Через обмен электронной почтой.
Когда эта implementation
add
Волшебная функция для отображения комплексного числа в более дружественной форме
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::Class
complex
Complex
Затем мы регистрируем функции-члены с помощью 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
Первое echo
5
Второй echo
1.41421...
Так как мы вернули указатель this
add
mod()
Третий echo
4+3i
Это ожидаемо, так как мы пытаемся определить комплексное число с помощью нашего магического метода __toString()
Он преобразует комплексное число в строку, используя реализацию, которую мы определили.
В четвертом echo
Complex
DateTime
Это показывает строгую проверку типов в 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
Класс и регистрация его членов остаются практически идентичными, за исключением того, что в объявлении параметра функции add
trComplex\\Complex
Complex
Затем мы переместили класс в пространство имен, а пространство имен в расширение.
Чтобы протестировать новое пространство имен и класс, мы можем изменить фрагменты тестирования:
<?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!
Вместо того, чтобы просто использовать Complex
trComplex\Complex
Вывод будет таким же, как и в предыдущем демо. Это показывает, что пространство имен PHP-CPP идентично естественному пространству имен PHP.
Последние мысли
PHP-CPP очень мощный и простой в использовании. Я надеюсь, что после этих двух частей обсуждения пользователи найдут многообещающую библиотеку, которая поможет разрабатывать расширения PHP с использованием знакомого синтаксиса и семантики C ++.
Однако есть несколько областей для улучшения.
Прежде всего, его документация требует более глубоких объяснений API. Например, мы видим ранее, что метод implementation()
Это не должно быть так. Пользователям нужны пошаговые инструкции и демонстрации (которые теперь находятся в его официальной документации), но также нужна полная ссылка на API.
Во-вторых, мы надеемся, что его основной релиз 1.0 появится как можно скорее. В моем тестировании всплыли некоторые забавные ошибки сегментации, и после обновления до последней версии эти ошибки исчезли; но нам нужна стабильная версия 1.0.
В-третьих, даже несмотря на то, что в последнем echo
try...catch
Это немного раздражает.
В этой части 2 по PHP-CPP мы рассмотрели несколько важных аспектов: функции класса и члена, магические методы, инкапсуляцию пространства имен и т. Д.
Я бы порекомендовал PHP-CPP в качестве инструмента для разработки расширений PHP. Его преимущества очевидны: быстрое обучение, знакомый синтаксис C ++, поддержка магических методов PHP, функции OO и т. Д.
Не стесняйтесь комментировать и дайте нам знать ваши мысли!