Zbigniew ‘zibi’ Jarosik Ecie-pecie o wszechświecie
  • PHP RemoteObjects

    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));
    No Comments