In your dealings with PHP, you may come to consider writing a PHP extension yourself. There are several reasons I can think of that motivate me to do so:
- to extend PHP functionality for some very particular usage (mathematics, statistics, geometric, etc).
- to have a higher performance and efficiency compared to a pure PHP implementation
- to leverage the swiftness obtained from programming in another previously grasped language (for me, C++).
When it comes to choosing a tool to build PHP extensions, we see two different approaches:
- Use more pro-PHP semantics, like Zephir.
- Use more pro-C/C++ semantics, like PHP-CPP, which will be addressed in this article.
For me, the main drive to select the second approach is simple: I started my programming hobby with C/C++, so I still feel more comfortable writing those lower level modules in C/C++. PHP-CPP’s official site gives a few other reasons to do so.
Key Takeaways
- PHP-CPP is a library for developing PHP extensions that allows C++ developers to write PHP extensions without the complexities of working directly with the Zend API. It is written in C++11 and offers a collection of well-documented and user-friendly classes.
- PHP-CPP is evolving rapidly and it is recommended to use git to clone the repository for the latest updates. It supports single-threaded PHP installations and requires an upgrade to the g++ compiler to version 4.8.x or above for compatibility.
- PHP-CPP provides a skeleton extension project, which includes a main.cpp file, a MAKE file for compiling the extension, and an .ini file for extension loading. The skeleton project can be customized to suit individual needs and compiled and installed with the ‘make && sudo make install’ command.
- PHP-CPP supports four types of function signatures to be called from PHP and allows parameters to be passed by value in an array form. It also allows for function export/registration, specification of function parameter types, and the creation of object-oriented extensions.
Installation and configuration
PHP-CPP is evolving rapidly. At the time of this article’s writing, it is in version 0.9.1 (with 0.9.0 released about 2 days before). According to its documentation, “this is a feature-freeze release that prepares for the upcoming v1.0 version”, so we are confident we’ll see its 1.0 major release very soon.
It is thus recommended, at least during this interim period, to use git
to clone the repository and get the latest update later via git pull
.
NOTE: The PHP-CPP documentation on installation states that for the time being, it “only supports single-threaded PHP installations” because “internally the Zend engine uses a very odd system to ensure thread safety”. Future releases may support multi-threaded PHP installations but let’s just keep this in mind for now and stick to its current limitation. Luckily, “single-threaded PHP installations” should be the case for most of the PHP installations out there.
PHP-CPP is written in C++11. Thus, the older version of g++ installed in my Ubuntu 12.04 LTS does not support it. We need to upgrade our g++ compiler to version 4.8.x above. There is an article detailing the steps to do the upgrading. Please follow the instructions listed there.
Also, PHP-CPP compilation will use the php.h
header file. This file is normally missing in an Ubuntu box, unless php-dev
was installed. We can install PHP5 related development files by issuing this command:
sudo apt-get install php5-dev
After upgrading g++ and installing the necessary header files, we can issue the following command to compile and install the PHP-CPP library file (libphpcpp.so
):
make && sudo make install
The compilation will be quite fast. After the installation, the libphpcpp.so
file will be copied over to /usr/lib
, and all PHP-CPP header files will be copied to /usr/include
and /usr/include/phpcpp
folders.
The installation of PHP-CPP lib is now complete. It is quite straightforward and we can now move on to the programming part.
Before we do that, we’ll discuss a few important concepts and terminologies used in PHP-CPP. The full documentation can be found on its official site, and everyone is encouraged to read through it BEFORE doing any real programming.
Skeleton (empty) extension project files
PHP-CPP provides a skeleton extension project, containing the following 3 files:
main.cpp
: the main cpp file containing aget_module
function (will be discussed in more detail later)Makefile
: the sample MAKE file to compile the extensionyourextension.ini
: contains just one line for extension loading
Makefile
If you are familiar with *nix development, you are familiar with this Makefile. Some slight changes shall be made to customize this file to fit our needs:
- Change
NAME = yourextension
to a more meaningful one, likeNAME = skeleton
. - Change
INI_DIR = /etc/php5/conf.d
to match your system’s configuration. In my case, it isINI_DIR = /etc/php5/cli/conf.d
. I modified the INI path to enable the extension for PHP’s cli environment first.
These are all the changes I have made. The rest of the Makefile
can be kept as it is.
yourextension.ini
I renamed this file to skeleton.ini
and changed the only line in that file like this:
extension=skeleton.so
main.cpp
In the empty project provided by PHP-CPP, this file contains only one function: get_module()
, which is excerpted below:
#include <phpcpp.h>
/**
* tell the compiler that the get_module is a pure C function
*/
extern "C" {
/**
* Function that is called by PHP right after the PHP process
* has started, and that returns an address of an internal PHP
* strucure with all the details and features of your extension
*
* @return void* a pointer to an address that is understood by PHP
*/
PHPCPP_EXPORT void *get_module()
{
// static(!) Php::Extension object that should stay in memory
// for the entire duration of the process (that's why it's static)
static Php::Extension extension("yourextension", "1.0");
// @todo add your own functions, classes, namespaces to the extension
// return the extension
return extension;
}
}
For now, let’s just change this line to match the extension name we intend to create:
static Php::Extension extension("skeleton", "1.0"); // To be humble, we can change the version number to 0.0.1
get_module()
is called by PHP when the latter tries to load a required library. It is considered the entry point for a lib. It is declared using the extern "C"
modifier to comply with PHP lib requirement for the get_module()
function. It also uses a macro PHPCPP_EXPORT
which makes sure that get_module()
is publicly exported, and thus callable by PHP.
So far, we have made some changes to the empty project to suit our needs. We can now compile and install this project and install the extension:
make && sudo make install
Next, we need to copy the required files into the appropriate folders:
cp -f skeleton.so /usr/lib/php5/20121212
cp -f skeleton.ini /etc/php5/cli/conf.d
We just need to make sure that the skeleton.so
lib is copied to the right location of PHP extensions (in my Ubuntu setup, it should be /usr/lib/php5/20121212
as shown above).
We can then verify the extension is loaded in CLI by php -i | grep skeleton
, and the terminal shall display something like this:
(Recall that the skeleton.ini
is the file we modified above, which contains the extension=skeleton.so
line.)
We have so far compiled and installed our first PHP extension using PHP-CPP. Of course, this extension does nothing yet. We will now create our first few functions to further understand the process of building PHP extensions.
“Hello, Taylor” function
The first function we create will be a slightly modified version of “Hello, World”. Let’s see the full code of main.cpp
first:
#include <phpcpp.h>
#include <iostream>
void helloWorld (Php::Parameters ¶ms)
{
std::string name=params[0];
std::cout<<"Hello "<<name<<"!"<<std::endl;
}
extern "C" {
PHPCPP_EXPORT void *get_module()
{
static Php::Extension extension("skeleton", "1.0");
extension.add("helloWorld", helloWorld);
return extension;
}
}
According to the PHP-CPP documentation on “Register native functions“, it supports four types of function signatures to be called from PHP:
void example1();
void example2(Php::Parameters ¶ms);
Php::Value example3();
Php::Value example4(Php::Parameters ¶ms);
In this case, I am using the second signature and the parameters are passed by value in an array form (PHP feature).
However, in helloWorld
, we have specifically used C++ type std::string
to grab the first parameter. We have also used C++ std
lib to output a welcoming message.
In get_module()
function, after declaring the extension
variable, we add the function we would like to export (helloWorld()
) and assign a name visible to the PHP script (helloWorld
).
Now let’s compile and install the extension. If everything goes smoothly, the new skeleton.so
file will be copied to the extension directory.
We can write a simple script to test the function just created:
<?php
echo "Testing helloWorld in skeleton.so\n";
echo helloWorld('Taylor');
echo helloWorld(1234+5678);
echo helloWorld(['string', 123+456]);
Please take some time to look at the output:
We will come back to what we have observed here later.
Function parameters by reference
Next, we will see another function which passes parameters by reference, a swap()
function. In this function, we will also try to specify the number of parameters and their type.
In main.cpp
, we add one more function swap()
:
void swap(Php::Parameters ¶ms) {
Php::Value temp = params[0];
params[0] = params[1];
params[1] = temp;
}
And also export the function by specifying the number of parameters and their type:
extension.add("swap", swap,{
Php::ByRef("a", Php::Type::Numeric),
Php::ByRef("b", Php::Type::Numeric)
});
We explicitly say that:
- There will be two parameters (
a
andb
); - They should be passed by reference (instead of by value);
- They should be of type Numeric.
Let’s compile and install the updated extension again and write some code snippets to see how this new functions works:
<?php
$a=10;
$b=20;
// swap($a);
// Above will create a segment fault
swap($a, $b);
echo $a."|".$b."\n";
$c=10;
$d="string";
swap($c, $d);
echo $c."|".$d."\n";
$e=10;
$f=new \DateTime();
swap($e, $f);
var_dump($e);
var_dump($f);
swap($a)
will fail. This is expected and unexpected. The expected part is that we need two parameters and only one is given. But, shouldn’t that error be captured by PHP when calling the function swap
and prompting us something like Not enough parameters
?
The first call (swap($a, $b)
) shows the expected result: 20|10
. The function swaps the two numbers passed in.
The second call is somehow unexpected: we have told PHP that we are to swap two numbers! But it just ignores the fact that the 2nd parameter passed is a string and does the swapping anyway!
Well, in a way, it is still expected. PHP does not really distinguish a number type and a string type. This behavior complies to the PHP standard. Also due to this behavior, we didn’t and can’t use C++ internal types for the temporary variable used in the function (temp
) but used Php::Value
as the variable type.
The third call will work. The first var_dump
will show the DateTime object and the second will show the integer. This is somehow very much unexpected (at least to me). After all, an object is quite different from a number/string. But after considering that this “swap” behavior is also doable in PHP, it fits in with PHP’s oddities.
So, does it mean the “type” specification won’t have any impact? Not really. To further elaborate this, we create a third function:
void swapObject(Php::Parameters ¶ms)
{
Php::Value temp = params[0];
params[0] = params[1];
params[1] = temp;
}
And we register this function like this:
extension.add("swapObject", swap,{
Php::ByRef("a", "sampleClass"),
Php::ByRef("b", "sampleClass")
});
The testing code will be like this:
class sampleClass {
var $i;
public function __construct($n) {
$this->i = $n;
}
}
$o1 = new sampleClass(10);
$o2 = new sampleClass(20);
swapObject($o1, $o2);
echo $o1->i . "|" . $o2->i . "\n";
class anotherClass {
}
$d1 = new anotherClass();
$d2 = new anotherClass();
swapObject($d1, $d2);
The first call to swapObject()
will work as we passed in the correct class type (sampleClass
). The second will fail, displaying “PHP Catchable fatal error: Argument 1 passed to swapObject() must be an instance of sampleClass, instance of anotherClass given...
“.
The above code segment illustrates one important aspect on type restriction: scalar types declaration is not really implemented. PHP and thus PHP-CPP only enforce object-type declaration. Also, the number of parameters is not really enforced on the PHP side.
Conclusion
In this article, we illustrated the steps to prepare PHP-CPP to work for our PHP environment. We also discussed some basic steps to create a PHP extension using PHP-CPP (and C++ semantics).
We covered the extension project files, function signatures, function export/registration, and the function parameter types.
In our next article, we will further elaborate a few key features in PHP-CPP and provide a real-world use case demonstrating the usage of C++ class and namespace implementations using PHP-CPP.
Frequently Asked Questions (FAQs) on PHP Extension Development
What is PHP-CPP and how does it differ from PHP?
PHP-CPP is a library for developing PHP extensions. It offers a collection of well-documented and user-friendly classes, allowing C++ developers to write PHP extensions without the complexities of working directly with the Zend API. Unlike PHP, which is an interpreted language, PHP-CPP allows you to write code in C++, a compiled language. This can result in performance improvements, as compiled code generally runs faster than interpreted code.
How do I install PHP-CPP on my system?
To install PHP-CPP on your system, you need to clone the PHP-CPP repository from GitHub. After cloning, navigate to the directory and execute the ‘make’ command. Once the build process is complete, install the library using the ‘make install’ command. Remember, you need to have root privileges to install the library.
How do I create a basic PHP extension using PHP-CPP?
Creating a PHP extension using PHP-CPP involves several steps. First, you need to create a directory for your extension and navigate into it. Then, create a ‘Makefile’ and a C++ source file for your extension. The ‘Makefile’ will contain instructions for building your extension, while the C++ source file will contain the actual code for your extension. After writing your code, you can build your extension using the ‘make’ command.
How can I debug my PHP extension?
Debugging a PHP extension can be a bit tricky, as you’re dealing with a compiled language. However, you can use tools like GDB (GNU Debugger) to debug your extension. GDB allows you to set breakpoints, step through your code, and inspect variables, which can be very helpful when trying to track down bugs.
Can I use PHP-CPP to create extensions for PHP 7?
Yes, PHP-CPP is compatible with PHP 7. However, you need to make sure that you’re using the latest version of PHP-CPP, as earlier versions may not support PHP 7.
How can I handle exceptions in PHP-CPP?
PHP-CPP provides a class called Php::Exception, which you can use to throw exceptions from your C++ code. These exceptions can then be caught and handled in your PHP code, just like any other PHP exception.
Can I use PHP-CPP to create object-oriented extensions?
Yes, PHP-CPP supports object-oriented programming. You can define classes in your C++ code, and these classes can then be used in your PHP code. This allows you to write clean, modular code that’s easy to maintain.
How can I call PHP functions from my C++ code?
PHP-CPP provides a class called Php::Call, which you can use to call PHP functions from your C++ code. This allows you to leverage the power of PHP’s built-in functions in your extension.
Can I use PHP-CPP to create extensions that interact with databases?
Yes, you can use PHP-CPP to create extensions that interact with databases. However, you’ll need to use a C++ database library, as PHP-CPP does not provide any built-in database functionality.
How can I distribute my PHP extension?
Once you’ve built your PHP extension, you can distribute it by packaging it as a PECL package. PECL is a repository for PHP extensions, and it provides a standard way to distribute and install extensions.
Taylor is a freelance web and desktop application developer living in Suzhou in Eastern China. Started from Borland development tools series (C++Builder, Delphi), published a book on InterBase, certified as Borland Expert in 2003, he shifted to web development with typical LAMP configuration. Later he started working with jQuery, Symfony, Bootstrap, Dart, etc.