PHP
Article

Developing PHP Extensions with C++ and PHP-CPP: Advanced

By Taylor Ren

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., a Complex *;
  • 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 &params) {
        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 &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;
    }

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!

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

Comments
boen_robot

I used to have reservations about PHP-CPP, since it ultimately creates some "boilerplate" code at runtime around the Zend Engine code, meaning that though faster than PHP, you may still not get the best possible performance vs. using the Zend Engine "directly".

I had my eye on Zephir, seeing that it's an entire parser that ultimately converts to actual Zend Engine code that is then compiled, supposedly without runtime boilerplate.

However, this discussion in Zephir's GitHub issues has me worried... It seems Zephir too may add some runtime code, which it is prone to do when you aren't explicit enough all over the place. But then if you are, then you may as well use PHP-CPP to write actual C++ code.

I don't actually use either (yet), mostly because I'm too lazy to go on the "adventure" that is setting up a PHP build environment on Windows. But I'd be very interested to see some more detailed benchmarks of both projects and the binaries coming out of them. Personally, I'd be willing to be as explicit with Zephir as I'd have to be with PHP-CPP, but only if that means having a more efficient code than with PHP-CPP.

TaylorRen

Thanks for the sharing on this.

To write an extension with C++ is more straightforward in my view.

goddanao

Thank you Taylor, really nice serie.
It inspired me to try something myself. I'm not a c++ guy (this is my first attempt...) so i did something orrible i think, but it works quite well.
You can find it here if you wish to take a look:
https://github.com/goddanao/php-czmq - It's an early stage CZMQ 3 (http://czmq.zeromq.org) php binding, wich brings incredible easy message exchange patterns and devices based on ZeroMq;
https://github.com/goddanao/php-zyre - It's an early stage Zyre (https://github.com/zeromq/zyre) php binding, which is an open-source framework for proximity-based peer-to-peer applications.

I really appreciate any feedback.
Andrea.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in PHP, once a week, for free.