Статьи

Разработка расширений PHP с использованием C ++ и PHP-CPP: Advanced

В моих предыдущих статьях я представил библиотеку 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 &params) { 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 &params) { 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) и т. Д.

Пожалуйста, оставьте свои комментарии и мнения, и дайте нам знать, что вы сделали с этой библиотекой!