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

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

        <para>
            <classname>Zend_XmlRpc_Server</classname> задуман как полнофункциональный XML-RPC сервер,
            следующий <ulink url="http://www.xmlrpc.com/spec">спецификациям,
            приведенным на www.xmlrpc.com</ulink>. Кроме того, он реализует метод
            <code>system.multicall()</code>, позволяющий объединять несколько
            запросов в один.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.usage">
        <title>Основы использования</title>

        <para>
            Простой пример использования:
        </para>

        <programlisting language="php"><![CDATA[
$server = new Zend_XmlRpc_Server();
$server->setClass('My_Service_Class');
echo $server->handle();
]]></programlisting>
    </sect2>

    <sect2 id="zend.xmlrpc.server.structure">
        <title>Структура сервера</title>

        <para>
            <classname>Zend_XmlRpc_Server</classname> состоит из множества компонент от собственно
            сервера до объектов запросов, ответов и сообщений об ошибке.
        </para>

        <para>
            Для загрузки <classname>Zend_XmlRpc_Server</classname> разработчик должен прикрепить классы
            или функции к серверу через методы <code>setClass()</code> и
            <code>addFunction()</code>.
        </para>

        <para>
            После этого можно передать объект <classname>Zend_XmlRpc_Request</classname>
            методу <code>Zend_XmlRpc_Server::handle()</code>; если он не был
            передан, то будет проинциализирован объект
            <classname>Zend_XmlRpc_Request_Http</classname>, при этом данные запроса
            берутся из <code>php://input</code>.
        </para>

        <para>
            Затем <code>Zend_XmlRpc_Server::handle()</code> пытается определить
            подходящий обработчик, основываясь на запрошенном методе. После
            этого он возвращает объект ответа, основанный на
            <classname>Zend_XmlRpc_Response</classname>, или объект сообщения
            об ошибке <classname>Zend_XmlRpc_Server_Fault</classname>. Эти объекты имеют
            метод <code>__toString()</code>, который возвращает валидный XML-RPC
            ответ в формате XML, что позволяет выводить эти объекты
            через <code>echo</code>.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.conventions">
        <title>Соглашения</title>
        <para>
            <classname>Zend_XmlRpc_Server</classname> позволяет прикреплять функции и
            методы класса - т.н. доступные для диспетчеризации XML-RPC
            методы. С помощью <classname>Zend_Server_Reflection</classname> он проводит интроспекцию по
            всем прикрепленным методам, используя docblock'и функций и методов
            для установки текста справки и сигнатур методов.
        </para>

        <para>
            Не обязательно, чтобы типы в XML-RPC в точности соответствовали
            типам в PHP. Тем не менее, для наилучшего результата код пытается
            угадать наиболее подходящий тип, основываясь на значениях
            дескрипторов @param и @return. Некоторые типы в XML-RPC не имеют
            эквивалентов в PHP и должны указываться в PHPDoc. В их список
            входят:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    dateTime.iso8601 - дата в формате YYYYMMDDTHH:mm:ss
                </para>
            </listitem>
            <listitem><para>base64 - данные, закодированные по алгоритму base64</para></listitem>
            <listitem><para>struct - любой ассоциативный массив</para></listitem>
        </itemizedlist>

        <para>
            Пример того, как указывается XML-RPC тип
        </para>

        <programlisting language="php"><![CDATA[
/**
* Это пример функции
*
* @param base64 $val1 Закодированные в base64 данные
* @param dateTime.iso8601 $val2 Дата в ISO-формате
* @param struct $val3 Ассоциативный массив
* @return struct
*/
function myFunc($val1, $val2, $val3)
{
]]></programlisting>

        <para>
            PhpDocumentor не проводит валидацию типов, определенных для
            параметров или возвращаемых значений, поэтому это не должно
            влиять на вашу документацию по API. Указание типов необходимо,
            если сервер проводит валидацию передаваемых методу параметров.
        </para>

        <para>
            Будет совершенно корректным с точки зрения синтаксиса определять
            набор возможных типов как для параметров, так и для возвращаемых
            значений; спецификация XML-RPC даже рекомендует, чтобы
            system.methodSignature возвращал массив всех
            возможных сигнатур метода (т.е. все возможные комбинации параметров
            и возвращаемых значений). Вы можете делать это в точности так же,
            как это обычно делается для PhpDocumentor - с использованием
            оператора '|':
        </para>

        <programlisting language="php"><![CDATA[
/**
* Это пример функции
*
* @param string|base64 $val1 Строка или закодированные в base64 данные
* @param string|dateTime.iso8601 $val2 Строка или дата в ISO-формате
* @param array|struct $val3 Обычный нумерованный массив или ассоциативный массив
* @return boolean|struct
*/
function myFunc($val1, $val2, $val3)
{
}
]]></programlisting>

        <para>
            Тем не менее, следует учесть, что обилие сигнатур может сбивать с
            толку разработчиков, использующих данный веб-сервис. Другими словами,
            следует стремится к тому, чтобы XML-RPC метод имел только одну
            сигнатуру.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.namespaces">
        <title>Использование пространств имен</title>

        <para>
            В XML-RPC есть такое понятие, как пространства имен. Они позволяют
            группировать методы посредством разделенных точкой
            имен пространств. Это позволяет предотвратить конфликты имен
            методов, предоставляемых разными классами. Например, обычно XML-RPC
            сервер предоставляет несколько методов в пространстве имен 'system':
        </para>

        <itemizedlist>
            <listitem><para>system.listMethods</para></listitem>
            <listitem><para>system.methodHelp</para></listitem>
            <listitem><para>system.methodSignature</para></listitem>
        </itemizedlist>

        <para>
            В нашем случае они соответствуют методам с теми же именами в
            <classname>Zend_XmlRpc_Server</classname>.
        </para>

        <para>
            Если необходимо добавить пространства имен для обслуживаемых
            методов, то просто укажите пространство имен в качестве параметра
            при вызове соответствующего метода для прикрепления функции или
            класса:
        </para>

        <programlisting language="php"><![CDATA[
// Все открытые методы в My_Service_Class можно будет вызывать как
// myservice.имя_метода
$server->setClass('My_Service_Class', 'myservice');

// Функцию 'somefunc' можно будет вызывать как funcs.somefunc
$server->addFunction('somefunc', 'funcs');
]]></programlisting>
    </sect2>

    <sect2 id="zend.xmlrpc.server.request">
        <title>Использование своих объектов запросов</title>

        <para>
            В большинстве случаев вы можете использовать включенный по умолчанию
            в <classname>Zend_XmlRpc_Server</classname> тип запроса –
            <classname>Zend_XmlRpc_Request_Http</classname>. Тем не
            менее, может потребоваться использование XML-RPC в окружениях
            CLI, GUI и т.п., журналирование приходящих запросов. Для этого вы
            можете создавать свои классы запросов, которые наследуют от
            <classname>Zend_XmlRpc_Request</classname>. Важно помнить при этом, что
            методы <code>getMethod()</code> и <code>getParams()</code>
            должны быть реализованы таким
            образом, чтобы XML-RPC сервер мог получить из них ту информацию,
            которая необходима для обработки запроса.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.response">
        <title>Использование своих объектов ответов</title>

        <para>
            Как и в случае объектов запросов, <classname>Zend_XmlRpc_Server</classname> может
            возвращать объекты других типов; по умолчанию возвращается
            объект <classname>Zend_XmlRpc_Response_Http</classname>, который отправляет соответствующий
            XML-RPC заголовок <code>Content-Type</code>. Целью создания своих
            типов ответов могут быть возможность журналирования
            ответов или отправки ответов обратно в STDOUT.
        </para>

        <para>
            Для того чтобы использовать свой класс ответа, вызывайте
            метод <code>Zend_XmlRpc_Server::setResponseClass()</code> до вызова
            метода <code>handle()</code>.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.fault">
        <title>Обработка исключений через сообщения об ошибке</title>

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

        <para>
            Тем не менее, можно включать классы исключений в список разрешенных
            к отображению в ответах с сообщением об ошибке. Для этого
            используйте <code>Zend_XmlRpc_Server_Fault::attachFaultException()</code>
            для включения данного класса исключения в список разрешенных.
        </para>

        <programlisting language="php"><![CDATA[
Zend_XmlRpc_Server_Fault::attachFaultException('My_Project_Exception');
]]></programlisting>

        <para>
            Если вы используете класс исключения, от которого наследуют другие
            исключения в проекте, то можете cразу включить все "семейство"
            исключений в список разрешенных. Исключения
            <classname>Zend_XmlRpc_Server_Exception</classname>
            всегда находится в списке разрешенных исключений для того, чтобы
            сообщать об отдельных внутренних ошибках (вызов несуществующего
            метода и т.д.).
        </para>

        <para>
            На любое исключение, не включенное в список разрешенных, будет
            генерироваться ответ с кодом ошибки '404' и сообщением 'Unknown
            error'.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.caching">
        <title>Кэширование определений сервера между запросами</title>
        <para>
            Прикрепление большого количества классов к экземпляру XML-RPC
            сервера может отнимать много ресурсов – каждый класс должен
            проверяться с использованием Reflection API (через
            <classname>Zend_Server_Reflection</classname>), который создает список всех возможных
            сигнатур методов для передачи классу сервера.
        </para>
        <para>
            Чтобы снизить ущерб производительности, можно использовать
            <classname>Zend_XmlRpc_Server_Cache</classname> для кэширования определений сервера между
            запросами. Если комбинировать его с <code>__autoload()</code>, то это может дать
            значительный прирост производительности.
        </para>
        <para>
            Пример использования:
        </para>
        <programlisting language="php"><![CDATA[
function __autoload($class)
{
    Zend_Loader::loadClass($class);
}

$cacheFile = dirname(__FILE__) . '/xmlrpc.cache';
$server = new Zend_XmlRpc_Server();

if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
    require_once 'My/Services/Glue.php';
    require_once 'My/Services/Paste.php';
    require_once 'My/Services/Tape.php';

    $server->setClass('My_Services_Glue', 'glue');   // пространство имен glue
    $server->setClass('My_Services_Paste', 'paste'); // пространство имен paste
    $server->setClass('My_Services_Tape', 'tape');   // пространство имен tape

    Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
}

echo $server->handle();
]]></programlisting>
        <para>
            В этом примере производится попытка получить определение сервера из
            файла xmlrpc.cache, находящегося в той же директории, что и скрипт.
            Если попытка не удалась, то загружаются нужные классы и
            прикрепляются к экземпляру сервера, затем создается новый файл кэша
            с определением сервера.
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.use">
        <title>Примеры использования</title>
        <para>
            Здесь приведены несколько примеров использования, демонстрирующих полный
            набор возможностей, доступных разработчикам. Примеры
            построены на основе предоставленных ранее примеров.
        </para>
        <sect3 id="zend.xmlrpc.server.use.case1">
            <title>Основы использования</title>

            <para>
                В примере ниже прикрепляется функция в качестве
                доступного для диспетчеризации XML-RPC метода и обрабатываются
                входящие вызовы.
            </para>

            <programlisting language="php"><![CDATA[
/**
 * Возвращает сумму MD5 переданного значения
 *
 * @param string $value Value to md5sum
 * @return string MD5 sum of value
 */
function md5Value($value)
{
    return md5($value);
}

$server = new Zend_XmlRpc_Server();
$server->addFunction('md5Value');
echo $server->handle();
]]></programlisting>
        </sect3>

        <sect3 id="zend.xmlrpc.server.use.case2">
            <title>Прикрепление класса</title>

            <para>
                Пример ниже иллюстрирует прикрепление открытых методов класса
                в качестве доступных для диспетчеризации XML-RPC методов.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Comb.php';

