Building PHP extensions with C++, the easy way (update: MySQL, threads)

Here is an easy way to build a PHP extension with C++ and default PHP packages on a Ubuntu system. I use SWIG to wrap C++ code to the Zend API. When using loops and recursion intensively, porting a few functions to C++ can give you some extra power.

First, install all required packages (tested with Ubuntu 12.10):

apt-get install php5-cli php5-dev swig g++

Second, write the code:


// example.swig
%{
#include <iostream>
#include <vector>
using namespace std;

int HelloWorld(char *str) {
cout << "Hello World: " << str << endl;

// run some slow code
vector<int> array;
for (int i=0; i < 10000000; i++) array.push_back(i*2);
for (int i=0; i < array.size(); i++) array[i]++;
return 0;
}
%}

%module example
int HelloWorld(char *str);

Third, compile the code and load the exension:


swig -c++ -php5 example.swig
g++ `php-config --includes` -O2 -march=native -mtune=native -std=c++11 -fPIC -c *.cpp
g++ -shared *.o -o example.so

echo extension=`pwd`/example.so >/etc/php5/mods-available/example.ini
php5enmod example

And finally run: php test.php


// test.php
<?php
HelloWorld("C++"); // gives: Hello World: C++

HelloWorld("foo\0bar"); // gives: Hello World: foo

Note: To send the output to a web server, zend_printf() needs be used instead of std::cout or printf().

Note: When using Apache2 MPM-Prefork, processes are re-used for a client until the keep-alive ends (see KeepAlive* in your apache2.conf). Therefore, global variables in the C++ code need to be reset with %rshutdown {...}.

Returning vectors is also possible:


// example.swig
%{
#include <vector>
using namespace std;

vector<string> HelloArray() {
vector<string> v;
v.push_back("hello world");
return v;
}
%}

%typemap(out) vector { # convert vector to php-array[string]
array_init(return_value);
for (int i=0; i<$1.size(); ++i) add_next_index_string(return_value, $1[i].c_str(), 1);
}

%module example
vector<string> HelloArray();

// test.php
print_r( HelloArray() ); // Array( [0] => hello world )

You can also start Threads from an extension (C++11 is really nice here):


// example.swig
%{
#include <iostream>
#include <vector>
#include <thread>
using namespace std;

void someThread(int i) {
for (int j=0; j < 100; j++) {
cout << " thread: " << i << endl ;
vector<int> array;
for (int i=0; i < 10000000; i++) array.push_back(i*2);
for (int i=0; i < array.size(); i++) array[i] *= 2;
}
}

int HelloWorld(char *str) {
cout << "Hallo World: " << str << endl;

// start some threads
thread t1(someThread, 1);
thread t2(someThread, 2);
thread t3(someThread, 3);
// wait for threads to finish
t1.join();
t2.join();
t3.join();
return 0;
}
%}

%module example
int HelloWorld(char *str);

A small MySQL example looks like this:

// apt-get install mysql-server-5.5 libmysqlcppconn-dev
// g++ -shared *.o -lmysqlcppconn -o example.so

// example.swig
%{
#include <iostream>
#include <cppconn/driver.h>
#include <cppconn/statement.h>
#include <cppconn/resultset.h>
#include <cppconn/exception.h>
#include "mysql_connection.h"

using namespace std;
using namespace sql;

int HelloWorld(char *str) {
try {
auto_ptr<Connection> con( get_driver_instance()->connect("127.0.0.1", "root", "") );
con->setSchema("test");

auto_ptr<Statement> stmt( con->createStatement() );
auto_ptr<ResultSet> res( stmt->executeQuery("SELECT 'Hello World!' AS result") );
while (res->next()) {
cout << res->getString("result") << endl;
cout << res->getString(1) << endl;
}
} catch (SQLException &e) {
cout << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ << endl;
cout << e.what() << " " << e.getSQLState() << " " << e.getErrorCode() << endl;
}
return 0;
}
%}

%module example
int HelloWorld(char *str);

Note: Inside extensions, php.ini settings like open_basedir and memory_limit are not active.

Other resources:

Comments

Popular posts from this blog

How to construct a B+ tree with example

How to show only month and year fields in android Date-picker?

Conflict Serializability in database