Remote-Procedure-Call for php

Sometimes you've to call a function from one php-process to another. Polling mechanisms are quite boring, so here's a fast RPC implementation.

Imagine a PHP-script running infinite in background. Now you'd like to send it some data, call functions in it and so on.

Basic example

the server

<?php
class rpc_function {
	var $name='rpc_test';
	var $arg=array('foo'=>'ok','bar'=>'123');
}
 
@unlink('/tmp/php-socket');
$socket = socket_create(AF_UNIX, SOCK_SEQPACKET, 0);
socket_bind($socket,'/tmp/php-socket');
socket_listen($socket);
 
$rpc=new rpc();
 
if (!$socket) {
	echo "$errstr ($errno)<br />\n";
} else {
	while (true) {
		while ($conn = socket_accept($socket)) {
			$data=socket_read($conn,1024);
			$funct=unserialize($data);
			$retval=false;
			if (method_exists($rpc,$funct->name)) {
				$retval=call_user_func_array(array('rpc',$funct->name),$funct->arg);
			}
			$data=serialize($retval);
			socket_write($conn, $data,strlen($data));
			socket_shutdown($conn);
		}
	}
}
 
class rpc {
	function rpc_test($foo,$bar) {
		echo "rpc_test($foo,$bar) called... \n";
		return array('result'=>'good');
	}
}
?>

the client

<?php
class rpc_function {
	var $name='rpc_test';
	var $arg=array('foo'=>'ok','bar'=>'123');
}
 
$socket = socket_create(AF_UNIX, SOCK_SEQPACKET, 0);
 
if (socket_connect($socket,'/tmp/php-socket')) {
	//send the call
	$data=serialize(new rpc_function() );
	socket_write($socket,$data,strlen($data));
 
	//retrieve retval
	$response=socket_read($socket,1024,PHP_BINARY_READ);
	$retval=unserialize($response);
	echo "result: \n";
	print_r($retval);
}
socket_close($socket);

via a class...

include file

<?php
 
class RPC_call{
	var $name;
	var $arg;
	function RPC_call($name,$args=array()) {
		$this->name=$name;
		$this->args=$args;
	}
}
 
class RPC {
	var $rpc_class;
	var $socket;
	var $connected=false;
	var $connections=10;
 
	function RPC($address,&$rpc_class) {
		$this->rpc_class=&$rpc_class;
		$this->address=$address;
		$this->socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
		return $this->socket;
	}
 
	function listen(){
		unlink($this->address);
		socket_bind($this->socket,$this->address);
		socket_listen($this->socket);
 
		$sockets[0]=$this->socket;
		while (true) {
			//copy our array cos select modifies $read
			$read=$sockets;
			$ready = socket_select($read,$write=null,$except=null,1);
			foreach($read as $socket) {
				if ($socket==$sockets[0]) {
					//read requests on master are connects
					$sockets[]=socket_accept($this->socket);
				} else {
					$data = socket_read($socket, 1024);
					if ($data == null) {
						//means socket has been closed
						$key=array_search($socket,$sockets);
						unset($sockets[$key]);
						socket_close($socket);
					} else {
						$funct=unserialize($data);
						$retval=false;
						if (method_exists($this->rpc_class,$funct->name)) {
							$retval=call_user_func_array(array($this->rpc_class,$funct->name),$funct->arg);
						}
						$data=serialize($retval);
						socket_write($socket, $data);
					}
				}
			}
		}
	}
	function call($func,$args=array()){
 
		$waiting=socket_select($read=array($this->socket),$write=null,$except=null,0);
		if ($waiting) {
			$connected=socket_connect($this->socket,$this->address);
		} else {
			$connected=true;
		}
		if ($connected) {
			//send the call
			$data=serialize(new RPC_call($func,$args));
			socket_write($this->socket,$data,strlen($data));
 
			//retrieve retval
			$response=socket_read($this->socket,1024);
			//socket_shutdown($this->socket);
			return unserialize($response);
		}
	}
}
 
class RPC_class {
	var $_rpc=false;
	function _bind(&$rpc_class){
		$this->_rpc=$rpc_class;
	}
 
	public function __call($method, $args) {
		return $this->_rpc->call($method, $args);
	}
}

example server

<?php
 
if(!defined('INC_PATH')) define('INC_PATH',realpath(dirname(__FILE__).'/../inc/').'/');
 
require_once(INC_PATH.'rpc/unix-sockets.php');
 
class myCalls{
	function test(){
		echo "I'm the server!\n";
		return "response for the client...\n";
	}
	function test2(){
		echo "I'm still the server!\n";
		return "another response for the client...\n";
	}
 
}
 
$rpc=new RPC('/tmp/php-socket',new myCalls());
$rpc->listen();
 
?>

example client

Since RPC_class overloads __call all functions in myCalls are unused, only their name and arguments are used.

<?php
 
if(!defined('INC_PATH')) define('INC_PATH',realpath(dirname(__FILE__).'/../inc/').'/');
 
require_once(INC_PATH.'rpc/unix-sockets.php');
 
class myCalls extends RPC_class{
	function test(){
		echo "I'm the client!\n";
	}
	function test2(){
		echo "I'm still the client!\n";
	}
 
}
 
$rpc=new RPC('/tmp/php-socket',new myCalls());
echo $rpc->call('test');
echo $rpc->call('test2');
 
?>
 
wiki/projects/php/rpc.txt · Last modified: 2007/05/21 00:01 (external edit)
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki