31 Jan

persistent web apps in PHP

Recently, I’ve been working in an environment which uses a lot of Java, so I got to reflect on the differences between PHP and Java.

One of the more obvious differences is that in Java’s case, it is possible to start up a persistent server which runs and runs even if there is no client keeping it alive with http requests. This is in contrast with the usual PHP setup, where each client request sparks up a new instance of a “page”, which is only alive for the duration of the request.

The problem with the stateless method (where instances are alive only temporarily) is that this makes it tricky to build larger application which rely on persistent data storage or potentially time-consuming algorithms.

As an example, consider the case of an IRC program, where it is required that the server keep a store of messages, which are doled out on request to a web client.

With a stateful engine, a Java service, for example, the messages can be kept in-memory, and only one instance of the message server needs to exist. Each request is handled pretty much instantly, and there is no pause while database connections are set up and polled.

With a stateless system, the default PHP setup, it is required that the messages be stored external to PHP, as PHP’s memory lasts only as long as the HTTP request. This means that every request from the client involves loading up the script, parsing, connecting to DB, retrieving, updating, and sending back to client. A lot slower than the Java method, I think you’ll agree.

This disturbed me as I really like PHP, and Java’s /such/ an awkward language to work with (every change involves recompiling, redeploying, and a lot of waiting).

Thinking about it on the way to work today, I realised that PHP /is/ capable of bing stateful. You just have to remember that PHP is not confined to the web – you can have a CLI engine running in PHP that is persistent.

The key to this is that the CLI script keeps the message store in-memory (like the Java service), and communicates with the client through a web-based PHP “gateway”.

IRC server <--> PHP Gateway <--> Client

The gateway step is not actually required, but helps to filter out the rubbish that might come through if the IRC server was connected directly to the net.

You start the IRC server running as a script from the console (php server.php), and leave it running. CLI scripts have no timeout, so it will keep running until you manually turn it off.

The client requests information from the gateway, which connects briefly to the running instance of the server through a socket, and returns the message to the client.

To illustrate this, try this script out from the console:

<?php

$port=10001;
$max_messages=3;

// initialise chat messages

$msgs=array();

// open socket

$s_socket=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_bind($s_socket,'127.0.0.1',$port);
socket_listen($s_socket,10);

// start the msg loop

while($c_socket=socket_accept($s_socket)){

  // what messages are being retrieved?
  $from=socket_read($c_socket,1024);
  if($from=='')$from=0;
  else if((int)$from != $from){
    // syntax error - first line should be a number or blank
    socket_close($c_socket);
    continue;
  }
  else $from=(int)$from;

  // send retrieved messages to client
  $retrieved=array();
  $highest=0;
  for($i=0;$i<count($msgs);++$i){
    $highest=$msgs[$i][0];
    if($highest<$from)continue;
    $retrieved[]='["num":'.$msgs[$i][0].',"msg":"'.addslashes($msgs[$i][1]).'"]';
  }
  socket_write($c_socket,'{'.join(',',$retrieved).'}');

  // get sent message
  $msg='';
  do{
    $m=socket_read($c_socket,1024);
    $msg.=$m;
  }while($m);

  // add sent message to message store
  if($msg!='')$msgs[]=array($highest+1,$msg);

  // restrict the size of the message store
  while(count($msgs)>$max_messages){
    array_shift($msgs);
  }

  socket_close($c_socket);
}

socket_close($s_socket);

?>

Run the above in one terminal, then open another terminal (or tab) and connect to it (telnet 127.0.0.1 10001). Press enter (to indicate you want all existing messages), then type “test”, press enter, then close the connection (^], then quit).

The first time you do it, you’ll get an empty object returned ({}). The second time, you’ll receive the first message that was sent. the third time, you’ll receive the first two messages.

This has some really cool implications – you can now write a continuously updating programming which doesn’t require cron and the inevitable timely bashing that it does to the system. Cron is periodic, but a lot of people choose the same periods for everything. Imagine what would happen to your email server if everyone chose to download their email at exactly 1pm every day but no other time? Instead, a continually running engine allows a more smooth flow to develop.

It also means that your application can be kept slim, with the server and gateway code kept distinctly separate. This has the added value that it speeds up the whole thing – when connecting to the server, the server is already running, so the only code needed to load up and run is the code that retrieves the message via the socket and passes it on, and with a caching webserver (Zend, for example), this will be faster again.

The above was just one example of how to do this. There may be others. This is most likely not a new idea to a lot of people, but I didn’t see it written anywhere so I thought I needed to write it.

6 thoughts on “persistent web apps in PHP

  1. well I’m thinking of writing the logic for my robot using this idea. it may be tricky because there is no documented threading support in PHP, but that can be gotten around with some creative sockets-based work.

  2. Sure, but then why not use a real language like Python (or even Ruby), which already has available open-source stateful web frameworks, and is miles ahead of PHP in every respect (power, ease, reliability, quality of language code, quality of libraries, quality of available open source code, etc) ? The only advantage of PHP is easily hacking some web pages thanks to webserver integration…

  3. interesting. how do you define “a real language”? what about Java? or is that not “a real language”? is “a real language” defined as “whatever I’m using right now, and nothing else”?

    one person I know looks on everything that I do with disdain. he hates PHP, hates JavaScript, and hates anything which is not written exactly the way he would write it. when writing PHP, he insisted the only way to write anything was through the Zend framework (which meant that the simplest things take days to write). when writing Java, he insists that anyone that is not using MVC is an idiot.

    My own personal opinion is that every language has it’s pros and cons. PHP is great for me because I find it very comfortable and I have never found a project that I couldn’t solve with it. obviously that argument would not be right for you, and I’m okay with that – use whatever you like, as long as it works.

    this blog is about PHP, JavaScript, and some other stuff. I don’t write about languages that I don’t work with. if you don’t like PHP, then don’t use it.

  4. I will need something like this. I need to created persistent web service in php. I have lot of clients which are using my ws wroten in php. Problem is that app must for each request connect to my ws (make new connection, send, receive data, close connection). I’m want to do some daemon which will send/receive requests. Eg will accept incoming connection create child which will handle ws requests. But how to do it (in php or perl + php ws)

  5. Pingback: ??????? » [Web] ????

Comments are closed.