<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.xmlrpc.client">
    <title>Zend_XmlRpc_Client</title>

    <sect2 id="zend.xmlrpc.client.introduction">
        <title>Введение</title>

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

    </sect2>


    <sect2 id="zend.xmlrpc.client.method-calls">
        <title>Вызов методов</title>

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

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

        <example id="zend.xmlrpc.client.method-calls.example-1">
            <title>Вызов метода XML-RPC</title>
            <programlisting role="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

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

// hello
]]></programlisting>
        </example>

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

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

        <example id="zend.xmlrpc.client.method-calls.example-2">
            <title>Вызов метода XML-RPC с параметрами</title>
            <programlisting role="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

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

$result = $client->call('test.sayHello', array($arg1, $arg2));

// возвращаемый результат имеет "родной" для PHP тип
]]></programlisting>
        </example>

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

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

    <sect2 id="zend.xmlrpc.value.parameters">
        <title>Типы и их преобразование</title>
        <para>
            Некоторые удаленные методы требуют передачи параметров при вызове.
            Они передаются методу <code>call()</code> объекта
            <classname>Zend_XmlRpc_Client</classname> в виде массива во втором параметре.
            Любой параметр может быть передан в "родном" для PHP типе, который
            будет автоматически преобразован в соответствующий тип XML-RPC,
            или как объект, представляющий
            определенный тип в XML-RPC (один из объектов
            <classname>Zend_XmlRpc_Value</classname>).
        </para>

        <sect3 id="zend.xmlrpc.value.parameters.php-native">
            <title>Параметры в "родном" для PHP типе</title>
            <para>
                Параметры могут передаваться методу <code>call()</code> как
                переменные "родного" для PHP типа, это могут быть типы
                <code>string</code>, <code>integer</code>, <code>float</code>,
                <code>boolean</code>, <code>array</code> или
                <code>object</code>. В этом случае каждый из этих типов будет
                автоматически определен и преобразован в один из типов XML-RPC
                согласно следующей таблице:
            </para>

            <table>
                <title>Преобразование типов PHP и XML-RPC</title>
                <tgroup cols="2">
                    <thead>
                        <row>
                            <entry>Тип в PHP</entry>
                            <entry>Тип в XML-RPC</entry>
                        </row>
                    </thead>
                    <tbody>
                        <row>
                            <entry>integer</entry>
                            <entry>int</entry>
                        </row>
                        <row>
                            <entry>double</entry>
                            <entry>double</entry>
                        </row>
                        <row>
                            <entry>boolean</entry>
                            <entry>boolean</entry>
                        </row>
                        <row>
                            <entry>string</entry>
                            <entry>string</entry>
                        </row>
                        <row>
                            <entry>array</entry>
                            <entry>array</entry>
                        </row>
                        <row>
                            <entry>array (ассоциативный)</entry>
                            <entry>struct</entry>
                        </row>
                        <row>
                            <entry>object</entry>
                            <entry>array</entry>
                        </row>
                    </tbody>
                </tgroup>
            </table>

            <note>
                <title>Какому типу будет соответствовать пустой массив?</title>

                <para>
                    Передача пустого массива методу XML-RPC несет в себе
                    потенциальную проблему, т.к. он может быть представлен
                    и массивом, и структурой. <classname>Zend_XmlRpc_Client</classname>
                    в этом случае делает запрос к методу сервера
                    <code>system.methodSignature</code> для определения
                    требуемого типа аргумента и производит соответствующее
                    преобразование.
                </para>

                <para>
                    Но такое решение само по себе тоже может быть источником
                    проблем. Во-первых, сервера, которые не поддерживают метод
                    <code>system.methodSignature</code>, будут журналировать это
                    как ошибочные вызовы, в этом случае
                    <classname>Zend_XmlRpc_Client</classname> будет производить
                    преобразование значения к типу array в XML-RPC.
                    Кроме того, это приводит к дополнительным вызовам к
                    удаленному серверу в случае передачи аргументов в виде
                    массивов.
                </para>

                <para>
                    Для того, чтобы полностью отключить эти вызовы, вы можете
                    вызвать метод <code>setSkipSystemLookup()</code> до
                    собственно запроса к методу XML-RPC:
                </para>

                <programlisting role="php"><![CDATA[
$client->setSkipSystemLookup(true);
$result = $client->call('foo.bar', array(array()));
]]></programlisting>
            </note>

        </sect3>

        <sect3 id="zend.xmlrpc.value.parameters.xmlrpc-value">
            <title>Параметры в виде объектов Zend_XmlRpc_Value</title>
            <para>
                Параметры могут также создаваться как экземпляры
                <classname>Zend_XmlRpc_Value</classname> для точного указания типа
                XML-RPC. Основные причины для этого:

                <itemizedlist>
                    <listitem>
                        <para>
                            Вы хотите быть уверенными в том, что
                            процедуре передается корректный тип параметра
                            (т.е. процедура требует целочисленное значение, а
                            вы можете получать его из БД в виде
                            строки)
                        </para>
                    </listitem>
                    <listitem>
                        <para>
                            Удаленная процедура требует тип <code>base64</code>
                            или <code>dateTime.iso8601</code> (которых нет среди
                            "родных" для PHP типов).
                        </para>
                    </listitem>
                    <listitem>
                        <para>
                            Автоматическое преобразование может работать
                            неправильно (например, вы хотите передать пустую
                            структуру XML-RPC в качестве параметра. Пустая структура
                            представляется в PHP пустым массивом, но если вы
                            передаете пустой массив в качестве параметра, то он
                            преобразовывается в массив XML-RPC, т.к. не
                            является ассоциативным массивом)
                        </para>
                    </listitem>
                </itemizedlist>
            </para>

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

            <table id="zend.xmlrpc.value.parameters.xmlrpc-value.table-1">
                <title>Объекты Zend_XmlRpc_Value для типов XML-RPC</title>
                <tgroup cols="3">
                    <thead>
                        <row>
                            <entry>Тип XML-RPC</entry>
                            <entry>Константа <classname>Zend_XmlRpc_Value</classname></entry>
                            <entry>Объект <classname>Zend_XmlRpc_Value</classname></entry>
                        </row>
                    </thead>
                    <tbody>
                        <row>
                            <entry>int</entry>
                            <entry><code>Zend_XmlRpc_Value::XMLRPC_TYPE_INTEGER</code></entry>
                            <entry><classname>Zend_XmlRpc_Value_Integer</classname></entry>
                        </row>
                        <row>
                            <entry>double</entry>
                            <entry><code>Zend_XmlRpc_Value::XMLRPC_TYPE_DOUBLE</code></entry>
                            <entry><classname>Zend_XmlRpc_Value_Double</classname></entry>
                        </row>
                        <row>
                            <entry>boolean</entry>
                            <entry><code>Zend_XmlRpc_Value::XMLRPC_TYPE_BOOLEAN</code></entry>
                            <entry><classname>Zend_XmlRpc_Value_Boolean</classname></entry>
                        </row>
                        <row>
                            <entry>string</entry>
                            <entry><code>Zend_XmlRpc_Value::XMLRPC_TYPE_STRING</code></entry>
                            <entry><classname>Zend_XmlRpc_Value_String</classname></entry>
                        </row>
                        <row>
                            <entry>base64</entry>
                            <entry><code>Zend_XmlRpc_Value::XMLRPC_TYPE_BASE64</code></entry>
                            <entry><classname>Zend_XmlRpc_Value_Base64</classname></entry>
                        </row>
                        <row>
                            <entry>dateTime.iso8601</entry>
                            <entry><code>Zend_XmlRpc_Value::XMLRPC_TYPE_DATETIME</code></entry>
                            <entry><classname>Zend_XmlRpc_Value_DateTime</classname></entry>
                        </row>
                        <row>
                            <entry>array</entry>
                            <entry><code>Zend_XmlRpc_Value::XMLRPC_TYPE_ARRAY</code></entry>
                            <entry><classname>Zend_XmlRpc_Value_Array</classname></entry>
                        </row>
                        <row>
                            <entry>struct</entry>
                            <entry><code>Zend_XmlRpc_Value::XMLRPC_TYPE_STRUCT</code></entry>
                            <entry><classname>Zend_XmlRpc_Value_Struct</classname></entry>
                        </row>
                    </tbody>
                </tgroup>
            </table>

            <para>
                <note>
                    <title>Автоматическое преобразование</title>
                    <para>
                        Когда создается новый объект
                        <classname>Zend_XmlRpc_Value</classname>, его значение
                        устанавливается в "родном" для PHP типе. Тип в PHP будет
                        преобразован к определенному типу средствами PHP.
                        Например, если в качестве значения для объекта
                        <classname>Zend_XmlRpc_Value_Integer</classname> была передана
                        строка, то она будет преобразована через
                        <code>(int)$value</code>.
                    </para>
                </note>
            </para>
        </sect3>
    </sect2>

    <sect2 id="zend.xmlrpc.client.requests-and-responses">
        <title>Прокси-объект сервера</title>
        <para>
            Другим способом вызова удаленных методов через клиента XML-RPC
            является использование "заместителя" сервера. Это PHP-объект,
            который предоставляет интерфейс к удаленному пространству имен
            XML-RPC, делая работу с ним максимально близкой к работе с обычным
            объектом в PHP.
        </para>

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

        <example id="zend.xmlrpc.client.requests-and-responses.example-1">
            <title>Прокси-объект к пространству имен по умолчанию</title>
            <programlisting role="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

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

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

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

        <example id="zend.xmlrpc.client.requests-and-responses.example-2">
            <title>Прокси-объект к произвольному пространству имен</title>
            <programlisting role="php"><![CDATA[
$client = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc');

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

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

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


    <sect2 id="zend.xmlrpc.client.error-handling">
        <title>Обработка ошибок</title>
        <para>
            При вызове методов XML-RPC могут могут быть ошибки двух типов: HTTP и
            XML-RPC. <classname>Zend_XmlRpc_Client</classname> распознает оба типа,
            позволяя обнаруживать и отлавливать их независимо друг от друга.
        </para>

        <sect3 id="zend.xmlrpc.client.error-handling.http">
            <title>Ошибки HTTP</title>

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

            <example id="zend.xmlrpc.client.error-handling.http.example-1">
                <title>Обработка ошибок HTTP</title>

                <programlisting role="php"><![CDATA[
$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"

}
]]></programlisting>
            </example>

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

        <sect3 id="zend.xmlrpc.client.error-handling.faults">
            <title>Ошибки XML-RPC</title>

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

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

            <example id="zend.xmlrpc.client.error-handling.faults.example-1">
                <title>Обработка ошибок XML-RPC</title>

                <programlisting role="php"><![CDATA[
$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"

}
]]></programlisting>
            </example>

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

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

    </sect2>

    <sect2 id="zend.xmlrpc.client.introspection">
        <title>Интроспекция сервера</title>
        <para>
            Некоторые XML-RPC сервера поддерживают интроспекцию методов
            под пространством имен <code>system.</code>.
            <classname>Zend_XmlRpc_Client</classname> предоставляет специальную поддержку
            для серверов с этой возможностью.
        </para>

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

    <sect2 id="zend.xmlrpc.client.request-to-response">
        <title>От запроса к ответу</title>
        <para>
            Метод <code>call()</code> экземпляра <classname>Zend_XmlRpc_Client</classname>
            в процессе выполнения строит объект запроса
            (<classname>Zend_XmlRpc_Request</classname>) и передает его другому методу
            <code>doRequest()</code>, который возвращает объект ответа
            (<classname>Zend_XmlRpc_Response</classname>).
        </para>

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

        <example id="zend.xmlrpc.client.request-to-response.example-1">
            <title>Выполнение запроса</title>

            <programlisting role="php"><![CDATA[
$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
]]></programlisting>
        </example>

        <para>
            После того, как через клиента был вызван метод XML-RPC (через
            методы <code>call()</code>, <code>doRequest()</code> или через
            прокси-объект сервера), всегда можно получить объекты последнего запроса и
            ответа на него через методы <code>getLastRequest()</code> и
            <code>getLastResponse()</code> соответственно.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.client.http-client">
        <title>HTTP-клиент и тестирование</title>

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

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

        <para>
            <code>setHttpClient()</code> может быть полезен при
            unit-тестировании. При совместном использовании с
            <classname>Zend_Http_Client_Adapter_Test</classname> можно имитировать
            удаленные сервисы для тестирования. В качестве примера реализации
            рассмотрите unit-тесты для <classname>Zend_XmlRpc_Client</classname>,
            входящие в поставку Zend Framework.
        </para>
    </sect2>

</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
