Comparing objects

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 functions, the Zend engine has additional features that are not exposed via magic methods, and that only are accessible for extension programmers. We have already mentioned that PHP-CPP allows you to implement the __toInteger(), __toFloat() and __toBool() methods (next to the __toString() that can be implemented in PHP scripts too). Another thing that is not available in PHP but that can be used by extensions is the possibility to install a custom object comparison procedure.

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 < operator of your class. In other words, if you want to install a custom comparison operator, you can do so by implementing operator<.


#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 Php::Value
     */
    virtual Php::Value __toString() override
    {
        return std::to_string(_value);
    }
    
    /**
     *  Comparison operator to compare the object
     *  @param  that
     *  @return bool
     */
    bool operator<(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.


<?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

We have thought about implementing the comparison method as a magic method (for example __compare()), or as a magic interface (for example Php::Comparable). In the end we have decided to go for the operator< approach. It better fits the C++ language, and the big advantage is that your objects can also be used in many C++ STL algorithms, because these algorithms also rely on operator<.