File: //usr/local/share/hostingplatform/esrpc.php
<?php
class ESRPCConnection {
public $proc;
public $out;
public $in;
public $target = NULL;
public $requestid = 1;
public $debug = 0;
public function connect($cmd=NULL) {
if($cmd) {
$this->debug = (
isset($_ENV["ESRPC_DEBUG"])
? true == $_ENV["ESRPC_DEBUG"]
: false
);
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "php://stderr", "w")
);
$this->proc = proc_open($cmd, $descriptorspec, $pipes);
if(!$this->proc) {
throw new ESRPCProtocolException('Failed to open connection');
}
$this->in = $pipes[1];
$this->out = $pipes[0];
if (!$this->ping()) {
throw new ESRPCProtocolException('Failed to ping');
}
} else {
$this->in = STDIN;
$this->out = STDOUT;
}
}
public function close() {
fclose($this->out);
fread($this->in, 1);
proc_close($this->proc);
}
public function get() {
return new ESRPCRemoteObject($this);
}
public function setTarget($target) {
$this->target = $target;
}
public function call($method, $arguments) {
$request = array(
"id" => "x" . $this->requestid++,
"method" => $method,
"params" => $arguments
);
$this->write_message($request);
for(;;) {
if (!$this->read_message($message)) break;
if(array_key_exists("result", $message)) {
return $message["result"];
} else if(array_key_exists("error", $message)) {
$e = new ESRPCRemoteException($message["error"]["message"]);
if($message["error"]["longmessage"]) {
$e->setLongMessage($message["error"]["longmessage"]);
}
throw $e;
} else {
$this->handle_request($message);
}
}
}
public function ping() {
$request = array(
"id" => "x" . $this->requestid++,
"ping" => 1
);
$this->write_message($request);
for(;;) {
if (!$this->read_message($message)) break;
if(
array_key_exists("pong", $message)
&& $message['id'] == $request['id']
&& $message['pong'] == $request['ping']
) {
return true;
} else {
$this->handle_request($message);
}
}
return false;
}
public function serve() {
try {
while($this->read_message($request)) {
$this->handle_request($request);
}
} catch (Exception $e) {
$response = array(
"error" => array(
"message" => $e->getMessage(),
"longmessage" => $e->getMessage() . "\n" .
$e->getTraceAsString()));
$this->write_message($response);
}
}
public function handle_request($request) {
$response = array("id" => $request["id"]);
if(array_key_exists("ping", $request)) {
$response["pong"] = $request["ping"];
} else if(array_key_exists("method", $request)) {
if(!$this->target) {
throw new ESRPCProtocolException('No call target set');
}
try {
$response["result"] = call_user_func_array(
array($this->target, $request["method"]),
$request["params"]);
} catch (Exception $e) {
$response["error"] = array(
"message" => $e->getMessage(),
"longmessage" => $e->getMessage() . "\n" .
$e->getTraceAsString());
}
} else {
throw new ESRPCProtocolException('Unhandled message received');
}
$this->write_message($response);
}
public function write_message($message) {
if($this->debug) {
fwrite(STDERR, " >>> " . json_encode($message) . "\n");
}
$message_data = json_encode($message);
if(!fwrite($this->out, sprintf("%08X", strlen($message_data)))) {
throw new ESRPCProtocolException('Failed to send message length');
}
if(!fwrite($this->out, $message_data)) {
throw new ESRPCProtocolException('Failed to send message body');
}
}
public function read_message(&$message) {
$message = NULL;
$message_length = fread($this->in, 8);
if($message_length === false) {
throw new ESRPCProtocolException(
'Failed to read ESRPC message: read error in length');
} if(strlen($message_length) == 0) {
return FALSE;
} else if(strlen($message_length) != 8) {
throw new ESRPCProtocolException(
'Failed to read ESRPC message: unexpected EOF in length');
}
if(!preg_match("/^[0-9a-fA-F]{8}$/", $message_length)) {
throw new ESRPCProtocolException(
'Illegal message length: ' . $message_length);
}
$message_length = hexdec($message_length);
$message_data = "";
while(strlen($message_data) < $message_length) {
$buf = fread($this->in, $message_length - strlen($message_data));
if($buf === false) {
throw new ESRPCProtocolException(
'Failed to read ESRPC message: read error in body');
} else if (strlen($buf) < 1) {
throw new ESRPCProtocolException(
'Failed to read ESRPC message: Unexpected EOF in body');
}
$message_data .= $buf;
}
$message = json_decode($message_data, TRUE);
if(!is_array($message) || !isset($message['id'])) {
throw new ESRPCProtocolException('Invalid message received: ' . $message_data);
}
if($this->debug) {
fwrite(STDERR, " <<< " . json_encode($message) . "\n");
}
return TRUE;
}
}
class ESRPCRemoteObject {
public $connection;
public function __construct($connection) {
$this->connection = $connection;
}
public function __call($method, $arguments) {
return $this->connection->call($method, $arguments);
}
}
class ESRPCProtocolException extends Exception {}
class ESRPCRemoteException extends Exception {
public $longmessage = NULL;
public function setLongMessage($message) {
$this->longmessage = $message;
}
public function getLongMessage() {
if($this->longmessage) {
return $this->longmessage;
} else {
return $this->getMessage();
}
}
}