ウェブサービス
ウェブサービス とはネットワーク越しに、マシン同士の相互運用性をサポートする仕組みのことです。 ウェブアプリケーションの文脈においては、多くの場合、インターネットを介してアクセスできる API 群で、リクエストされたサービスをホストするリモートシステム上で実行されるものを、ウェブサービスと呼んでいます。 たとえば、Flex ベースのクライアントは、サーバで動いている PHP のウェブアプリケーションに実装された関数を呼び出すことが出来ます。 ウェブサービスはコミュニケーションプロトコルスタックの基礎部分を SOAP に依存しています。
Yii は CWebService と CWebServiceAction を提供することで、ウェブアプリケーションでウェブサービスを提供する作業を簡単にします。 API は サービスプロバイダ と呼ばれるクラス群にまとめられます。 Yii はそれぞれのクラスについて、WSDL スペックファイルを生成し、どの API が利用可能で、どのように呼び出すことができるのかを記述します。 クライアントによって API が呼び出されると、Yii が対応するサービスプロバイダをインスタンス化し、要求された API を実行してリクエストに応えます。
注意: CWebService は PHP SOAP extension に依存します。 この章の例を実行する前に拡張が有効になっていることを確認して下さい。
サービスプロバイダの定義
上で述べたように、サービスプロバイダとはリモートから呼び出し可能なメソッドを定義するクラスのことです。 Yii は doc comment と class reflection に基づいて、どのメソッドがリモートから呼び出し可能であり、どのような引数をとり、そしてどのような返り値を返すのかを決定します。
まず単純な株価情報サービスからはじめましょう。
このサービスではクライアントが株価情報を要求できます。
サービスプロバイダを以下のように定義します。
プロバイダのクラス StockController
を CController のサブクラスとして定義していることに注意してください。
ただし、このことは必須ではありません。
class StockController extends CController { /** * @param string the symbol of the stock * @return float the stock price * @soap */ public function getPrice($symbol) { $prices=array('IBM'=>100, 'GOOGLE'=>350); return isset($prices[$symbol])?$prices[$symbol]:0; $symbol の株価を返す } }
上記の例では、コメント (doc comment) に @soap
タグをつけることによって、getPrice
というメソッドがウェブサービス API であることを宣言しています。
引数と返値のデータタイプの定義も、コメント (doc comment) に依存します。
API を追加したい場合は、同じ方法で宣言して下さい。
ウェブサービスアクションを定義する
サービスプロバイダを定義したので、クライアントから呼び出し可能にする必要があります。
具体的には、サービスを公開するためのコントローラアクションを作成したいと思います。
これはコントローラクラスで、CWebServiceAction アクションを宣言することで容易に実現可能です。
サンプルコードでは StockController
にそれを追加します。
class StockController extends CController { public function actions() { return array( 'quote'=>array( 'class'=>'CWebServiceAction', ), ); } /** * @param string the symbol of the stock * @return float the stock price * @soap */ public function getPrice($symbol) { //...$symbol の株価を返す } }
これがウェブサービスを作るのに必要なことすべてです!
URL http://hostname/path/to/index.php?r=stock/quote
にアクセスすれば、今定義したウェブサービスの WSDL を表す大量の XML が表示されます。
ヒント: デフォルトでは、CWebServiceAction は現在のコントローラをサービスプロバイダとみなします。 これが
getPrice
メソッドをStockController
クラスに定義した理由です。
ウェブサービスを利用する
この例を完成させるために、できたばかりのウェブサービスを利用するクライアントを作ってみましょう。
例示したクライアントは PHP で書かれていますが、Java
, C#
, Flex
など、その他の言語で書くこともできます。
$client=new SoapClient('http://hostname/path/to/index.php?r=stock/quote'); echo $client->getPrice('GOOGLE');
このスクリプトをブラウザか、コンソールで実行すると、GOOGLE
の株価として 350
が表示される筈です。
データ型
リモートから呼び出し可能なクラスメソッドとプロパティを定義する際に、入出力パラメータのデータ型を決める必要があります。 以下の基本データ型が利用可能です。
- str/string:
xsd:string
に対応します; - int/integer:
xsd:int
に対応します; - float/double:
xsd:float
に対応します; - bool/boolean:
xsd:boolean
に対応します; - date:
xsd:date
に対応します; - time:
xsd:time
に対応します; - datetime:
xsd:dateTime
に対応します; - array:
xsd:string
に対応します; - object:
xsd:struct
に対応します; - mixed:
xsd:anyType
に対応します.
上記の基本型に当てはまらない場合は、複数のプロパティからなる複合型とみなされます。
複合型はクラスの形式で表され、コメント (doc comment) で @soap
のマークが付けられたパブリックなメンバ変数が、プロパティになります。
また、基本型や複合型の末尾に []
をつけることで配列型を使うこともできます。
[]
が指定された型の配列を定義します。
以下は Post
オブジェクトの配列を返す getPosts
ウェブ API の例です。
class PostController extends CController { /** * @return Post[] a list of posts * @soap */ public function getPosts() { return Post::model()->findAll(); } } class Post extends CActiveRecord { /** * @var integer post ID * @soap */ public $id; /** * @var string post title * @soap */ public $title; public static function model($className=__CLASS__) { return parent::model($className); } }
クラスの対応付け
複合型のデータをクライアントから受け取るためには、WSDL 型から PHP クラスへの対応を宣言する必要があります。 これは CWebServiceAction の classMap プロパティを設定することで実現されます。
class PostController extends CController { public function actions() { return array( 'service'=>array( 'class'=>'CWebServiceAction', 'classMap'=>array( 'Post'=>'Post', // あるいは単に'Post' ), ), ); } ...... }
リモート呼び出しを阻止する
サービスプロバイダで IWebServiceProvider インタフェースを実装することで、リモートからのメソッド呼び出しを阻止することができます。 IWebServiceProvider::beforeWebMethod 内でプロバイダは CWebService のインスタンスをうけとり、CWebService::methodName によって、リクエストされたメソッド名を得ることができます。 何らかの理由 (例: 権限のないアクセスなど) でリモートからのメソッド呼び出しを許可したくない場合は、false を返すことで呼び出しを阻止できます。