$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb');
echo $server->handle();
]]></programlisting>
        </sect3>

        <sect3 id="zend.xmlrpc.server.use.case3">
            <title>Прикрепление нескольких классов с использованием пространств имен</title>

            <para>
                Пример ниже демонстрирует прикрепление нескольких классов,
                каждый со своим пространством имен.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';

$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb', 'comb');   // методы, вызываемые как comb.*
$server->setClass('Services_Brush', 'brush'); // методы, вызываемые как brush.*
$server->setClass('Services_Pick', 'pick');   // методы, вызываемые как pick.*
echo $server->handle();
]]></programlisting>
        </sect3>

        <sect3 id="zend.xmlrpc.server.use.case4">
            <title>Указание исключений в качестве используемых для ответов с сообщением об ошибке</title>

            <para>
                Пример ниже позволяет любым наследующим от
                <code>Services_Exception</code>
                классам предоставлять свои коды и сообщения для подстановки в
                ответ с сообщением об ошибке.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';

// Allow Services_Exceptions to report as fault responses
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');

$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb', 'comb');   // методы, вызываемые как comb.*
$server->setClass('Services_Brush', 'brush'); // методы, вызываемые как brush.*
$server->setClass('Services_Pick', 'pick');   // методы, вызываемые как pick.*
echo $server->handle();
]]></programlisting>
        </sect3>

        <sect3 id="zend.xmlrpc.server.use.case5">
            <title>Использование своих объектов запроса</title>

            <para>
                В примере ниже инстанцируется специальный объект запроса и
                передается серверу для обработки.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Request.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';

