The problem:
I’ve got a class library in PHP. This class library is distributed in a several servers and I want to call them synchronously. The class library has a XMLRPC interface with Zend Framework XMLRPC server.
There is an example class
class Gam_Dummy { /** * foo * * @param integer $arg1 * @param integer $arg2 * @return integer */ function foo($arg1, $arg2) { return $arg1 + $arg2; } }
and the xmlrpc server:
$class = (string) $_GET['class']; $server = new Zend_XmlRpc_Server(); $server->setClass($class); echo $server->handle();
First solution
An easy a fast solution for calling remote interfaces is:
$class = "Gam_Dummy"; $client = new Zend_XmlRpc_Client("http://location/of/xmlrpc/server?class={class}"); echo $client->call('foo', array($arg1, $arg2));
and if we have several remote servers:
$servers = array( 'server1' => 'http://location/of/xmlrpc/server1', 'server2' => 'http://location/of/xmlrpc/server2', 'server3' => 'http://location/of/xmlrpc/server3' ); $class = "Gam_Dummy"; foreach (array_values($servers) as $_server) { $server = "{_server}?class={$class}"; $client = new Zend_XmlRpc_Client($server); echo $client->call('foo', array($arg1, $arg2)); }
Second solution (one remote server):
I want to use the following interface to call my remote class
$class = "Gam_Dummy"; Gam_Dummy::remote("Gam_Dummy", 'server1')->foo($arg1, $arg2);
Why? The answer is because I like to coding with the help of the IDE. If I use the first solution I must remember Gam_Dummy class has a foo function with two parameters. With the second solution if I place the PHPDoc code correctly my IDE will help me showing me the function list of the Gam_Dummy class and even when I type Gam_ IDE will show me all the classes of my repository starting with Gam_. That issue could sound irrelevant for a lot of people but for me is really useful
To get this interface I will change my Gam_Dummy class to:
class Gam_Dummy { /** * foo * * @param integer $arg1 * @param integer $arg2 * @return integer */ function foo($arg1, $arg2) { return $arg1 + $arg2; } /** * Remote interface * * @param string|array $server * @return Gam_Dummy */ static function remote($server) { return new Remote(get_called_class(), $server); } }
And of course Remote class:
class Remote { private $_class = null; private $_server = null; function __construct($class, $server) { $this->_class = $class; $this->_server = $server; } function __call($method, $arguments) { if (class_exists($this->_class)) { $server = "{$this->_server}?class={$this->_class}"; $client = new Zend_XmlRpc_Client($server); return $client->call($method, array($arg1, $arg2)); } } }
Cool. Isn’t it?. But there is a problem if I want to work with two or more remote servers I must write one line of code for each server:
Gam_Dummy::remote('http://location/of/xmlrpc/server1')->foo($arg1, $arg2); Gam_Dummy::remote('http://location/of/xmlrpc/server2')->foo($arg1, $arg2); Gam_Dummy::remote('http://location/of/xmlrpc/server3')->foo($arg1, $arg2);
or may better with the array $servers
foreach (array_values($servers) as $server) { Gam_Dummy::remote($server)->foo($arg1, $arg2); }
Third solution for multiple remote servers:
I would like to use this interface instead of solution two with a foreach for multiple servers:
$servers = array( 'server1' => 'http://location/of/xmlrpc/server1', 'server2' => 'http://location/of/xmlrpc/server2', 'server3' => 'http://location/of/xmlrpc/server3' ); Gam_Dummy::remote($servers)->foo($arg1, $arg2);
so I change Remote class to:
class Remote { private $_class = null; private $_server = null; function __construct($class, $server) { $this->_class = $class; $this->_server = $server; } function __call($method, $arguments) { $out = array(); if (is_array($this->_server)) { foreach ($this->_server as $key => $_server) { $server = "{$_server}?class={$this->_class}"; $client = new Zend_XmlRpc_Client($server); $out[$key] = $client->call($method, $arguments); } } else { $server = "{$this->_server}?class={$this->_class}"; $client = new Zend_XmlRpc_Client($server); $out = $client->call($method, $arguments); } return $out; } }
http://www.zendcasts.com/forum/topic/193/zendxmlrpcserver/
“foreach (array_values($servers) as $_server)” is kind of redundant. You can just use “foreach ($servers as $_server)”
True. Maybe it’s a personal habit. When I use “as $key => $value” I like to iterate with a asociative arrays and when I only need $value I use array_values (or array_keys if I need keys). But in fact it’s a bit redundant.