29.2. Zend_XmlRpc_Client
Пред. | Глава 29. Zend_XmlRpc | След.

## 29.2. Zend_XmlRpc_Client

### 29.2.1. Введение

Zend Framework поддерживает клиентское использование удаленных XML-RPC сервисов через пакет `Zend_XmlRpc_Client`. Его основные возможности включают в себя автоматическое преобразование типов между PHP и XML-RPC, прокси-объект сервера и доступ к средствам интроспекции на сервере.

### 29.2.2. Вызов методов

Конструктор `Zend_XmlRpc_Client` принимает URL удаленного XML-RPC сервера в качестве первого параметра. Новый экземпляр класса может использоваться для вызова любых удаленных методов этого сервера.

Для вызова удаленного метода через клиентa XML-RPC инстанцируйте его и используйте его метод `call()`. В примере ниже используется демонстрационный XML-RPC сервер на веб-сайте Zend Framework. Вы можете использовать его для тестирования или изучения компонент `Zend_XmlRpc`.

Пример 29.1. Вызов метода XML-RPC

    <?php

    require_once 'Zend/XmlRpc/Client.php';

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

    echo $client->call('test.sayHello');

    // hello

    ?>

Значение XML-RPC, возвращаемое при вызове удаленного метода, будет автоматически приведено к эквивалентному типу в PHP. В примере выше возвращается строка (тип `string` в PHP), и она уже готова к применению.

Первый параметр метода `call()` принимает имя удаленного метода, вызов которого требуется. Если удаленный метод требует каких-либо параметров, то они могут быть переданы методу `call()` через второй необязательный параметр в виде массива значений для последующей передачи удаленному методу:

Пример 29.2. Вызов метода XML-RPC с параметрами

    <?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));

    ?>

Если удаленный метод не требует параметров, то этот необязательный параметр можно опустить или передать пустой массив. Массив параметров для удаленного метода может содержать значения "родного" для PHP типа, объекты `Zend_XmlRpc_Value`, либо и то и другое вместе.

Метод `call()` будет автоматически преобразовывать ответ XML-RPC и возвращать его в эквивалентном "родном" для PHP типе. Кроме этого, можно получить объект `Zend_XmlRpc_Response` для возвращенного значения, вызвав метод `getLastResponse()` после вызова `call()`.

### 29.2.3. Типы и их преобразование

Некоторых удаленных методов требуют передачи параметров при вызове. Они предоставляются методу `call()` объекта `Zend_XmlRpc_Client` в виде массива во втором параметре. Каждый параметр может быть передан в "родном" для PHP типе, который будет автоматически преобразован, или как объект, представляющий определенный тип в XML-RPC (один из объектов `Zend_XmlRpc_Value`).

#### 29.2.3.1. Параметры в "родном" для PHP типе

Параметры могут передаваться методу `call()` как переменные "родного" для PHP типа, это могут быть типы `string`, `integer`, `float`, `boolean`, `array` или `object`. В этом случае каждый из этих типов будет автоматически определен и преобразован в один из типов XML-RPC согласно этой таблице:

Таблица 29.1. Преобразование типов PHP и XML-RPC

Тип в PHP | Тип в XML-RPC
--- | ---
integer | int
double | double
boolean | boolean
string | string
array | array
array (ассоциативный) | struct
object | array

#### 29.2.3.2. Параметры в виде объектов `Zend_XmlRpc_Value`

Параметры могут также создаваться как экземпляры `Zend_XmlRpc_Value` для точного указания типа XML-RPC. Основные причины для этого:

- Вы хотите быть уверенными в том, что процедуре передается корректный тип параметра (т.е. процедура требует целочисленное значение, а вы можете получать его из БД в виде строки)

- Удаленная процедура требует тип `base64` или `dateTime.iso8601` (которых нет среди "родных" для PHP типов).

- Автоматическое преобразование может работать неправильно (например, вы хотите передать пустую структуру XML-RPC как параметр. Пустая структура представляется в PHP пустым массивом, но когда вы передаете пустой массив как параметр, он будет преобразован в массив XML-RPC, так как не является ассоциативным массивом)

Есть два пути создания объектов `Zend_XmlRpc_Value` ― непосредственное инстанцирование одного из подклассов `Zend_XmlRpc_Value` и использование статического фабричного метода `Zend_XmlRpc_Value::getXmlRpcValue()`.

Таблица 29.2. Объекты `Zend_XmlRpc_Value` для типов XML-RPC

