Persistent PHP processes in Erlang/OTP

Running PHP code from within Erlang is easy: os:cmd("php -r 'echo \"Hello, World!\";'"). This is fine when you need to run simple commands. When you demand more from PHP, this approach becomes awkward, wasteful, and eventually unusable. If your Erlang-to-PHP calls require large PHP applications, open connections to databases, or somehow incur significant initialization overhead, you should maintain pool of reusable PHP processes.

My first complete application for Erlang/OTP is php_app. It manages a pool of persistent PHP processes and provides a simple API to evaluate PHP code. I designed php_app to be robust and easy to use. It’s so easy, in fact, that I now use it to debug WordPress functions from within Erlang. Here is a sample session using start/0 and eval/1:

$ erl
Eshell V5.6.4  (abort with ^G)
1> php:start().
ok
2> php:eval("echo 'Hello, World!';
2>           trigger_error('Uh-oh!');
2>           return array(true, true);").
{ok,<<"Hello, World!">>,
    [{0,true},{1,true}],
    <<"Uh-oh!">>,continue}
3> 

In the resulting tuple we have the output, the return value, and the last error. The atom continue indicates that the PHP process is eligible for reuse, determined by its size in memory after evaluating my code. In the next example I’ll reserve a PHP process to demonstrate persistence and what happens when we hit the memory limit.

3> Ref = php:reserve().
#Ref<0.0.0.52>
4> php:eval("$a = array_fill(0, 200000, rand());
4>           return count($a);", Ref).
{ok,<<>>,200000,<<>>,continue}
5> php:eval("$a = array_merge($a, array_fill(0, 200000, rand()));
5>           return count($a);", Ref).
{ok,<<>>,400000,<<>>,break}
6> php:eval("return count($a);", Ref).
{ok,<<>>,0,<<"Undefined variable:  a">>,continue}
7> php:release(Ref).
ok
8>

The function reserve/0 removes a PHP process from the pool and returns a key that is used in eval/2. Without a key, we can’t be sure that the same PHP process will evaluate our next string of code. Notice the correct return value of 400000 and the atom break which indicates that the PHP process has been restarted because it exceeded the memory usage limit. Our Ref now points to a fresh PHP process. The reservation remains valid.

There are a few other return tuples: one for timeouts, one for parse errors, and one for exits. That last one includes fatal errors. They can’t be trapped. You’ll just have to refer to your error logs. (You do write code with a terminal tailing all your error logs, don’t you?) Here are some more bullet points to keep in mind:

  • Never define a PHP function without first testing function_exists because you will get a fatal error every time. This is by design.
  • Be mindful of escaping quotes and control characters. User input is the enemy. Test.
  • This app was written by an Erlang novice. Do not underestimate its potential destructive power.
  • Even so, it’s in use on a production system that makes lots of PHP calls.
  • The php module has EDoc for all of the API functions.  The HTML version is included for completeness.
  • If you modify the PHPLOOP, you should restart the PHP processes. Try php:restart_all().

The code is here. All you have to do is compile it. I like to make:all(). The configuration is in php.app.

If you would like to contribute changes to the code or documentation, I will be happy to hear from you. My OTP stuff could benefit from a more experienced set of hands.

11 Comments

  1. Hey Andy, do you work on any project using Erlang and PHP or is this just for the fun of it? I actually just thought about how Erlang might be able to help me with certain aspects of the application I am working on today and then a friend passes me this link ; ).

  2. Felix: Yes, WordPress.com’s new firehose is available as an XMPP PubSub node implemented with ejabberd.

  3. They say that the moment you start to look for something Universe will provide it :)

    Our team gravitates to start new development based on Erlang. Amazing technology! Now even WordPress is talking about it. Do you use RabbitMQ, CouchDB ? It would be interesting to get an opinion of High volume traffic site like WordPress about these technologies. So far information is scarce.

  4. I was working on something almost identical to this, but I hadn’t finished it – now you’ve saved me the trouble :). Cheers!

  5. We are probably the first company to use Erlang for our in-text content platform development using Erlang / Mnesia / Yaws… We could not have made a better choice…

  6. Jimmy

     /  April 6, 2009

    I found a new php extension for php/erlang intergrate.

    http://code.google.com/p/mypeb/

  7. Angel Alvarez

     /  April 9, 2009

    Hi Andy

    Very cute concept, You should take a look at FastCGI , There is a php fast cgi implementation with cares about memory and worker processes. You connect to a fastcgo php server with a sockets and dont need to take care of housekeeping of the pool.

    Im learning how you done the eval loop on every port process and i saw the similarities with fast cgi.

    Anyway greatjob, I’ll stay tuned for more erlang pearls!!

  8. Pedram

     /  July 21, 2009

    FastCGI though maybe more efficient, it is not as grid based and server transparent

  9. niahoo

     /  January 30, 2011

    Hello,

    are you still maintaining php_app, or have you got a finished version ? i’m really interested.

    Thanks

  10. laurei

     /  August 22, 2013

    I’d like to get this running after cowboy requests. I’m interested in how it handles $_SERVER[] vars, and how to inject them.

  1. Erlang, PHP, WP — Matt Mullenweg
Follow

Get every new post delivered to your Inbox.

Join 1,670 other followers

%d bloggers like this: