Classes and objects

Serious business now. C++ and PHP are both object oriented programming languages, in which you can create classes and objects. The PHP-CPP library gives you the tools to combine these two and make a native C++ class accessible from PHP.

Sadly (but also logically if you think about it) not every thinkable C++ class can be directly exported to PHP. It takes a little more work (although not so much). For a start, you must make sure that your class is derived from Php::Base, and secondly, when you add your class to the extension object, you must also specify all methods that you want to make accessible from PHP.


#include <phpcpp.h>

// actual class implementation
class Counter : public Php::Base
{
private:
    int _value = 0;

public:
    MyClass() {}
    virtual ~MyClass() {}
    
    Php::Value increment() { return ++_value; }
    Php::Value decrement() { return --_value; }
    Php::Value value() const { return _value; }
};

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::Class<Counter> counter("Counter");
        counter.method("increment", &Counter::increment);
        counter.method("decrement", &Counter::decrement);
        counter.method("value", &Counter::value);
        
        // add the class to the extension
        myExtension.add(std::move(counter));
        
        // return the extension
        return myExtension;
    }
}

Let's talk about programming conventions first - I always use capitals for the first letter of a classname, and my member variables always start with an underscore. Every class always has a destructor, and it is always virtual. That's just a convention - my convention - and you of course do not have to follow that.

On topic. The example shows a very simple Counter class with three methods: increment(), decrement() and value(). The two update methods return the value of the counter after the operation, the value() method returns the current value.

If you want to make a class method that is accessible from PHP, you must ensure that is has one of the four supported signatures (which are the same signatures that exportable plain functions can have):


void YourClass::example1();
void YourClass::example2(Php::Parameters &params);
Php::Value YourClass::example3();
Php::Value YourClass::example4(Php::Parameters &params);

In the example we have used the third method form, a method that does not take any parameters, and that returns a Php::Value object. Methods work exactly the same as regular functions, with the difference that in the methods you have (of course) access to the member variables of the object.

To make the class accessible from PHP, you must add it to the extension object inside the get_module() function. The Php::Class template class can be be used for that. The template parameter should be your implementation class, so that the Php::Class object internally knows which class to instantiate the moment the "new" operator is used inside a PHP script.

The Php::Class constructor receives a string parameter, with the name of class in PHP. After you've created an instance of the Php::Class object, you should specify all methods that you want to make accessible from PHP, and finally, when all methods have been registered, you should add the class to your extension object so that it will be accessible from PHP. Note that in our example we have used the C++11 std::move function for this, so that the class object is actually moved into the extension object, which is a more efficient operation than copying.

Method parameters

Methods are just like functions, and just how you use the Php::ByVal and the Php::ByRef classes to specify the parameters of a function, you can specify method parameters too.


#include <phpcpp.h>

// actual class implementation
class Counter : public Php::Base
{
private:
    int _value = 0;

public:
    MyClass() {}
    virtual ~MyClass() {}
    
    Php::Value increment(Php::Parameters ¶ms) 
    { 
        return _value += params.size() > 0 ? (int)params[0] : 1; 
    }
    
    Php::Value decrement(Php::Parameters ¶ms) 
    { 
        return _value -= params.size() > 0 ? (int)params[0] : 1; 
    }
    
    Php::Value value() const 
    { 
        return _value; 
    }
};

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::Class<Counter> counter("Counter");
        counter.method("increment", &Counter::increment, { Php::ByVal("change", Php::Type::Numeric, false) });
        counter.method("decrement", &Counter::decrement, { Php::ByVal("change", Php::Type::Numeric, false) });
        counter.method("value", &Counter::value);
        
        // add the class to the extension
        myExtension.add(std::move(counter));
        
        // return the extension
        return myExtension;
    }
}

In the code above we have modified our first example. The increment and decrement method now get an optional 'change' parameter, which is a numeric value that holds the change that should be applied to the counter. Note that this parameter is optional - so inside our method implementation we have to check if the number of parameters is bigger than zero to prevent nasty segmentation faults.


<?php
$counter = new Counter();
$counter->increment(5);
$counter->increment();
$counter->decrement(3);
echo($counter->value()."\n");
?>

Output of above script is (of course) 3.