// Включение Services_Exceptions в список разрешенных исключений
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');

$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb', 'comb');   // методы, вызываемые как comb.*
$server->setClass('Services_Brush', 'brush'); // методы, вызываемые как brush.*
$server->setClass('Services_Pick', 'pick');   // методы, вызываемые как pick.*

// Создание объекта запроса
$request = new Services_Request();

echo $server->handle($request);
]]></programlisting>
        </sect3>

        <sect3 id="zend.xmlrpc.server.use.case6">
            <title>Использование своих объектов ответа</title>

            <para>
                Пример ниже демонстрирует указание специального класса ответа
                для возвращаемого ответа.
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Services/Request.php';
require_once 'Services/Response.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';

// Включение Services_Exceptions в список разрешенных исключений
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');

$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb', 'comb');   // методы, вызываемые как comb.*
$server->setClass('Services_Brush', 'brush'); // методы, вызываемые как brush.*
$server->setClass('Services_Pick', 'pick');   // методы, вызываемые как pick.*

// Создание объекта запроса
$request = new Services_Request();

// Установка другого класса ответа
$server->setResponseClass('Services_Response');

echo $server->handle($request);
]]></programlisting>
        </sect3>

        <sect3 id="zend.xmlrpc.server.use.case7">
            <title>Кэширование определений сервера между запросами</title>

            <para>
                Пример ниже демонстрирует кэширование определений сервера между
                запросами.
            </para>

            <programlisting language="php"><![CDATA[
// Указание файла кэша
$cacheFile = dirname(__FILE__) . '/xmlrpc.cache';

// Включение Services_Exceptions в список разрешенных исключений
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');

$server = new Zend_XmlRpc_Server();

// Попытка получить определение сервера из кэша
if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
    $server->setClass('Services_Comb', 'comb');   // методы, вызываемые как comb.*
    $server->setClass('Services_Brush', 'brush'); // методы, вызываемые как brush.*
    $server->setClass('Services_Pick', 'pick');   // методы, вызываемые как pick.*

    // Сохранение в кэш
    Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
}

// Создание объекта запроса
$request = new Services_Request();

// Установка другого класса ответа
$server->setResponseClass('Services_Response');

echo $server->handle($request);
]]></programlisting>
        </sect3>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
