Zend_XmlRpc_Server
導入
Zend_XmlRpc_Server は、完全な機能を有した XML-RPC サーバです。
www.xmlrpc.com で提示されている仕様 に準拠しています。
さらに system.multicall() メソッドを実装しており、
リクエストをまとめる (boxcarring of requests) ことができます。
基本的な使用法
もっとも基本的な使用例は次のとおりです。
setClass('My_Service_Class');
echo $server->handle();
]]>
サーバの構造
Zend_XmlRpc_Server はさまざまなコンポーネントで構成されています。
サーバ自身からリクエスト、レスポンス、fault
オブジェクトなど広範囲に広がっています。
Zend_XmlRpc_Server を起動するには、
まずサーバにひとつ以上のクラスか関数をアタッチする必要があります。
アタッチするには setClass() メソッドおよび
addFunction() メソッドを使用します。
起動させたら、次に Zend_XmlRpc_Request オブジェクトを
Zend_XmlRpc_Server::handle() に渡します。
もし渡さなかった場合は、Zend_XmlRpc_Request_Http
のインスタンスを作成して php://input
からの入力を受け取ります。
Zend_XmlRpc_Server::handle() は、
リクエストメソッドに応じて適切なハンドラに処理を振り分けます。
そして、
Zend_XmlRpc_Response を継承したオブジェクトか
Zend_XmlRpc_Server_Fault オブジェクトを返します。
これらのオブジェクトはどちらも __toString()
メソッドを実装しており、妥当な XML-RPC XML レスポンスを直接出力することができます。
規約
Zend_XmlRpc_Server では、開発者が関数やクラスメソッドを
XML-RPC メソッドとしてアタッチできるようになっています。
アタッチされるメソッドの情報は Zend_Server_Reflection
を使用して取得し、関数やメソッドのコメントブロックから
メソッドのヘルプ文とシグネチャを取得します。
XML-RPC の型は必ずしも PHP の型と一対一対応しているわけではありません。
しかし、@param や @return の行をもとに、できるだけ適切な型を推測しようとします。
XML-RPC の型の中には、直接対応する PHP の型がないものもありますが、
その場合は PHPDoc の中で XML-RPC の型のヒントを指定します。
たとえば次のような型が該当します。
dateTime.iso8601 ...
YYYYMMDDTHH:mm:ss 形式の文字列
base64 ... base64 エンコードされたデータ
struct ... 任意の連想配列
ヒントを指定するには、次のようにします。
PhpDocumentor はパラメータや返り値の型を検証しません。
そのため、これが API ドキュメントに影響を及ぼすことはありません。
しかし、このヒントは必須です。メソッドがコールされた際に、
この情報をもとにサーバで検証を行うからです。
パラメータや返り値で複数の型を指定してもかまいません。
XML-RPC の仕様では、system.methodSignature は
すべてのメソッドシグネチャ
(すなわちパラメータと返り値の組み合わせ) の配列を返すことになっています。
複数指定する方法は、通常の PhpDocumentor の場合と同様に
'|' 演算子を使用します。
しかし、注意すべきことがあります。複数のシグネチャを定義すると、
それを利用する開発者を混乱させてしまいます。
一般論として、XML-RPC のメソッドは複数のシグネチャを持たないほうがいいでしょう。
名前空間の活用
XML-RPC には名前空間の概念があります。基本的に、これは
複数の XML-RPC メソッドをドット区切りの名前空間でまとめるものです。
これにより、さまざまなクラスで提供されるメソッド名の衝突を避けることができます。
例として、XML-RPC サーバは 'system'
名前空間でこれらのメソッドを提供することが期待されています。
system.listMethods
system.methodHelp
system.methodSignature
内部的には、これらは
Zend_XmlRpc_Server の同名のメソッドに対応しています。
自分が提供するメソッドに名前空間を追加したい場合は、
関数やクラスをアタッチする際のメソッドで名前空間を指定します。
setClass('My_Service_Class', 'myservice');
// 関数 'somefunc' は funcs.somefunc としてアクセスするようにします
$server->addFunction('somefunc', 'funcs');
]]>
独自のリクエストオブジェクト
ほとんどの場合は、
Zend_XmlRpc_Server や Zend_XmlRpc_Request_Http
に含まれるデフォルトのリクエスト型を使用するでしょう。
しかし、XML-RPC を CLI や GUI 環境などで動かしたい場合もあるでしょうし、
リクエストの内容をログに記録したい場合もあるでしょう。
そのような場合には、Zend_XmlRpc_Request
を継承した独自のリクエストオブジェクトを作成します。
注意すべき点は、getMethod() メソッドと getParams()
メソッドを必ず実装しなければならないということです。
これらは、XML-RPC サーバがリクエストを処理する際に必要となります。
独自のレスポンス
リクエストオブジェクトと同様、Zend_XmlRpc_Server
は独自のレスポンスオブジェクトを返すこともできます。
デフォルトでは Zend_XmlRpc_Response_Http オブジェクトが返されます。
これは、XML-RPC で使用される適切な Content-Type HTTP
ヘッダを送信します。独自のオブジェクトを使用する場面としては、
レスポンスをログに記録したり、
あるいはレスポンスを標準出力に返したりといったことが考えられます。
独自のレスポンスクラスを使用するには、handle() をコールする前に
Zend_XmlRpc_Server::setResponseClass() を使用します。
Fault による例外の処理
Zend_XmlRpc_Server は、配送先のメソッドで発生した例外を捕捉します。
例外を捕捉した場合は、XML-RPC の fault レスポンスを生成します。
しかし、デフォルトでは、例外メッセージとコードは fault
レスポンスで用いられません。これは、
あなたのコードを守るための判断によるものです。
たいていの例外は、コードや環境に関する情報を必要以上にさらけ出してしまいます
(わかりやすい例だと、データベースの抽象化レイヤの例外を想像してみてください)。
しかし、例外クラスをホワイトリストに登録することで、
fault レスポンス内で例外を使用することもできます。
そうするには、
Zend_XmlRpc_Server_Fault::attachFaultException()
を使用して例外クラスをホワイトリストに渡します。
他のプロジェクトの例外を継承した例外クラスを利用するのなら、
一連のクラス群を一度にホワイトリストに登録することもできます。
Zend_XmlRpc_Server_Exceptions は常にホワイトリストに登録されており、
固有の内部エラー (メソッドが未定義であるなど) を報告することができます。
ホワイトリストに登録されていない例外が発生した場合は、
コード '404'、メッセージ 'Unknown error' の falut
レスポンスを生成します。
リクエスト間でのサーバ定義のキャッシュ
たくさんのクラスを XML-RPC サーバインスタンスにアタッチすると、
リソースを大量に消費してしまいます。各クラスを調べるために
リフレクション API を (Zend_Server_Reflection 経由で) 使用する必要があり、
使用できるすべてのメソッドのシグネチャをサーバクラスに提供します。
使用するリソースの量を軽減するために、Zend_XmlRpc_Server_Cache
を用いてリクエスト間でサーバ定義をキャッシュすることができます。
__autoload() と組み合わせることで、これはパフォーマンスを劇的に向上させます。
使用例は次のようになります。
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();
]]>
この例では、スクリプトと同じディレクトリにある xmlrpc.cache
からサーバの定義を取得しようとします。取得できなかった場合は、
必要なサービスクラスを読み込み、
それをサーバのインスタンスにアタッチし、
そしてその定義を新しいキャッシュファイルに記録します。
使用例
以下のいくつかの使用例で、開発者が使用できるオプションを説明します。
各使用例は、それまでに紹介した例に追加していく形式になります。
基本的な使用法
次の例は関数を XML-RPC メソッドとしてアタッチし、
受け取ったコールを処理します。
addFunction('md5Value');
echo $server->handle();
]]>
クラスのアタッチ
次の例は、クラスのパブリックメソッドを
XML-RPC メソッドとしてアタッチします。
setClass('Services_Comb');
echo $server->handle();
]]>
名前空間を用いた複数のクラスのアタッチ
次の例は、複数のクラスをそれぞれの名前空間でアタッチします。
setClass('Services_Comb', 'comb'); // メソッドをコールするには comb.* とします
$server->setClass('Services_Brush', 'brush'); // メソッドをコールするには brush.* とします
$server->setClass('Services_Pick', 'pick'); // メソッドをコールするには pick.* とします
echo $server->handle();
]]>
fault レスポンス用に使用する例外の指定
次の例は、Services_Exception の派生クラスに対して
そのコードとメッセージを falut レスポンスで報告させるようにします。
setClass('Services_Comb', 'comb'); // メソッドをコールするには comb.* とします
$server->setClass('Services_Brush', 'brush'); // メソッドをコールするには brush.* とします
$server->setClass('Services_Pick', 'pick'); // メソッドをコールするには pick.* とします
echo $server->handle();
]]>
独自のリクエストオブジェクトの利用
次の例は、独自のリクエストオブジェクトを作成し、
それをサーバに渡して処理します。
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);
]]>
独自のレスポンスオブジェクトの利用
次の例は、独自のレスポンスクラスを作成し、
それをレスポンスとして返します。
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);
]]>
リクエスト間でのサーバ定義のキャッシュ
次の例は、リクエスト間でサーバ定義をキャッシュします。
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);
]]>