Тип XML-RPC | Константа `Zend_XmlRpc_Value` | Объект `Zend_XmlRpc_Value`
--- | --- | ---
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`

| Автоматическое преобразование
Когда создается новый объект `Zend_XmlRpc_Value`, его значение устанавливается в "родном" для PHP типе. Тип в PHP будет преобразован к определенному типу средствами PHP. Например, если в качестве значения для объекта `Zend_XmlRpc_Value_Integer` была передана строка, то она будет преобразована с помощью `(int)$value`.

### 29.2.4. Прокси-объект сервера

Другим способом вызова удаленных методов через клиента XML-RPC является использование "заместителя" сервера. Это PHP-объект, который предоставляет интерфейс к удаленному пространству имен XML-RPC, делая работу с ним настолько близкой к работе с обычным объектом в PHP, насколько это возможно.

Для того, чтобы инстанцировать "заместителя" сервера, вызовите метод `getProxy()` объекта `Zend_XmlRpc_Client`. Любые вызовы методов прокси-объекта сервера будет перенаправлены к удаленному серверу, параметры могут передаваться так же, как и для любых других методов в PHP.

Пример 29.3. Прокси-объект к пространству имен по умолчанию

    <?php

    require_once 'Zend/XmlRpc/Client.php';

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

    // Создание прокси-объекта к пространству имен по умолчанию
    $server = $client->getProxy();

    $hello = $server->test->sayHello(1, 2);
    // test.Hello(1, 2) возвращает "hello"

    ?>

Метод `getProxy()` принимает необязательный аргумент, указывающий, к какому пространству имен следует создать прокси-объект. Если этот аргумент не был указан, то то будет использоваться пространство имен по умолчанию. В следующем примере используется пространство имен `test`.

Пример 29.4. Прокси-объект к любому пространству имен

    <?php

    require_once 'Zend/XmlRpc/Client.php';

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

    // ... Proxy the "test" namespace
    $test  = $client->getProxy('test');

    $hello = $test->sayHello(1, 2);
    // test.Hello(1,2) возвращает "hello"

    ?>

Если удаленный сервер поддерживает сколько угодно вложенные пространства имен, то они также могут использоваться через прокси-объект сервера. Например, если сервер в примере выше имеет метод `test.foo.bar()`, то он может вызываться следующим образом: `$test->foo->bar()`.

### 29.2.5. Обработка ошибок

При вызове методов XML-RPC могут происходить два типа ошибок: HTTP и XML-RPC. `Zend_XmlRpc_Client` распознает оба типа, позволяя обнаруживать и отлавливать их независимо друг от друга.

#### 29.2.5.1. Ошибки HTTP

Если произошла ошибка HTTP, например, удаленный HTTP-сервер вернул код `404 Not Found`, то будет сгенерировано исключение `Zend_XmlRpc_Client_HttpException`.

Пример 29.5. Обработка ошибок HTTP

    <?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() возвращает 404
        // $e->getMessage() возвращает "Not Found"

    }

    ?>

Независимо от того, какой клиент XML-RPC используется, всякий раз, когда происходит ошибка HTTP, генерируется исключение `Zend_XmlRpc_Client_HttpException`.

#### 29.2.5.2. Ошибки XML-RPC

Ошибка XML-RPC аналогична исключению в PHP. Это специальный тип, возвращаемый при вызове метода XML-RPC и включающий в себя код и сообщение ошибки. Ошибки XML-RPC обрабатываются по-разному в зависимости от контекста использования `Zend_XmlRpc_Client`.

Если используется метод `call()` или прокси-объект сервера, то ошибка XML-RPC приведет к тому, что будет сгенерировано исключение `Zend_XmlRpc_Client_FaultException`. Код и сообщение исключения будут в точности соответствовать значениям в возвращенном ответе с сообщением об ошибке.

Пример 29.6. Обработка ошибок XML-RPC

    <?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() возвращает 1
        // $e->getMessage() возвращает "Unknown method"

    }

    ?>

Если для выполнения запроса используется метод `call()`, то в случае ошибки будет сгенерировано исключение `Zend_XmlRpc_FaultException`. Объект `Zend_XmlRpc_Response`, содержащий возвращенную ошибку, можно также получить через метод `getLastResponse()`.

Если для выполнения запроса используется метод `doRequest()`, то исключение не генерируется. Вместо этого будет возвращен объект `Zend_XmlRpc_Response`, содержащий возвращенную XML-RPC ошибку. Проверить, содержит ли объект ошибку, можно через метод `isFault()` объекта `Zend_XmlRpc_Response`.

### 29.2.6. Интроспекция сервера

Некоторые XML-RPC сервера де-факто поддерживают интроспекцию методов под пространством имен `system.`. `Zend_XmlRpc_Client` предоставляет специальную поддержку для серверов с этой возможностью.

Экземпляр `Zend_XmlRpc_Client_ServerIntrospection` может быть получен через вызов метода `getIntrospector()` класса `Zend_XmlRpcClient`. Далее он может использоваться для выполнения операций интроспекции на сервере.

### 29.2.7. От запроса к ответу

Метод `call()` экземпляра `Zend_XmlRpc_Client` в процессе выполнения строит объект запроса (`Zend_XmlRpc_Request`) и передает его другому методу `doRequest()`, который возвращает объект ответа (`Zend_XmlRpc_Response`).

Метод `doRequest()` также доступен для непосредственного использования:

Пример 29.7. Выполнение запроса

    <?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() возвращает экземпляр Zend_XmlRpc_Request
    // $server->getLastResponse() возвращает экземпляр Zend_XmlRpc_Response

    ?>

Всегда после того, как через клиента был вызван метод XML-RPC (через методы `call()`, `doRequest()` или через прокси-объект сервера), можно получить объекты последнего запроса и ответа на него через методы `getLastRequest()` и `getLastResponse()` соответственно.

### 29.2.8. HTTP-клиент и тестирование

Ни в одном из предыдущих примеров не указывался HTTP-клиент. В этом случае создается новый экземпляр `Zend_Http_Client` с настройками по умолчанию и автоматически используется клиентом `Zend_XmlRpc_Client`.

HTTP-клиент может быть получен в любое время через метод `getHttpClient()`. В большинстве случаев достаточно использование HTTP-клиента по умолчанию. Тем не менее, метод `setHttpClient()` позволяет установить HTTP-клиент, отличный от принятого по умолчанию.

`setHttpClient()` может быть полезен при unit-тестировании. При совместном использовании с `Zend_Http_Client_Adapter_Test` можно имитировать удаленные сервисы для тестирования. За примером того, как можно это реализовать, см. unit-тесты для `Zend_XmlRpc_Client`.

Пред. | Уровень выше | След.
Глава 29. Zend_XmlRpc | Начало | 29.3. Zend_XmlRpc_Server