В моих предыдущих статьях я представил библиотеку PHP-CPP для создания расширения для PHP с использованием C ++ ( первая и вторая статьи ). В последнем я продемонстрировал некоторую OO-сторону написания расширения PHP с классом Complex
выполняющего сложные числовые манипуляции.
Это введение не является полным, так как основное внимание в этой статье уделяется больше демонстрации возможностей OO PHP-CPP, а не деталям реализации OO.
В этой статье мы подробнее рассмотрим разработку Complex
lib, добавим больше функций-членов и обратимся к некоторым дополнительным темам при написании расширения PHP с функциями OO с использованием PHP-CPP:
- Возврат
this
указателя; - Возврат указателя
Complex
объекта, т.Complex *
; - Разоблачение магического метода
__toString
; - Связывание вызовов функций-членов;
- Создание исключения и обработка его в PHP
Готовый исходный код Complex
lib, а также тестовый PHP-скрипт находятся в этом репозитории Github .
Давайте начнем.
Препараты
Весь процесс подготовки вашей среды объясняется в первом посте .
Возвращая this
указатель в C ++
Как описано во втором посте , мы используем функции-члены для выполнения различных математических операций над комплексными числами. В этой демонстрации мы реализуем четыре такие функции: add
, sub
, mul
и div
. Сначала я объясню первые три. Функция div
включает обработку исключений и будет обсуждена позже.
Давайте рассмотрим функцию mul
(для умножения). Функции add
и sub
более или менее одинаковы.
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; }
ПРИМЕЧАНИЕ. В этой статье я не буду затрагивать некоторые ранее обсуждавшиеся основные темы, такие как изменение файла Makefile
и ini, регистрация функций-членов, класса и пространства имен и т. Д. Пожалуйста, обратитесь к предыдущим частям для этих битов.
Получить this
указатель из C ++ обратно в PHP просто. Внутри этой функции C ++ this
указатель (как тип Complex *
) может быть возвращен обратно в PHP как тип Php :: Value. Конверсия не потеряет никакой информации об объекте. Он также не требует явного преобразования типов.
Возврат указателя Complex
объекта
Возвращение this
означает, что большую часть времени сам объект был изменен. Но в некоторых случаях мы можем захотеть вернуть новый объект и оставить «текущий» объект (вызывающий объект) без изменений.
В нашем классе Complex
у нас есть одна такая функция, которая возвращает сопряженное число данного комплексного числа ( a+bi
становится a-bi
).
Php::Value conjugate() { Complex *t = new Complex(); t->r = r; t->i = -i; return Php::Object("tr\\Complex", t); }
Ключевым моментом здесь является то, что нам придется использовать Php::Object
для явного преобразования нашего объекта Complex *
в Php::Object
, таким образом, информация о классе может быть надлежащим образом сохранена и доступна, когда этот объект будет проанализирован PHP-скриптом позже. ,
Первым параметром этой функции является тип класса, в данном случае tr\\Complex
. Я использую это имя, поскольку я обернул этот класс (« Complex
») в отдельное пространство имен (« tr
»).
Второй параметр — это объект, который нужно передать обратно.
Возврат нового экземпляра класса немного сложнее, чем просто возвращение указателя this
но все же управляемым, если вы прочитали документацию и нашли правильный раздел. Для некоторых других примеров использования вы можете прочитать эту часть в официальной документации PHP-CPP.
Разоблачение магического метода __toString
В нашем классе есть функция __toString
которая печатает комплексное число в более удобочитаемом виде, например: 1+2i
. В моей предыдущей статье эта функция не была представлена (или «зарегистрирована» в терминах PHP-CPP), но все еще может быть вызвана изнутри PHP. Но чтобы сделать эту функцию вызываемой для объекта Complex
после того, как мы применили некоторые математические операции, такие как « echo $a->add($b)->sub($c)
», нам нужно явно зарегистрировать ее в нашем скомпилированном расширении:
complex.method("__toString", &Complex::__toString);
Причина, по которой мы должны это сделать, подробно обсуждается в проблеме, представленной в репозиторий PHP-CPP под номером 150 .
Цепные вызовы функций-членов
Одна вещь, которая должна быть реализована в этом классе, — это возможность связывать функции-члены так, чтобы мы могли выполнять вычисления следующим образом: $a->add($b)->sub($c)
. Результат все еще должен иметь возможность вызывать функции-члены.
Это делается с помощью подхода, описанного выше, то есть, возвращая указатель this
обратно в PHP. Однако в более ранней версии PHP-CPP есть ошибка при разыменовании объекта, и он создает «ошибку сегментации», если вызовы метода связаны друг с другом.
Ошибка ( # 151 ) была подана, и коммит был отправлен вместе с патчем в исходный код PHP-CPP. Если вы используете более старую версию библиотеки PHP-CPP для компиляции библиотеки PHP-CPP и вашей собственной библиотеки, обновите исходный код PHP, перекомпилируйте и переустановите библиотеку PHP-CPP и вашу библиотеку.
Как поясняет коммит:
fix issue #151, chaining method calls was not working as it should be… …cause the per-object refcount was not updated correctly, which caused an object to be destructed even when it already was assigned to a different variable.
Я рад, что моя работа над моим собственным проектом может помочь улучшить мою библиотеку. [Эд: Молодец!]
Исключение и обработка в PHP
В нашем классе Complex
есть еще две функции, которые, вероятно, вернут исключение в PHP для обработки: div
и phi
. Первый выполняет операцию деления, а второй возвращает угол комплексного числа, как в его альтернативном представлении, поляризованной записи (r, θ)
.
Обе операции могут завершиться неудачей, если в качестве параметра (или вызывающего) передается комплексное число, но его действительная часть и часть изображения равны 0. Для этих двух операций нам нужна обработка исключений. Помните, мы должны добавить исключение в наш код C ++, и это PHP-скрипт, который перехватит исключение и выполнит необходимую обработку:
Php::Value div(Php::Parameters ¶ms) { Php::Value t = params[0]; Complex *b = (Complex*) t.implementation(); double t1 = b->mod() * b->mod(); if (t1 < EPS) //EPS is a predefined double value which is very small, say 1E-12 throw Php::Exception("Division by zero"); double tr = r * (double) (b->getReal()) + i * (double) (b->getImage()); double ti = i * (double) (b->getReal()) - r * (double) (b->getImage()); r = tr / t1; i = ti / t1; return this; }
и в скрипте PHP мы ловим это исключение следующим образом:
$a=new tr\Complex(1,2); $c=new tr\Complex(); //$c is actuall 0+0i try { $res=$a->div($c); } catch(Exception $e) { echo "Caught exception: ".$e->getMessage()."\n"; } }
Приведенный выше фрагмент кода будет отображать строку текста, как показано ниже:
Caught exception : Division by zero
Легко, правда? Исключение C ++, созданное внутри нашего расширения, передается обратно в PHP и перехватывается должным образом. Кроме того, мы можем манипулировать исключением, как если бы оно было собственным исключением PHP, созданным другим PHP-кодом!
Проверьте все функции
Наконец, мы можем скомпилировать и установить расширение complex.so
для нашей установки PHP через make && sudo make install
. Если все идет гладко, мы можем проверить установку нашего расширения, введя следующую команду в терминале:
php -i | grep complex
Терминал должен отображать строку « /etc/php5/cli/conf.d/complex.ini
», и мы можем быть уверены, что наше расширение установлено и готово к /etc/php5/cli/conf.d/complex.ini
любыми сценариями PHP.
ПРИМЕЧАНИЕ. Если мы проверим Makefile
для этого расширения, мы увидим, что мы устанавливаем это расширение PHP в его среду CLI. Если мы хотим установить это расширение так, чтобы оно было загружено Apache, мы изменим строку ниже:
# Below is for installation to CLI #INI_DIR = /etc/php5/cli/conf.d # Below is for installation to Apache INI_DIR = /etc/php5/apache2/conf.d
Сценарий тестирования PHP для этого расширения приведен ниже с некоторыми комментариями:
//Testing the mod function //Displays 5 $c=new tr\Complex(-3,-4); echo "Mod of $c is: ".$c->mod()."\n"; ... //Testing a string representation of a complex number //Displays: -4-3i $e=new tr\Complex(-4,-3); echo ((string)$e."\n"); ... //Chain the member function calls $a=new tr\Complex(1,1); $b=new tr\Complex(1,2); ... echo ($a->add($b)->sub($c)->add($d))."\n"; ... //Exception handling $a=new tr\Complex(1,2); $c=new tr\Complex(); try { $res=$a->div($c); } catch(Exception $e) { echo "Caught exception: ".$e->getMessage()."\n"; }
Все тестовые сценарии должны работать правильно, а исключения регистрируются правильно.
Вывод
На этом мы завершаем серию из 3 статей об этой мощной библиотеке для создания расширения PHP с C ++. Мы рассмотрели основы, OO и некоторые дополнительные темы в OO-программировании. Мы также помогли немного улучшить PHP-CPP.
Что еще мы можем сделать с PHP-CPP? Я процитирую несколько строк из электронных писем, которые я получил от Эмиеля Брюйнтеса (соавтора PHP-CPP):
Библиотека PHP-CPP идеально подходит для использования, если вы работаете над проектом и у вас есть одно или несколько из следующих требований:
— Вы работаете над частью программного обеспечения / структурой данных / алгоритмом, и вы хотите, чтобы в будущем ваше программное обеспечение могло использоваться и в не-PHP проектах.
— Вы хотите использовать инструменты или библиотеки, которые еще не были доступны как расширение PHP.
— Вы хотите более высокую производительность кода C / C ++ (по сравнению с PHP), но вы также хотите создавать структурированный, объектно-ориентированный код, который легко понять и поддерживать для других разработчиков / коллег.
Возможности огромны: фреймворк (например, Phalcon), язык шаблонов (например, Smarty или Twig) и т. Д.
Пожалуйста, оставьте свои комментарии и мнения, и дайте нам знать, что вы сделали с этой библиотекой!