Building PHP extensions with C the easy way

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 
#include 
using namespace std;

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

    // run some slow code
    vector 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
HelloWorld("C++"); // gives: Hello World: C++

HelloWorld("foobar"); // gives: Hello World: foo

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

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

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

void someThread(int i) {
  for (int j=0; j < 100; j++) {
    cout << " thread: " << i << endl ;
    vector 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 
#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 con( get_driver_instance()->connect("127.0.0.1", "root", "") );
    con->setSchema("test");

    auto_ptr stmt( con->createStatement() );
    auto_ptr 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);