Special features

One of the questions we had to ask ourselves when we developed the PHP-CPP library was whether we should follow PHP conventions or follow C++ conventions for many of the library features.

PHP uses magic methods and magic interfaces to add special behavior to classes. With C++ you can achieve the same, but by using technologies like operator overloading, implicit constructors and casting operators. The PHP __invoke() method for example, is more or less identical to operator () in C++. The question that we asked ourselves was whether we should automatically pass the __invoke PHP method to a C++ operator() call - or use the same __invoke() method name in C++ too?

We have decided to follow the PHP conventions, and use magic methods and magic interfaces in C++ as well - although we must admit that having methods that start with two underscores does not make the code very pretty. But by using magic methods the switch from PHP to C++ is kept simpler for starting C++ programmers. And on top of that, not all magic methods and interfaces could have been implemented with core C++ features, so we did have to use some magic methods and/or interfaces anyway - so we could just as well follow PHP completely in this.

Besides the magic methods and interfaces that are also available in PHP user space, the Zend engine has additional features that are not exposed via magic methods, and that only are accessible for extension programmers. These features available for extensions built with PHP-CPP.

Extra casting functions

Internally, the Zend engine has special casting routines to cast objects to integers, to booleans and to floating point values. For one reason or another, a PHP script can only implement the __toString() method, while all other casting operations are kept away from it. The PHP-CPP library solves this limitation, and allows one to implement the other casting functions as well.

One of the design goals of the PHP-CPP library is to stay as close to PHP as possible. For that reason the casting functions have been given names that match the __toString() method: __toInteger(), __toFloat(), and __toBool().


#include <phpcpp.h>

/**
 *  A sample class, with methods to cast objects to scalars
 */
class MyClass : public Php::Base
{
public:
    /**
     *  C++ constructor and C++ destructpr
     */
    MyClass() {}
    virtual ~MyClass() {}

    /**
     *  Cast to a string
     *
     *  Note that now we use const char* as return value, and not Php::Value.
     *  The __toString function is detected at compile time, and it does
     *  not have a fixed signature. You can return any value that can be picked
     *  up by a Php::Value object.
     *
     *  @return const char *
     */
    const char *__toString()
    {
        return "abcd";
    }
    
    /**
     *  Cast to a integer
     *  @return long
     */
    long __toInteger()
    {
        return 1234;
    }
    
    /**
     *  Cast to a floating point number
     *  @return double
     */
    double __toFloat()
    {
        return 88.88;
    }
    
    /**
     *  Cast to a boolean
     *  @return bool
     */
    bool __toBool()
    {
        return true;
    }
};

/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        
        // extension object
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows 
        // which methods are accessible
        Php::Class<MyClass> myClass("MyClass");
        
        // add the class to the extension
        myExtension.add(std::move(myClass));
        
        // return the extension
        return myExtension;
    }
}

The casting methods are automatically called when an object is casted to a scalar type, or when it is used in a scalar context. The following example demonstrates this.


<?php
// initialize an object
$object = new MyClass();

// cast it
echo((string)$object."\n");
echo((int)$object."\n");
echo((bool)$object."\n");
echo((float)$object."\n");

?>

Comparing objects

If you compare two objects in PHP with comparison operators like < ==, != > (and the obvious others), the Zend engine runs an object comparison function. The PHP-CPP library interupts this method, and passes the comparison method to the __compare method of your class. In other words, if you want to install a custom comparison operator, you can do so by implementing __compare().


#include <phpcpp.h>
/**
 *  A sample class, that shows how objects can be compared
 */
class MyClass : public Php::Base
{
private:
    /**
     *  Internal value of the class
     *  @var    int
     */
    int _value;

public:
    /**
     *  C++ constructor
     */
    MyClass() 
    {
        // start with random value
        _value = rand();
    }
    
    /**
     *  C++ destructor
     */
    virtual ~MyClass() {}

    /**
     *  Cast the object to a string
     *  @return std::string
     */
    std::string __toString()
    {
        return std::to_string(_value);
    }
    
    /**
     *  Compare with a different object
     *  @param  that
     *  @return int
     */
    int __compare(const MyClass &that) const
    {
        return _value - that._value;
    }
};

/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        
        // extension object
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows 
        // which methods are accessible
        Php::Class<MyClass> myClass("MyClass");
        
        // add the class to the extension
        myExtension.add(std::move(myClass));
        
        // return the extension
        return myExtension;
    }
}

The comparison function is automatically called when you try to compare objects in PHP scripts. It should return 0 when the two objects are identical, a value less than zero when the 'this' object is smaller, and higher than zero when 'this' is bigger.


<?php
// initialize a couple of objects
$object1 = new MyClass();
$object2 = new MyClass();
$object3 = new MyClass();

// compare the objects
if ($object1 < $object2)
{
    echo("$object1 is smaller than $object2\n");
}
else
{
    echo("$object1 is bigger than $object2\n");
}

if ($object1 == $object3)
{
    echo("$object1 is equal to $object3\n");
}
else
{
    echo("$object1 is not equal to $object3\n");
}
?>

The above PHP script could produce the following output:

// output
1699622247 is bigger than 151717746
1699622247 is not equal to 627198306