<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<!-- EN-Revision: 15617 -->
<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> に準拠しています。
            さらに system.multicall() メソッドを実装しており、
            リクエストをまとめる (boxcarring of requests) ことができます。
        </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> はさまざまなコンポーネントで構成されています。
            サーバ自身からリクエスト、レスポンス、fault
            オブジェクトなど広範囲に広がっています。
        </para>

        <para>
            <classname>Zend_XmlRpc_Server</classname> を起動するには、
            まずサーバにひとつ以上のクラスか関数をアタッチする必要があります。
            アタッチするには <code>setClass()</code> メソッドおよび
            <code>addFunction()</code> メソッドを使用します。
        </para>

        <para>
            起動させたら、次に <classname>Zend_XmlRpc_Request</classname> オブジェクトを
            <classname>Zend_XmlRpc_Server::handle()</classname> に渡します。
            もし渡さなかった場合は、<classname>Zend_XmlRpc_Request_Http</classname>
            のインスタンスを作成して <code>php://input</code>
            からの入力を受け取ります。
        </para>

        <para>
            <classname>Zend_XmlRpc_Server::handle()</classname> は、
            リクエストメソッドに応じて適切なハンドラに処理を振り分けます。
            そして、
            <classname>Zend_XmlRpc_Response</classname> を継承したオブジェクトか
            <classname>Zend_XmlRpc_Server_Fault</classname> オブジェクトを返します。
            これらのオブジェクトはどちらも <code>__toString()</code>
            メソッドを実装しており、妥当な XML-RPC XML レスポンスを直接出力することができます。
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.conventions">
        <title>規約</title>
        <para>
            <classname>Zend_XmlRpc_Server</classname> では、開発者が関数やクラスメソッドを
            XML-RPC メソッドとしてアタッチできるようになっています。
            アタッチされるメソッドの情報は <classname>Zend_Server_Reflection</classname>
            を使用して取得し、関数やメソッドのコメントブロックから
            メソッドのヘルプ文とシグネチャを取得します。
        </para>

        <para>
            XML-RPC の型は必ずしも PHP の型と一対一対応しているわけではありません。
            しかし、@param や @return の行をもとに、できるだけ適切な型を推測しようとします。
            XML-RPC の型の中には、直接対応する PHP の型がないものもありますが、
            その場合は PHPDoc の中で XML-RPC の型のヒントを指定します。
            たとえば次のような型が該当します。
        </para>

        <itemizedlist>
            <listitem><para>dateTime.iso8601 ...
                    YYYYMMDDTHH:mm:ss 形式の文字列</para></listitem>
            <listitem><para>base64 ... base64 エンコードされたデータ</para></listitem>
            <listitem><para>struct ... 任意の連想配列</para></listitem>
        </itemizedlist>

        <para>
            ヒントを指定するには、次のようにします。
        </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[
/**
* This is a sample function
*
* @param string|base64 $val1 文字列あるいは base64 エンコードされたデータ
* @param string|dateTime.iso8601 $val2 文字列あるいは ISO 日付
* @param array|struct $val3 Normal 数値添字配列あるいは連想配列
* @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 メソッドをドット区切りの名前空間でまとめるものです。
            これにより、さまざまなクラスで提供されるメソッド名の衝突を避けることができます。
            例として、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>
            を継承した独自のリクエストオブジェクトを作成します。
            注意すべき点は、getMethod() メソッドと getParams()
            メソッドを必ず実装しなければならないということです。
            これらは、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 で使用される適切な Content-Type HTTP
            ヘッダを送信します。独自のオブジェクトを使用する場面としては、
            レスポンスをログに記録したり、
            あるいはレスポンスを標準出力に返したりといったことが考えられます。
        </para>

        <para>
            独自のレスポンスクラスを使用するには、handle() をコールする前に
            <classname>Zend_XmlRpc_Server::setResponseClass()</classname> を使用します。
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.fault">
        <title>Fault による例外の処理</title>

        <para>
            <classname>Zend_XmlRpc_Server</classname> は、配送先のメソッドで発生した例外を捕捉します。
            例外を捕捉した場合は、XML-RPC の fault レスポンスを生成します。
            しかし、デフォルトでは、例外メッセージとコードは fault
            レスポンスで用いられません。これは、
            あなたのコードを守るための判断によるものです。
            たいていの例外は、コードや環境に関する情報を必要以上にさらけ出してしまいます
            (わかりやすい例だと、データベースの抽象化レイヤの例外を想像してみてください)。
        </para>

        <para>
            しかし、例外クラスをホワイトリストに登録することで、
            fault レスポンス内で例外を使用することもできます。
            そうするには、
            <classname>Zend_XmlRpc_Server_Fault::attachFaultException()</classname>
            を使用して例外クラスをホワイトリストに渡します。
        </para>

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

        <para>
            他のプロジェクトの例外を継承した例外クラスを利用するのなら、
            一連のクラス群を一度にホワイトリストに登録することもできます。
            <classname>Zend_XmlRpc_Server_Exceptions</classname> は常にホワイトリストに登録されており、
            固有の内部エラー (メソッドが未定義であるなど) を報告することができます。
        </para>

        <para>
            ホワイトリストに登録されていない例外が発生した場合は、
            コード '404'、メッセージ 'Unknown error' の falut
            レスポンスを生成します。
        </para>
    </sect2>

    <sect2 id="zend.xmlrpc.server.caching">
        <title>リクエスト間でのサーバ定義のキャッシュ</title>
        <para>
            たくさんのクラスを XML-RPC サーバインスタンスにアタッチすると、
            リソースを大量に消費してしまいます。各クラスを調べるために
            リフレクション API を (<classname>Zend_Server_Reflection</classname> 経由で) 使用する必要があり、
            使用できるすべてのメソッドのシグネチャをサーバクラスに提供します。
        </para>
        <para>
            使用するリソースの量を軽減するために、<classname>Zend_XmlRpc_Server_Cache</classname>
            を用いてリクエスト間でサーバ定義をキャッシュすることができます。
            __autoload() と組み合わせることで、これはパフォーマンスを劇的に向上させます。
        </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 sum を返します
 *
 * @param string $value md5sum を計算する値
 * @return string 値の MD5 sum
 */
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>fault レスポンス用に使用する例外の指定</title>

            <para>
                次の例は、Services_Exception の派生クラスに対して
                そのコードとメッセージを falut レスポンスで報告させるようにします。
            </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';

// Services_Exceptions を fault レスポンスで報告させるようにします
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 を fault レスポンスで報告させるようにします
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 を fault レスポンスで報告させるようにします
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 を fault レスポンスで報告させるようにします
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:
-->
