Developing PHP Extensions with C++ and PHP-CPP: Advanced
In my earlier articles, I have introduced the PHP-CPP lib to create an extension for PHP using C++ (first article and second article). In the latter, I demonstrated a bit of the OO side of writing a PHP extension with a Complex
class doing complex number manipulations.
That introduction is not complete as the main focus of that article is more on the demonstration of the OO capability of PHP-CPP, not on the OO implementation details.
In this article, we will further drill down the Complex
lib development, adding more member functions, and addressing some advanced topics in writing a PHP extension with OO features using PHP-CPP:
- Returning
this
pointer; - Returning a
Complex
object pointer, i.e., aComplex *
; - Exposing the
__toString
magic method; - Chaining member function calls;
- Throwing an exception and handling it in PHP
The finished Complex
lib source, as well as a test PHP script is located in this Github repo.
Let’s get started.
Preparations
The entire process of preparing your environment is explained in the first post.
Returning this
pointer in C++
As described in the second post, we are using member functions to perform various mathematical operations on complex numbers. In this demo, we will implement four of such functions: add
, sub
, mul
and div
. I will explain the first three first. The div
function involves exception handling and will be discussed later.
Let’s take a look a the mul
function (for multiplication). add
and sub
functions are more or less the same.
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;
}
NOTE: In this article, I will not cover some previously discussed basic topics, like modifying the Makefile
and ini file, registering the member functions, class and namespace, etc. Please refer to the previous parts for those bits.
Returing a this
pointer from C++ back to PHP is straightforward. Inside this C++ function, this
pointer (as a Complex *
type) can be returned back to PHP as a Php::Value type. The conversion won’t lose any object information. It does not require an explicit type conversion either.
Returning a Complex
object pointer
Returning this
means, most of the time, that the object itself has been changed. But in some cases, we may want to return a new object and keep the “current” object (the calling object) unchanged.
In our Complex
class, we have one such function that returns the conjugated number of a given complex number (a+bi
becomes a-bi
).
Php::Value conjugate() {
Complex *t = new Complex();
t->r = r;
t->i = -i;
return Php::Object("tr\\Complex", t);
}
The key point here is that we will have to use Php::Object
to explicitly convert our Complex *
object into a Php::Object
, thus the class information can be properly retained and kept accessible when that object is parsed by a PHP script later.
The first parameter of this function is the class type, in this case tr\\Complex
. I use this name as I have wrapped this class (“Complex
“) into a separate namespace (“tr
“).
The second parameter is the object to be passed back.
Returning a new class instance is a bit trickier than just returning a this
pointer but still manageable, as long as you have read through the documentation and found the right section. For some more examples of use, you may want to read this part in PHP-CPP’s official documentation.
Exposing the __toString
magical method
In our class, there is a __toString
function that prints a complex number in a more readable way like: 1+2i
. In my previous article, this function is not exposed (or “registered” in PHP-CPP terms) but still can be called from within PHP. But to make this function callable on the Complex
object after we apply some math operations like “echo $a->add($b)->sub($c)
“, we need to explicitly register it in our compiled extension:
complex.method("__toString", &Complex::__toString);
The reason we have to do so is discussed in detail in the issue submitted to PHP-CPP repository as Issue #150.
Chaining member function calls
One thing that must be implemented in this class is the ability to chain member functions so that we can do the calculation like: $a->add($b)->sub($c)
. The result should still be able to call its member functions.
This is done by the approach described above, i.e., returning a this
pointer back to PHP. However, older PHP-CPP lib has a bug on dereferencing the object and will create a “Segmentation Fault” if the method calls are chained.
An issue (#151) has been filed and a commit is submitted with the patch on PHP-CPP source. If you are using an older version of PHP-CPP lib to compile the PHP-CPP lib and your own lib, please update the PHP source and re-compile and re-install the PHP-CPP lib and your lib.
As the commit summary explains:
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.
I am glad that my work on my own project can help the lib I use become better. [Ed: Well done!]
Exception throwing and handling in PHP
There are two more functions in our Complex
class that will probably throw an exception back to PHP to handle: div
and phi
. The former does a division operation and the latter returns the angle of the complex number as in its alternative representation, the polarized notation (r, θ)
.
Both operations might fail if a complex number is passed as the parameter (or caller) but its real part and image part are 0. For those two operations, we need to have exception handling. Remember, we are to throw an exception in our C++ code and it is the PHP script that will catch the exception and do the necessary handling:
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;
}
and in the PHP script, we catch this exception like this:
$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";
}
}
The above code segment will display a line of text as below:
Caught exception: Division by zero
Easy, right? A C++ exception constructed inside our extension is passed back to PHP and caught properly. Furthermore, we can manipulate the exception as if it were a native PHP exception thrown by some other PHP code!
Test all the functions
Finally, we can compile and install the complex.so
extension for our PHP installation via make && sudo make install
. If everything goes smoothly, we can verify the installation of our extension by issuing this command in the terminal:
php -i | grep complex
The terminal should display a line saying “/etc/php5/cli/conf.d/complex.ini
” and we can be sure that our extension is installed and ready to be called by any PHP scripts.
NOTE: If we examine the Makefile
for this extension, we will see that we are installing this PHP extension into its CLI environment. If we want to install this extension so that it will be loaded by Apache, we change the below line:
# 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
The testing PHP script for this extension is excerpted below with some comments:
//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";
}
All the test scripts should run correctly and the exceptions are caught properly.
Conclusion
This concludes my 3-article series on this powerful lib to build a PHP extension with C++. We covered the basics, the OO side and some advanced topics in OO programming. We also helped PHP-CPP improve a bit.
What else can we do with PHP-CPP? I will quote a few lines from the email correspondences that I received from Emiel Bruijntjes (co-author of PHP-CPP):
The PHP-CPP library is ideal to use if you’re working on a project, and you have one or more of the following requirements:
– You are working on a piece of software / data structure / algorithm, and you want to ensure that in the future your software can be used in non-PHP projects as well.
– You want to make use of tools or libraries that have not yet been made available as PHP extension.
– You want the better performance of C/C++ code (compared to PHP), but you also want to build structured, object-oriented code, that is easy to understand and easy to maintain for other developers / colleagues.
The possibilities are enormous: a framework (like Phalcon), a template language (like Smarty or Twig), etc.
Please leave your comments and views, and let us know what you have done with this lib!