29.2. Zend_XmlRpc_Client
Prev | Chapter 29. Zend_XmlRpc | Next

## 29.2. Zend_XmlRpc_Client

### 29.2.1. Introduction

The 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.

### 29.2.2. 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.

Example 29.1. XML-RPC Method Call

    <?php

    require_once 'Zend/XmlRpc/Client.php';

    $client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

    echo $client->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:

Example 29.2. XML-RPC Method Call with Parameters

    <?php

    require_once 'Zend/XmlRpc/Client.php';

    $client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

    $arg1 = 1.1;
    $arg2 = 'foo';

    $result = $client->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.

### 29.2.3. 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).

#### 29.2.3.1. 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:

Table 29.1. PHP and XML-RPC Type Conversions

PHP Native Type | XML-RPC Type
--- | ---
integer | int
double | double
boolean | boolean
string | string
array | array
associative array | struct
object | array

#### 29.2.3.2. `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()`.

Table 29.2. `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`
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`
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 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`.

### 29.2.4. 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.

Example 29.3. Proxy the Default Namespace

    <?php

    require_once 'Zend/XmlRpc/Client.php';

    $client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

    $server = $client->getProxy();           // Proxy the default namespace

    $hello = $server->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:

Example 29.4. Proxy Any Namespace

    <?php

    require_once 'Zend/XmlRpc/Client.php';

    $client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

    $test  = $client->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()`.

### 29.2.5. 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.

#### 29.2.5.1. 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.

Example 29.5. Handling HTTP Errors

    <?php

    require_once 'Zend/XmlRpc/Client.php';

    $client = new Zend_XmlRpc_Client('http://foo/404');

    try {

        $client->call('bar', array($arg1, $arg2));

    } catch (Zend_XmlRpc_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.

#### 29.2.5.2. 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.

Example 29.6. Handling XML-RPC Faults

    <?php

    require_once 'Zend/XmlRpc/Client.php';

    $client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

    try {

        $client->call('badMethod');

    } catch (Zend_XmlRpc_FaultException $e) {

        // $e->getCode() returns 1
        // $e->getMessage() returns "Unknown method"

    }

    ?>

When the `call()` method is used to make the request, the `Zend_XmlRpc_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`.

### 29.2.6. 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.

### 29.2.7. 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:

Example 29.7. Processing Request to Response

    <?php

    require_once 'Zend/XmlRpc/Client.php';

    $client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

    $request = new Zend_XmlRpc_Request();
    $request->setMethod('test.sayHello');
    $request->setParams(array('foo', 'bar'));

    $client->doRequest($request);

    // $server->getLastRequest() returns instanceof Zend_XmlRpc_Request
    // $server->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.

### 29.2.8. 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.

Prev | Up | Next
Chapter 29. Zend_XmlRpc | Home | 29.3. Zend_XmlRpc_Server