This is mostly an experiment, but I do think it does have some practical uses.
The idea is to be able to patch a PHP script at runtime without have to reload the request.
For example, let’s say I wrote a WebSocket server in PHP, and this server is running the following class…
class MyServerApp extends WebSocketServerApp {
protected $clients = array();
public function onConnect($client){
$this->clients[$client->getId()] = $client;
}
public function onDisconnect($client){
unset($this->clients[$client->getId()]);
}
public function onData($client, $data){
$client->send($data); // perform echo functionality
}
}
Basically, the server creates a single instance of MyServerApp class and calls it appropriately. The above app is an echo server; it responds to all client request with exactly what they asked for.
Now let’s say I modified the server source code and want to keep the existing server running, but change behaviour (so as to not loose the existing clients). The app server conveniently has an onTick() event, which we can use to check changes to the source code:
class MyServerApp extends WebSocketServerApp {
// the existing code from above goes here
/**
* @var integer Timestamp of when the server was last patched.
*/
public $last_patch = 0;
public function __construct(){
$this->last_patch = time();
}
public function onTick(){
if($this->last_patch < filemtime(__FILE__)){
// include __FILE__;
}
}
}
The patching check will probably work nicely, but the actual patching method won’t (it is currently commented, by the way).
The main reason is that the server class has already been defined.
So how would you do the actual patching? Somehow overwrite functions, or classes?
Possible Solutions
- Use a better suited language (such as js/node.js) which allows overwriting (@teresko)
- Use classkit (and/or alternatives) to overwrite PHP functions/classes (@MatějZábský)
- Offload server logic to anonymous functions and simply overwrite such functions*
- Rename duped classes and load them normally (definitely leaks memory though) (@chris)
[*] server.php would look like:
$server['onData'] = function(){ /* new function body */ };
Notes
On the other hand, this architecture poses some issues that really need to be tended to:
- permanent damage to the running server
- memory leaks (some resources will get leaked, probably including functions)
- (does PHP have a GC for functions?)
You may want to have a look at classkit (its function classkit_method_redefine and classkit_import in particular).
But that’s really horrible way to go. Are you sure you can’t afford to have a millisecond down-time while restarting the script (as everyone does)?
This may also not be sustainable in long term (I wouldn’t be surprised if the overwritten method was just leaked). Also, I’m not sure if the tick callback is really built for use on production server (it may have some performance overhead).
EDIT:
Sadly, there is rather limited number of articles about PHP’s garbage collector. Here is one (it is a part of series on performance optimizations in PHP), but it doesn’t go into details on how functions or types are treated.
From my experience, PHP scripts running continuously for days need to be restarted every few days (I use Cron-job which tries to start the script every few minutes along with a lock file), because after that, the script becomes less and less stable.