Wednesday, December 26, 2012

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:

3 comments:

  1. thanks to share your knowledge, can you please tell me that you are using %{ .... %} to create an extension, is it the default standard to make a php extension cause i didn't found ??

    Web Development Company

    ReplyDelete
    Replies
    1. The "%{...%}" is based on http://www.swig.org/tutorial.html . Instead of having .swig, .h and .cpp files, I put everything in one file to make it easier to understand.

      Delete
  2. I have tried so many different examples to get Swig to work and everything works good up until the point of trying to load the extension. I always get the following error:

    PHP Warning: PHP Startup: Unable to load dynamic library: /usr/lib/php/modules/example.so: undefined symbol: compiler_globals_id in Unknown on line 0

    Any suggestions?

    ReplyDelete

Labels

performance (23) benchmark (6) MySQL (5) architecture (5) coding style (5) memory usage (5) HHVM (4) C++ (3) Java (3) Javascript (3) MVC (3) SQL (3) abstraction layer (3) framework (3) maintenance (3) Go (2) Golang (2) HTML5 (2) ORM (2) PDF (2) Slim (2) Symfony (2) Zend Framework (2) Zephir (2) firewall (2) log files (2) loops (2) quality (2) real-time (2) scrum (2) streaming (2) AOP (1) Apache (1) Arrays (1) C (1) DDoS (1) Deployment (1) DoS (1) Dropbox (1) HTML to PDF (1) HipHop (1) OCR (1) OOP (1) Objects (1) PDO (1) PHP extension (1) PhantomJS (1) SPL (1) SQLite (1) Server-Sent Events (1) Silex (1) Smarty (1) SplFixedArray (1) Unicode (1) V8 (1) analytics (1) annotations (1) apc (1) archiving (1) autoloading (1) awk (1) caching (1) code quality (1) column store (1) common mistakes (1) configuration (1) controller (1) decisions (1) design patterns (1) disk space (1) dynamic routing (1) file cache (1) garbage collector (1) good developer (1) html2pdf (1) internationalization (1) invoice (1) just-in-time compiler (1) kiss (1) knockd (1) legacy code (1) legacy systems (1) logtop (1) memcache (1) memcached (1) micro framework (1) ncat (1) node.js (1) openssh (1) pfff (1) php7 (1) phpng (1) procedure models (1) ramdisk (1) recursion (1) refactoring (1) references (1) regular expressions (1) search (1) security (1) sgrep (1) shm (1) sorting (1) spatch (1) ssh (1) strange behavior (1) swig (1) template engine (1) threads (1) translation (1) ubuntu (1) ufw (1) web server (1) whois (1)