Czasem potrzebujemy użyć kawałka kodu PHP będącego na innej maszynie. Sięgamy wtedy po xmlrpc czy inne wynalazki takie jak CURL czy zdalne file_get_contents. Na nasze głowy spada wtedy przetworzenie danych przed wysłaniem i po odebraniu. Do tego jedno wywołanie z reguły oznacza całkowite wykonanie kodu po drugiej stronie. A czasem przydałoby się coś na raty zrobić.
Przedstawione poniżej klasy pozwalają na zdefiniowanie serwera obiektów. Obiekt taki można z poziomu klienta utworzyć na serwerze i korzystać potem z niego jak z obiektu lokalnego, wywoływać metody, odwoływać się do pól. Każda taka operacja okupiona jest wywołaniem HTTP do serwera, więc należy tego używać z umiarem.
Oto kod klasy klienta:
class RPC_Client
{
private $server = false;
private $token = false;
function __construct($server=false)
{
if($server)
{
$this->server = $server;
}
$ret=$this->__call('__construct',array());
return $ret;
}
function __call($method,$params)
{
$call['method'] = $method;
$call['params'] = $params;
$call['token'] = $this->token;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->server);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, array('call'=>serialize($call)));
$ret_string = curl_exec($ch);
if(curl_errno($ch))
{
print curl_error($ch);
}
curl_close($ch);
$ret = unserialize($ret_string);
$this->token = $ret['token'];
if(isset($ret['exception']))
{
throw unserialize($ret['exception']);
}
return $ret['data'];
}
function __set($key,$value)
{
$params['key'] = $key;
$params['value'] = $value;
return $this->__call('__set',$params);
}
function __get($key)
{
$params['key'] = $key;
return $this->__call('__get',$params);
}
}
Kod klasy serwera:
class RPC_Server
{
private $server = false;
private $data = array();
function __construct()
{
$ret=array();
$query_str = trim($_SERVER['QUERY_STRING']);
$query = explode('=',$query_str);
$class = array_shift($query);
$call = unserialize(stripslashes($_POST['call']));
$method = $call['method'];
$params = $call['params'];
$token = $call['token'];
// odkomentowac, jesli nie jest zdefiniowana funkcja __autoload
// if(!class_exists($class))
// {
// require_once('class/'.$class.'.class.php');
// }
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ("Could not connect");
if(!$token)
{
$token = md5(getmypid().time());
$obj = new $class ();
}
else
{
$obj = $memcache->get($token);
}
$ret['token'] = $token;
try
{
$ret['data'] = @call_user_func_array(array(&$obj,$method),$params);
}
catch(Exception $e)
{
$ret['exception'] = serialize($e);
}
$memcache->set($token,$obj);
echo serialize($ret);
}
}
Klasa macierzysta dla klas serwowanych:
class RPC
{
private $data = array();
function __set($key,$value)
{
return $this->data[$key] = $value;
}
function __get($key)
{
return $this->data[$key];
}
}
Przykład klasy serwowanej:
class RPC_Test extends RPC
{
public $aaa = 10;
function __construct()
{
return 'RPC_Test ready';
}
function mul($x,$y)
{
return $x*$y;
}
function except()
{
throw new Exception('Hello!');
}
function spl($string)
{
return explode(' ',$string);
}
}
Oraz serwera:
$srv = new RPC_Server();
I klienta:
$srv = new RPC_Client('http://localhost/~zibi/src/RPCServer.php?RPC_Test');
echo $srv->mul(10,11));
$srv->val = 10;
echo $srv->val;
$srv->val += 20;
echo $srv->val;
$srv->tekst='jaki wiekszy tekscik coby bylo co obrabiac';
echo var_dump($srv->spl($srv->tekst));