Zend_XmlRpc_Client
Introduction
Zend Framework provides support for consuming remote XML-RPC
services as a client in the Zend_XmlRpc_Client
package. Its major features include automatic type conversion
between PHP and XML-RPC, a server proxy object,
and access to server introspection capabilities.
Method Calls
The constructor of Zend_XmlRpc_Client receives the
URL of the remote XML-RPC server endpoint as its
first parameter. The new instance returned may be used to call any number of
remote methods at that endpoint.
To call a remote method with the XML-RPC client, instantiate it
and use the call() instance method. The code sample
below uses a demonstration XML-RPC server on the Zend Framework
website. You can use it for testing or exploring the
Zend_XmlRpc components.
XML-RPC Method Call
call('test.sayHello');
// hello
]]>
The XML-RPC value returned from the remote method call will be
automatically unmarshaled and cast to the equivalent PHP native
type. In the example above, a PHP String is returned
and is immediately ready to be used.
The first parameter of the call() method receives the
name of the remote method to call. If the remote method requires
any parameters, these can be sent by supplying a second, optional
parameter to call() with an Array of
values to pass to the remote method:
XML-RPC Method Call with Parameters
call('test.sayHello', array($arg1, $arg2));
// $result is a native PHP type
]]>
If the remote method doesn't require parameters, this optional
parameter may either be left out or an empty array()
passed to it. The array of parameters for the remote method can
contain native PHP types, Zend_XmlRpc_Value
objects, or a mix of each.
The call() method will automatically convert the
XML-RPC response and return its equivalent PHP
native type. A Zend_XmlRpc_Response object for the return value
will also be available by calling the getLastResponse()
method after the call.
Types and Conversions
Some remote method calls require parameters. These are given to
the call() method of Zend_XmlRpc_Client
as an array in the second parameter. Each parameter may be
given as either a native PHP type which will be automatically
converted, or as an object representing a specific XML-RPC type
(one of the Zend_XmlRpc_Value objects).
PHP Native Types as Parameters
Parameters may be passed to call() as native
PHP variables, meaning as a String,
Integer, Float,
Boolean, Array, or an
Object. In this case, each PHP native type will
be auto-detected and converted into one of the XML-RPC types
according to this table:
PHP and XML-RPC Type Conversions
PHP Native Type
XML-RPC Type
integer
int
Zend_Crypt_Math_BigInteger
i8
double
double
boolean
boolean
string
string
null
nil
array
array
associative array
struct
object
array
Zend_Date
dateTime.iso8601
DateTime
dateTime.iso8601
What type do empty arrays get cast to?
Passing an empty array to an XML-RPC method is problematic,
as it could represent either an array or a struct.
Zend_XmlRpc_Client detects such conditions and
makes a request to the server's
system.methodSignature method to determine the
appropriate XML-RPC type to cast to.
However, this in itself can lead to issues. First off,
servers that do not support
system.methodSignature will log failed
requests, and Zend_XmlRpc_Client will resort to
casting the value to an XML-RPC array type. Additionally,
this means that any call with array arguments will result in
an additional call to the remote server.
To disable the lookup entirely, you can call the
setSkipSystemLookup() method prior to making
your XML-RPC call:
setSkipSystemLookup(true);
$result = $client->call('foo.bar', array(array()));
]]>
Zend_XmlRpc_Value Objects as Parameters
Parameters may also be created as Zend_XmlRpc_Value
instances to specify an exact XML-RPC type. The primary reasons
for doing this are:
When you want to make sure the correct parameter
type is passed to the procedure (i.e. the
procedure requires an integer and you may get it
from a database as a string)
When the procedure requires base64 or
dateTime.iso8601 type (which doesn't exists as a
PHP native type)
When auto-conversion may fail (i.e. you want to
pass an empty XML-RPC struct as a parameter. Empty
structs are represented as empty arrays in PHP
but, if you give an empty array as a parameter it
will be auto-converted to an XML-RPC array since
it's not an associative array)
There are two ways to create a Zend_XmlRpc_Value
object: instantiate one of the Zend_XmlRpc_Value
subclasses directly, or use the static factory method
Zend_XmlRpc_Value::getXmlRpcValue().
Zend_XmlRpc_Value Objects for XML-RPC Types
XML-RPC Type
Zend_XmlRpc_Value Constant
Zend_XmlRpc_Value Object
int
Zend_XmlRpc_Value::XMLRPC_TYPE_INTEGER
Zend_XmlRpc_Value_Integer
i8
Zend_XmlRpc_Value::XMLRPC_TYPE_I8
Zend_XmlRpc_Value_BigInteger
ex:i8
Zend_XmlRpc_Value::XMLRPC_TYPE_APACHEI8
Zend_XmlRpc_Value_BigInteger
double
Zend_XmlRpc_Value::XMLRPC_TYPE_DOUBLE
Zend_XmlRpc_Value_Double
boolean
Zend_XmlRpc_Value::XMLRPC_TYPE_BOOLEAN
Zend_XmlRpc_Value_Boolean
string
Zend_XmlRpc_Value::XMLRPC_TYPE_STRING
Zend_XmlRpc_Value_String
nil
Zend_XmlRpc_Value::XMLRPC_TYPE_NIL
Zend_XmlRpc_Value_Nil
ex:nil
Zend_XmlRpc_Value::XMLRPC_TYPE_APACHENIL
Zend_XmlRpc_Value_Nil
base64
Zend_XmlRpc_Value::XMLRPC_TYPE_BASE64
Zend_XmlRpc_Value_Base64
dateTime.iso8601
Zend_XmlRpc_Value::XMLRPC_TYPE_DATETIME
Zend_XmlRpc_Value_DateTime
array
Zend_XmlRpc_Value::XMLRPC_TYPE_ARRAY
Zend_XmlRpc_Value_Array
struct
Zend_XmlRpc_Value::XMLRPC_TYPE_STRUCT
Zend_XmlRpc_Value_Struct
Automatic Conversion
When building a new Zend_XmlRpc_Value
object, its value is set by a PHP type. The
PHP type will be converted to the specified type using
PHP casting. For example, if a string is given as a
value to the Zend_XmlRpc_Value_Integer
object, it will be converted using
(int)$value.
Server Proxy Object
Another way to call remote methods with the XML-RPC client is to
use the server proxy. This is a PHP object that proxies a remote
XML-RPC namespace, making it work as close to a native
PHP object as possible.
To instantiate a server proxy, call the getProxy()
instance method of Zend_XmlRpc_Client. This will
return an instance of Zend_XmlRpc_Client_ServerProxy.
Any method call on the server proxy object will be forwarded to
the remote, and parameters may be passed like any other PHP
method.
Proxy the Default Namespace
getProxy(); // Proxy the default namespace
$hello = $service->test->sayHello(1, 2); // test.Hello(1, 2) returns "hello"
]]>
The getProxy() method receives an optional argument
specifying which namespace of the remote server to proxy. If it
does not receive a namespace, the default namespace will be
proxied. In the next example, the 'test' namespace
will be proxied:
Proxy Any Namespace
getProxy('test'); // Proxy the "test" namespace
$hello = $test->sayHello(1, 2); // test.Hello(1,2) returns "hello"
]]>
If the remote server supports nested namespaces of any depth,
these can also be used through the server proxy. For example, if
the server in the example above had a method
test.foo.bar(), it could be called as
$test->foo->bar().
Error Handling
Two kinds of errors can occur during an XML-RPC method call:
HTTP errors and XML-RPC faults. The
Zend_XmlRpc_Client recognizes each and provides the ability
to detect and trap them independently.
HTTP Errors
If any HTTP error occurs, such as the remote
HTTP server returns a 404 Not Found, a
Zend_XmlRpc_Client_HttpException will be thrown.
Handling HTTP Errors
call('bar', array($arg1, $arg2));
} catch (Zend_XmlRpc_Client_HttpException $e) {
// $e->getCode() returns 404
// $e->getMessage() returns "Not Found"
}
]]>
Regardless of how the XML-RPC client is used, the
Zend_XmlRpc_Client_HttpException will be thrown
whenever an HTTP error occurs.
XML-RPC Faults
An XML-RPC fault is analogous to a PHP
exception. It is a special type returned from an XML-RPC method
call that has both an error code and an error message. XML-RPC
faults are handled differently depending on the context of how the
Zend_XmlRpc_Client is used.
When the call() method or the server
proxy object is used, an XML-RPC fault will result in a
Zend_XmlRpc_Client_FaultException being thrown.
The code and message of the exception will map directly to
their respective values in the original XML-RPC fault
response.
Handling XML-RPC Faults
call('badMethod');
} catch (Zend_XmlRpc_Client_FaultException $e) {
// $e->getCode() returns 1
// $e->getMessage() returns "Unknown method"
}
]]>
When the call() method is used to make the
request, the Zend_XmlRpc_Client_FaultException will be
thrown on fault. A Zend_XmlRpc_Response object
containing the fault will also be available by calling
getLastResponse().
When the doRequest() method is used to make the
request, it will not throw the exception. Instead, it will
return a Zend_XmlRpc_Response object returned
will containing the fault. This can be checked with
isFault() instance method of
Zend_XmlRpc_Response.
Server Introspection
Some XML-RPC servers support the de facto introspection methods
under the XML-RPC system. namespace.
Zend_XmlRpc_Client provides special support for servers with
these capabilities.
A Zend_XmlRpc_Client_ServerIntrospection instance may be
retrieved by calling the getIntrospector() method of
Zend_XmlRpcClient. It can then be used to perform introspection
operations on the server.
From Request to Response
Under the hood, the call() instance method of
Zend_XmlRpc_Client builds a request object
(Zend_XmlRpc_Request) and sends it to another method,
doRequest(), that returns a response object
(Zend_XmlRpc_Response).
The doRequest() method is also available for use directly:
Processing Request to Response
setMethod('test.sayHello');
$request->setParams(array('foo', 'bar'));
$client->doRequest($request);
// $client->getLastRequest() returns instanceof Zend_XmlRpc_Request
// $client->getLastResponse() returns instanceof Zend_XmlRpc_Response
]]>
Whenever an XML-RPC method call is made by the client through any
means, either the call() method,
doRequest() method, or server proxy, the last request
object and its resultant response object will always be available
through the methods getLastRequest() and
getLastResponse() respectively.
HTTP Client and Testing
In all of the prior examples, an HTTP client was never specified.
When this is the case, a new instance of
Zend_Http_Client will be created with its default
options and used by Zend_XmlRpc_Client automatically.
The HTTP client can be retrieved at any time with the
getHttpClient() method. For most cases, the default
HTTP client will be sufficient. However, the
setHttpClient() method allows for a different
HTTP client instance to be injected.
The setHttpClient() is particularly useful for unit testing.
When combined with the Zend_Http_Client_Adapter_Test, remote
services can be mocked out for testing. See the unit tests for
Zend_XmlRpc_Client for examples of how to do this.