Forum und email

SCA 関数

導入

警告

この拡張モジュールは、 実験的 なものです。この拡張モジュールの動作・ 関数名・その他ドキュメントに書かれている事項は、予告なく、将来的な PHP のリリースにおいて変更される可能性があります。 このモジュールは自己責任で使用してください。

SCA を使用すると、再利用可能なコンポーネントを PHP プログラマが書けるようになります。 このコンポーネントはさまざまな方法でコール可能ですし、 固有のインターフェイスを持っており、まったく手がかかりません。 現時点ではローカルあるいはウェブサービス経由でしか このコンポーネントをコールできませんが、将来はそれ以外の方法も可能になるでしょう。 これらの作業を、PHP プログラマが違和感なく行えるするようにする手段を提供しています。

SCA のコンポーネントは、phpDocumentor 形式 (https://www.phpdoc.org/ を参照ください) のアノテーション (注記) を用いて 他の SCA コンポーネントやウェブサービスとの依存性を宣言します。 SCA for PHP のランタイムは、これらの依存性をコンポーネントに代わって 実行時に解決します。したがって、プログラマは 依存性を解決するために参照を取得して配置したりすることよりも ビジネスロジックの作成に集中することができます。

SCA for PHP のプログラミングモデルは、さまざまな形式のサービス、 たとえば REST や Atompub などをサポートするように拡張することもできます。 しかし、現在指定できるのはウェブサービス (正確には WSDL で定義された SOAP/HTTP サービス) のみです。

これらのコンポーネントでは、 サービスが公開するインターフェイスを定義するアノテーションも使用します。 SCA for PHP のランタイムは、これらのアノテーションをもとにして WSDL を自動的に生成します。これを用いることで、SCA コンポーネントで簡単にウェブサービスを公開できるようになります。 これらのアノテーションは、phpDocumentor の記法を、 違和感なく拡張したものです。 ウェブサービスを公開する方法は簡単で、単に PHP コンポーネントをウェブサーバのドキュメントルートに置くだけです。

これらのコンポーネントではまた、 データ構造 (XML スキーマの複合型を使用して表します) を指定するアノテーションも使用します。 このデータ構造を、Service Data Object (SDO) を用いて処理します。

SCA コンポーネントではなくアノテーションを含まない PHP スクリプトから、SCA コンポーネントのサービスを使用することができます。 また、PHP スクリプトやコンポーネントは、SCA コンポーネントでない ウェブサービスをコールすることもできます。しかし、 参照を取得するには、SCA コンポーネントと同じ形式のコール方法やアノテーションを使用します。

まず手始めに、ConvertedStockQuote という SCA コンポーネントをご覧に入れましょう。 これを用いて、SCA for PHP の多くの機能について説明します。 このコンポーネントはひとつのメソッド getQuote() を持っています。このメソッドは株式の "ティッカー" を指定して その株の株価を取得し、指定した通貨に変換します。 このドキュメントの残りの部分では、この例をもとにして SCA for PHP の機能を説明していきます。

Example#1 SCA コンポーネントのサンプル

<?php

include "SCA/SCA.php";

/**
 * 指定したティッカーシンボルと通貨についての株価を計算する
 *
 * @service
 * @binding.soap
 */
class ConvertedStockQuote {

    
/**
     * 使用する為替レートサービス
     *
     * @reference
     * @binding.php ../ExchangeRate/ExchangeRate.php
     */
    
public $exchange_rate;

    
/**
     * 使用する株価サービス
     *
     * @reference
     * @binding.soap ../StockQuote/StockQuote.wsdl
     */
    
public $stock_quote;

    
/**
     * ティッカーシンボルと通貨を指定して、株価を取得する
     *
     * @param string $ticker ティッカーシンボル。
     * @param string $currency 値を変換する通貨単位。
     * @return float 指定した通貨単位で表した株価。
     */
    
function getQuote($ticker$currency)
    {
        
$quote  $this->stock_quote->getQuote($ticker);
        
$rate   $this->exchange_rate->getRate($currency);
        return  
$rate $quote;
    }
}
?>

この例では、PHP のクラスを含むスクリプトで SCA コンポーネントを実装しており、SCA.php をインクルードしています。このクラスは、ビジネスロジックと 他のコンポーネントやサービスへの参照を両方含んでいます。 getQuote() メソッドにはビジネスロジックしかありませんが、 このメソッドではインスタンス変数 $stock_quote および $exchange_rate を使用しています。 これらはそれぞれ別のコンポーネントを参照しており、 SCA のランタイムがこれら二つのサービスのプロキシを用いて初期化します。 これは、このコンポーネントが実行されるたびに行われます。 これらの二つのサービスのアノテーションからわかるように、 一方はローカルのコンポーネントで、同じ PHP ランタイムからコールします。 もう一方はリモートのコンポーネントで、SOAP リクエストでコールします。 このコンポーネントは getQuote() メソッドをローカルおよびウェブサービスの両方で公開しているので、 ローカルからもリモートからもコール可能です。

要件

SCA を使用してウェブサービスの作成や使用を行うには PHP 5.2.0 以降が必要です。また soap 拡張モジュールが有効でなければなりません。 ローカルのコンポーネントのみを使用し、ウェブサービスを使用しないのなら、 このバージョンの SCA for PHP は PHP 5.1.6 以降で動作します。

インストール手順

SCA は、SDO とともにひとつのパッケージとして PECL で公開されています。 SCA_SDO パッケージを PECL からインストールする方法については https://www.php.net/sdo#sdo.installation を参照ください。 SCA のコードは、PHP のインクルードパスに存在しなければなりません。 つまり、もし /usr/local/lib/php/SCA にインストールされたのなら、 include_path ディレクティブに /usr/local/lib/php を含める必要があります。

これ以降の節の例では、PHP for SCA の次のような側面を説明します。

  • PHP のアノテーションを使用して PHP のクラスを SCA コンポーネントとして定義する方法、 そしてアノテーションを使用してサービスを定義する方法。

  • SCA コンポーネントをウェブサービスとして公開する方法。

  • SCA コンポーネントから別のウェブサービスを使用する方法。 使用するウェブサービスが別の SCA コンポーネントである場合と まったく SCA とは関係ないサービスである場合の両方を示します。

  • SCA コンポーネントから、ローカル (同一プロセス、同一コールスタック) の別の SCA コンポーネントをコールする方法。

  • SCA コンポーネントではないクライアントスクリプトが、 getService をコールして SCA コンポーネントのプロキシを使用する方法。

  • 住所や発注データなどのデータ構造を Service Data Object で表現し、処理する方法。

  • SCA コンポーネントを配置する方法。特に、サービスの WSDL がいつどのように作成されるか。

  • コンポーネント間でのパラメータの受け渡しが、ローカルコールであっても (参照ではなく) 値渡しとなること。これにより、 コンポーネントの場所にかかわらずコール方法が同じになります。

  • サービスへの、位置指定によるパラメータをサポートする方法。 もとになる WSDL がドキュメントリテラルをラップしたもので、 通常は名前指定によるパラメータしかサポートしていない場合も含みます。

  • ビジネスロジックのエラーや実行時例外の処理方法

サービスコンポーネントの構造

サービスコンポーネントは、クラスとして実装します。 サービスコンポーネントであることを示すために、@service アノテーションを使用します。SCA ランタイムは、 スクリプトのファイル名にもとづく規約でコンポーネント名を決定します。 したがって、クラス名とスクリプトのファイル名は同じにしておく必要があります。

PHP SCA コンポーネントは常にサービスを公開しています。 コンポーネントは、ウェブサービスへのリクエストの結果としてコールされるか スクリプト内の別のコンポーネントから直接コールされるかのいずれかの方法でしか起動しません。 このため、有効な PHP SCA コンポーネントは常に @service アノテーションを含んでおり、少なくともひとつの public メソッドを持っています。

各 SCA コンポーネントは、SCA.php をインクルードする必要があります。 ここには SCA クラスの定義が含まれているだけでなく、 スクリプトがコールされた際に実行される、 コンポーネントとして振舞うための PHP コードも含まれています。

警告

もし別のファイルもインクルードするのなら、それは SCA.php をインクルードする前に行わなければなりません。 SCA.php の後で別のファイルをインクルードすると、 SCA ランタイムがクラスを処理する際に、 そのファイルの内容が処理されません。

以下の例で、これらすべての構造について説明します。

Example#2 SCA for PHP のコンポーネントの構造

<?php

// 何かをインクルードします

include "SCA/SCA.php";

/**
 * @service
 */

class ConvertedStockQuote {

       
// インスタンス変数、ビジネスロジック、そして最低ひとつの public メソッドを含めます

}
?>

別のサービスコンポーネントへのプロキシの取得

ある SCA コンポーネントが、別の SCA コンポーネントのサービスをコールすることができます。 コンポーネントが提供するサービスは、すべて public メソッドでできています。 SCA for PHP では現在、あるコンポーネントから他のコンポーネントをコールする方法を 二通り提供しています。ローカルに (同一の PHP ランタイム、 同一のコールスタック上で) コールする方法と、リモートにコールする方法です。 リモートにコールする場合は、コールされるコンポーネントが ウェブサービスを公開していなければなりません。

あるコンポーネントが別のコンポーネントをコールするには、 呼び出し元のコンポーネント側に呼び出し先コンポーネントへのプロキシが必要です。 このプロキシは、通常は呼び出し元コンポーネントのインスタンス変数として用意します。 しかし、後でご覧いただくように SCA::getService() でプロキシを取得することも可能です。 コンポーネントを作成する際にプロキシを作成し、 別のコンポーネントを参照するインスタンス変数にそのプロキシを "注入 (inject)" します。プロキシは、そのコンポーネントがローカルにあるかリモートにあるかにかかわらず 常に用いられます。これにより、リモートだろうがローカルだろうが同じ方法でコールできるようになります (たとえば、ローカルへのコールの際もデータは常に値渡しとなります)。 プロキシは、そのコンポーネントがある場所と、 それをコールする方法を知っています。

サービスのプロキシを保持することを意図したインスタンス変数は、 PHPDocumentor 形式のふたつのアノテーション @reference および @binding で表します。それぞれのアノテーションは、 クラスのインスタンス変数のコメント部で指定します。以下のコードを参照ください。

インスタンス変数の前にある @reference アノテーションは、 そのインスタンス変数がコンポーネントへのプロキシとして初期化されることを示します。

@binding アノテーションには二通りの形式 @binding.php および @@binding.soap があり、それぞれそのプロキシが ローカルコンポーネント用なのかウェブサービス用なのかを表します。 @binding.php および @binding.soap のいずれの場合も、 対象の URI をアノテーションで指定します。

現時点では依存性の指定をアノテーションで行っているので、 参照先を変更する唯一の方法はコンポーネント内のアノテーションを書き換えることになります。

今回の例の ConvertedStockQuote では、インスタンス変数 $exchange_rate をローカルのコンポーネント ExchangeRate へのプロキシとして初期化しています。これは、 ConvertedStockQuote のインスタンスが作成されるたびに作成されます。

Example#3 ローカルの PHP クラスへのプロキシの取得

<?php 
   
/**
     * 使用する為替レートサービス
     *
     * @reference
     * @binding.php ../ExchangeRate/ExchangeRate.php
     */
    
public $exchange_rate;
?>

@binding.php の場合の URI は、 そのコンポーネントの実装を含むスクリプトの場所を表します。 コンポーネントはローカルにコールされます。 提供されるサービスは、コンポーネントの public メソッド群となります。 URI は、絶対パスあるいは相対パスで表す単純なパス名でなければなりません。 コンポーネントの読み込みには、PHP の include ディレクティブを使用します。 その際に、既に読み込まれていないかどうかを class_exists() で調べます。URI が相対パスの場合は、 そのアノテーションを含むコンポーネントの場所を基準としてパスを解決します。 これは、通常の PHP の振る舞いとは異なることに注意しましょう。 通常は、PHP は include_path を基準として解決します。 このようにしない理由は、複数コンポーネント間の参照関係を、 できるだけ場所の影響を受けないようにするためです。

この ExchangeRate サービスがリモートにあり、ウェブサービスとしてコールされるものだとすると、 変更するところは @binding の行だけです。PHP クラスの場所を指定するかわりに、 ウェブサービスについて記述した WSDL ファイルの場所を指定します。 今回のサンプルでは、二番目の参照でこの方式を説明しています。

Example#4 ウェブサービスへのプロキシの取得

<?php
/**
     * 使用する株価サービス
     *
     * @reference
     * @binding.soap ../StockQuote/StockQuote.wsdl
     */
    
public $stock_quote;
?>

StockQuote コンポーネントはウェブサービスへのリクエストとしてコールされます。 今回の場合は WSDL の URI が単純なパス名となっていますが、 PHP のラッパーを使用して、たとえば file://https:// で始めることもできます。 単純にパス指定する場合は絶対パスでも相対パスでもかまいません。 相対パスの場合は、そのアノテーションを含むコンポーネントの位置からの相対パスと解釈されます。 これは @binding.php の振る舞いと同様で、通常の PHP の挙動とは異なることに注意しましょう。 通常は、PHP は現在の作業ディレクトリからの相対パスとして探します。 作業ディレクトリは、通常はそのスクリプトがコールされた場所となります。 このようにしない理由は、複数コンポーネント間の参照関係を、 まずバインド形式の違いに影響を受けないようにすること、 そして場所の影響を受けないようにするためです。

別のサービスコンポーネントのコール

ConvertedStockQuote の例では、これらのプロキシをコールすることで それぞれが参照するコンポーネントを使用しています。

Example#5 サービスのコール

<?php
$quote  
$this->stock_quote->getQuote($ticker);
$rate   $this->exchange_rate->getRate($currency);
?>

StockQuote サービスのコールはローカルサービスへのコール、 一方 ExchangeRate サービスのコールはリモートサービスへのコールとなります。 それがローカルサービスであるかリモートサービスであるかにかかわらず、 コール方法は同じようになることに注意しましょう。

プロキシを使用することにより、コンポーネントのコール方法や振る舞いが ローカルであろうがリモートであろうが同じものになることが保証されます。 つまり、コンポーネントをコールする際に それがローカルにあるのかリモートにあるのかを気にしなくてもよいということです。 たとえば、ローカルサービスへのプロキシは、 引数をコピーしてそのコピーのみをサービスに渡します。 これにより、リモートコールの場合と同様に引数が値渡しとなることが保証されるわけです。 またリモートサービスへのプロキシは、受け取ったパラメータを SOAP リクエストのプロパティに変換し、戻ってきた内容を通常のパラメータリストに変換します。

上の例では、$ticker および $currency は明らかに PHP のスカラー型です。 コンポーネントには PHP のスカラー型である string、integer、float および boolean を渡すことができます。しかし、構造化されたデータは常に Service Data Objects (SDO) として渡します。 これ以降では、コンポーネントがどのようにして SDO を作成して ローカルコールやウェブサービスコールに渡すのか、 あるいはどのようにしてコンポーネントが返す SDO を作成するのかについて説明します。 PHP SDO プロジェクトのドキュメントに、SDO API の動作原理についての説明があります (SDO のページ を参照ください)。

SCA コンポーネントではないスクリプトからのサービスの指定およびコール

SCA コンポーネントは、インスタンス変数に @reference アノテーションを指定することで他のコンポーネントやサービスのプロキシを取得することができます。 しかし、コンポーネントではないスクリプトの場合はこれは不可能です。 クライアントスクリプトがコンポーネントではない場合は、静的メソッド SCA::getService() を使用して ローカルあるいはリモートサービスのプロキシを取得する必要があります。 getService() メソッドの引数には URI を指定します。 通常は、これはコンポーネントを含むローカル PHP スクリプトの場所か、 あるいは wsdl ファイルの場所となります。また、先ほどの節で説明した @binding アノテーションで指定するものと同じようになります。 つまり、相対 URI はクライアントスクリプトの場所を基準として解決し、 PHP の include_path や現在の作業ディレクトリは使用しません。

たとえば、あるスクリプトで ExchangeRate および StockQuote サービスのプロキシを取得する必要があるが、そのスクリプトがコンポーネントではない場合、 getService() メソッドを次のように使用します。

Example#6 getService を使用したプロキシの取得

<?php
$exchange_rate 
SCA::getService('../ExchangeRate/ExchangeRate.php');
$stock_quote   SCA::getService('../StockQuote/StockQuote.wsdl');
?>

サービスのメソッドをコールするには、返されたプロキシを使用します。 これはコンポーネント内からコールする場合と同じです。

Example#7 プロキシ上でのコール

<?php
$quote  
$stock_quote->getQuote($ticker);
$rate   $exchange_rate->getRate($currency);
?>

サービスコンポーネントの、ウェブサービスとしての公開

SCA for PHP は、サービスコンポーネント内のアノテーションから WSDL を作成することができます。これにより、 ウェブサービスとして公開することが簡単にできるようになります。 WSDL の作成に必要な情報を SCA に与えるには、@binding.soap アノテーションを @service アノテーションの下に追加しなければなりません。 そこに、メソッドのパラメータおよび返り値をそれぞれ @param アノテーションおよび @return アノテーションで指定します。 これらのアノテーションを読み込むことで WSDL が作成され、 パラメータの順序や型が WSDL の <schema> セクションの内容を決定します。

SCA for PHP は、常に document/literal でラップされたコンポーネントの WSDL を作成します。これにより、ウェブサービスを公開します。注意してほしいのは、 これは、SCA コンポーネント以外のウェブサービスや 別の形式の WSDL で表されるウェブサービスの使用を妨げるものではないということです。

@param アノテーションで使用できるスカラー型は、 一般的な PHP のスカラー型である boolean、integer、float そして string です。これらは、WSDL において それぞれ同名の XML スキーマ型に変換されます。 以下の例は、ConvertedStockQuote コンポーネントが呼び出す StockQuote サービスをごく平凡に実装したもので、 string および float 型の使用法をご確認いただけます。

Example#8 StockQuote サービス

<?php

include "SCA/SCA.php";

/**
 * リモートの StockQuote ウェブサービスの土台となる実装
 *
 * @service
 * @binding.soap
 *
 */
class StockQuote {

    
/**
     * 指定したティッカーシンボルの株価を取得する
     *
     * @param string $ticker ティッカーシンボル
     * @return float 株価
     */
    
function getQuote($ticker) {
        return 
80.9;
  }
}
?>

このサービスの WSDL は、次のようになります (おそらく、サービスの場所は 'localhost' ではなく別のものになるでしょう)。

Example#9 作成された WSDL

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="https://schemas.xmlsoap.org/wsdl/" xsi:type="tDefinitions"
    xmlns:tns2="https://StockQuote" xmlns:tns="https://schemas.xmlsoap.org/wsdl/"
    xmlns:tns3="https://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" targetNamespace="https://StockQuote">
  <types>
    <xs:schema xmlns:xs="https://www.w3.org/2001/XMLSchema"
      targetNamespace="https://StockQuote">
      <xs:element name="getQuote">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="ticker" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="getQuoteResponse">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="getQuoteReturn" type="xs:float"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
  </types>

  <message name="getQuoteRequest">
    <part name="getQuoteRequest" element="tns2:getQuote"/>
  </message>
  <message name="getQuoteResponse">
    <part name="return" element="tns2:getQuoteResponse"/>
  </message>
  <portType name="StockQuotePortType">
    <operation name="getQuote">
      <input message="tns2:getQuoteRequest"/>
      <output message="tns2:getQuoteResponse"/>
    </operation>
  </portType>
  <binding name="StockQuoteBinding" type="tns2:StockQuotePortType">
    <operation name="getQuote">
      <input>
        <tns3:body xsi:type="tBody" use="literal"/>
      </input>
      <output>
        <tns3:body xsi:type="tBody" use="literal"/>
      </output>
      <tns3:operation xsi:type="tOperation" soapAction=""/>
    </operation>
    <tns3:binding xsi:type="tBinding" transport="https://schemas.xmlsoap.org/soap/http" style="document"/>
  </binding>
  <service name="StockQuoteService">
    <port name="StockQuotePort" binding="tns2:StockQuoteBinding">
      <tns3:address xsi:type="tAddress" location="https://localhost/StockQuote/StockQuote.php"/>
    </port>
  </service>
</definitions>

<!-- this line identifies this file as WSDL generated by SCA for PHP. Do not remove -->

SCA コンポーネントの配置

PHP SCA コンポーネントを配置するには、特別な方法は必要ありません。 コンポーネントの PHP スクリプトを、 ウェブサーバのドキュメントルート以下の適切なディレクトリに配置するだけです。 これはごく普通の PHP スクリプトと同様です。 スクリプトがコールされると、各コンポーネント内の SCA::initComponent() の行が実行されます。 またウェブサービスのコール、ローカルのコール、 あるいは WSDL へのリクエストに対してコンポーネントが適切に応答するようにします。

ウェブサービスとしてのサービスを提供する SCA コンポーネントの WSDL の取得

ウェブサービスのインターフェイスを公開している (つまり、@binding.soap アノテーションを持っている) SCA コンポーネントは、HTTP リクエストのパラメータに "wsdl" を指定されるとその WSDL 定義を返します。 つまり、WSDL を取得する一般的な方法は、URL の最後に "?wsdl" をつけることです。以下の例では、 file_get_contents() を使用してサービスの WSDL を取得し、それをテンポラリファイルに書き出します。 それをもとにして、サービスへのプロキシを取得します。 もちろん、WSDL をブラウザやその他の方法で取得して、自分でそれを保存することも可能です。

Example#10 作成された WSDL

<?php
$wsdl = file_get_contents('https://www.example.com/Services/Example.php?wsdl');
file_put_contents("service.wsdl",$wsdl); // wsdl をファイルに書き出します
$service = SCA::getService('service.wsdl'); 
?>

注意: wsdl が xsd をインポートしている場合は、 それを個別に取得する必要があります。

WSDL の作成方法について理解する

SCA for PHP は、@service アノテーションの後に @binding.soap アノテーションを含むコンポーネントについての WSDL を作成します。WSDL を作成するために、SCA はコンポーネントの情報を取得し、各 public メソッドの @param アノテーションおよび @return アノテーションの内容を調べます。 また、コンポーネント内の @types アノテーションの内容も調べます。 @param アノテーションおよび @return アノテーションからの情報を基にして、 WSDL の <types> セクションを構築します。また、@types アノテーションによる別のスキーマファイルの指定は、WSDL 内の <import> 要素に変換されます。

<service> 要素の location 属性

WSDL の最後は <service> 要素で、 この要素の location 属性によってサービスの URL を表します。 たとえば、次のようになります。

Example#11 location 属性

    <service name="ConvertedStockQuote"
...
location="https://localhost/ConvertedStockQuote/ConvertedStockQuote.php"/>

この location は、ウェブサーバのドキュメントルートからの相対位置であることに注意しましょう。 事前に解決することはできません。コンポーネントがウェブサーバ上の適切な場所に配置され、 ホスト名やポート番号がわかった時点ではじめて解決することができます。 WSDL を要求した URL からの情報を使用します。つまり、たとえば https://www.example.com:1111/ConvertedStockQuote/ConvertedStockQuote.php?wsdl へのリクエストの応答として WSDL が作成されたとすると、 WSDL の location 属性には https://www.example.com:1111/ConvertedStockQuote/ConvertedStockQuote.php が挿入されます。

WSDL および位置パラメータをラップするドキュメント/リテラル

SCA for PHP が作成する WSDL は、ドキュメント/リテラル でラップした形式となります。これは、パラメータを囲い込んで 'ラッパー' メソッド型として返すものです。この型は、 対応するメソッドの名前となります。WSDL の先頭にある <types> 要素で、これらのラッパーを定義します。 ConvertedStockQuote のサンプルにおける getQuote() メソッドを考えてみましょう。

Example#12 ふたつの引数を持つメソッド

<?php
   
/**
     * ティッカーシンボルと通貨を指定して、株価を取得する
     *
     * @param string $ticker ティッカーシンボル。
     * @param string $currency 値を変換する通貨単位。
     * @return float 指定した通貨単位で表した株価。
     */
    
function getQuote($ticker$currency)
    {
        
$quote  $this->stock_quote->getQuote($ticker);
        
$rate   $this->exchange_rate->getRate($currency);
        return  
$rate $quote;
    }
?>

このメソッドの WSDL は、メソッドとパラメータの名前を定義します。 また、パラメータに対応する XML スキーマ型を提供します。 WSDL の types セクションは、このようになります。

Example#13 パラメータ名を表す types セクション

  <types>
    <xs:schema xmlns:xs="https://www.w3.org/2001/XMLSchema"
      targetNamespace="https://ConvertedStockQuote">
      <xs:element name="getQuote">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="ticker" type="xs:string"/>
            <xs:element name="currency" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="getQuoteResponse">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="getQuoteReturn" type="xs:float"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
  </types>

インターフェイス内で順番に指定したパラメータと soap リクエスト内の名前つきパラメータを含む XML を相互変換するために、 SCA ランタイムは特別な処理をします。この件について知るために、 さまざまなインターフェイスを使用する PHP スクリプトが SOAP コールようのパラメータリストをどのように作成するのかを考えてみましょう。 たとえば PHP の SoapClient を使用する PHP スクリプトは、 "ticker" および "currency" の値を指定したひとつのパラメータを SoapClient に渡す必要があります。おそらくこれは連想配列になるでしょう。 SCA コンポーネントがウェブサービス用にパラメータリストを作成する場合は、 相手がローカルであるかリモートであるかによって形式が異なります。 そのため、個別のアプローチが必要となります。

SCA が SCA コンポーネント用の WSDL を作成する際に、 その WSDL が SCA コンポーネントのインターフェイスであることを示すコメントを埋め込みます。 ある SCA コンポーネントが別のコンポーネントをウェブサービスとしてコールするとき、 呼び出し側の SCA ランタイムが受け取るパラメータは、 指定された順番に並べられたリストとなります。 これを、soap メッセージの名前つきパラメータにひとつひとつ割り当てるわけです。 たとえば、上で定義した getQuote() メソッドに値 'IBM' および 'USD' を渡す場合のコール方法は、このようになります。

$quote = $remote_service->getQuote('IBM','USD');

この結果は、次のような soap メッセージになります。

<getQuote>
  <ticker>IBM</ticker>
  <currency>USD</currency>
</getQuote>

サービス提供側の SCA コンポーネントは、soap メッセージから受け取った 名前つきパラメータをひとつひとつ受け取り、順番が指定されたパラメータリスト形式の ('IBM','USD') に再変換します。

警告

どちらの側の SCA ランタイムについても、 soap メッセージ内でのパラメータの登場順が メソッドのパラメータリストの順序と一致していることを前提としています。 これは、@param アノテーションの順序によって決まります。 これが、WSDL 内でのパラメータの登場順と soap メッセージ内でのパラメータの登場順を定義しています。 したがって、@param アノテーションの順序は メソッドのパラメータの順番と一致させておくことが必要不可欠です。

データ構造の扱い

SCA コンポーネントが受け渡しできる PHP のスカラー型は boolean、integer、float および string の四種類です。 それ以外のデータ構造を扱う際には、SCA コンポーネントは Service Data Objects (SDO) を使用します。SDO についての詳細は、このマニュアルの SDO のページ を参照ください。 SDO になじみのあるかたならご存知のとおり、 SDO は (XML のモデルのような) 構造化されたデータを扱うのに非常に適しています。 また、リモートコンポーネントやウェブサービスとのデータのやり取りの際に ごく自然にシリアライズすることができます。 データ構造の受け渡しを行う手段としては、現在 SDO のみをサポートしています。 その他の PHP オブジェクトや配列を受け渡しすることはできません。

SCA ランタイムは、データを常に値渡しします。これはローカルコールでも同じです。 そのため、SCA ランタイムは、パラメータリスト中の SDO のコピーを事前に作成します。これはスカラー型の場合と同じです。

データ構造を SCA コンポーネントで定義する方法

現在、データ構造の定義の場所を指定する手段としては XML スキーマファイルのでの型の指定しかありません。 しかし、将来的には別の方法も可能になるでしょう。 たとえば PHP のクラスやインターフェイスで定義したり、 連想配列で定義したりなどです。

SDO の使用法を説明するために、以下では新しいコンポーネント PortfolioMangement サービスを使用します。 これは、指定した顧客のポートフォリオを表す SDO を返します。

Example#14 データ構造を使用するコンポーネント

<?php

include "SCA/SCA.php";

/**
 * 顧客のポートフォリオを管理する
 *
 * @service
 * @binding.soap
 *
 * @types https://www.example.org/Portfolio PortfolioTypes.xsd
 *
 */
class PortfolioManagement {

    
/**
     * 指定した顧客のポートフォリオを取得する
     *
     * @param integer $customer_id 顧客 ID
     * @return Portfolio https://www.example.org/Portfolio ポートフォリオ (銘柄と数量)
     */
    
function getPortfolio($customer_id) {
        
// データベースから取得しているものとします
        
$portfolio SCA::createDataObject('https://www.example.org/Portfolio''Portfolio');
        
$holding $portfolio->createDataObject('holding');
        
$holding->ticker 'AAPL';
        
$holding->number 100.5;
        
$holding $portfolio->createDataObject('holding');
        
$holding->ticker 'INTL';
        
$holding->number 100.5;
        
$holding $portfolio->createDataObject('holding');
        
$holding->ticker 'IBM';
        
$holding->number 100.5;
        return 
$portfolio;
    }

}
?>

@types アノテーションは次のようになります。

<?php
@types http://www.example.org/Portfolio PortfolioTypes.xsd
?>

これは、名前空間 https://www.example.org/Portfolio 内の型が、スキーマファイル URI PortfolioTypes.xsd で見つかることを表します。 作成される WSDL では、この情報を次のような import ステートメントで再現します。

   <xs:import schemaLocation="PortfolioTypes.xsd"
                      namespace="https://www.example.org/Portfolio"/>

つまり、この URI は、絶対 URI であろうが相対 URI であろうが schemaLocation 属性で指定されたときに解決可能でなければなりません。

SDO の作成

SDO になじみの深い方は、SDO は 認められている構造 (あるいは 'スキーマ' や 'モデル' と呼ばれることもあります) でのみ作成されることをご存知でしょう。 また、'new' を使用するのではなくデータファクトリが必要となることもご存知でしょう。 たいていは、既存のデータオブジェクトをデータファクトリとして使用します。 しかし時には、特に最初のデータオブジェクトを取得するときなど、 何か別のものをデータファクトリとして使用する必要があります。

SCA では、SCA ランタイムクラスかあるいは (ローカルかリモートの) サービスのプロキシを SDO のデータファクトリとして使用できます。 どれを使用するのか、そしていつ使用するのかについて、 次の二つの節で説明します。

SDO の作成について説明するための新しい例を使用します。 サービスに SDO を渡し、そしてそのサービスから SDO を返すものです。

サービスに渡す SDO の作成

サービスをコールする際にデータ構造が必要となる場合、 そのサービスへのプロキシを 対応する SDO 用のデータファクトリとして使用します。 たとえば、ローカルの AddressBook コンポーネントが提供するサービスのプロキシとして使用するコンポーネントを考えて見ましょう。

<?php
/**
 * @reference
 * @binding.local AddressBook.php
 */
$address_book;
?>

コールしようとしている AddressBook コンポーネントの定義は、 次のようになります。

<?php
/**
* @service
* @binding.soap
* @types https://addressbook ../AddressBook/AddressBook.xsd
*/
class AddressBook {

    
/**
     * @param personType $person https://addressbook (person オブジェクト)
     * @return addressType https://addressbook (その person オブジェクトに対応する address オブジェクト)
     */
    
function lookupAddress($person)  {
        ...
    }
}
?>

AddressBook コンポーネントは、 lookupAddress() という名前のサービスメソッドを提供します。 これは、https://addressbook 名前空間の型を使用します。 lookupAddress メソッドは、personType 型のデータを受け取って addressType 型のデータを返します。それぞれの型は、スキーマファイル addressbook.xsd で定義されています。

AddressBook コンポーネントを使用するコンポーネントを作成すると、 インスタンス変数 $address_book にサービスへのプロキシが設定されます。 呼び出し元のコンポーネントは、$address_book に格納されたプロキシを使用して SDO を作成します。実際の例を以下に示します。

<?php
$william_shakespeare        
$address_book->createDataObject('https://addressbook','personType');
$william_shakespeare ->name "William Shakespeare";
$address                    $address_book->lookupAddress($william_shakespeare);
?>

このようにしてプロキシから SDO を作成する方法は、 SCA コンポーネントに限った話ではないことに注意しましょう。 サービスが一般の PHP スクリプトからコールされ、 プロキシを getService() で取得した場合にも同様の方法を使用します。

<?php
$address_book 
SCA::getService('AddressBook.php');
$william_shakespeare $address_book->createDataObject('https://addressbook','personType');
?>

コンポーネントから返す SDO の作成

データオブジェクトを作成して呼び出し元に返す必要のあるコンポーネントは、 データオブジェクトへのプロキシを使用しません。この場合は、 SCA.php の静的メソッド createDataObject() を使用します。 したがって、もし先ほど説明した AddressBook コンポーネントが addressType 型のオブジェクトを名前空間 https://addressbook に作成するのなら、 次のようになります。

<?php
$address 
SCA::createDataObject('https://addressbook','addressType');
?>

エラー処理

この節では、エラーの処理方法について説明します。 エラーには次の二種類があります。

  • SCA の実行時例外。これは、 コンポーネントの実行やリモートサービスとのやりとりの際に問題が発生したことを表すものです。 ネットワークや設定の問題が原因で起こることがあります。

  • ビジネスロジックの例外。これは、 プログラマが定義するものです。 PHP の Exception クラスを継承したクラスを作成し、 ビジネスロジックで問題が発生した場合に故意にスローします。

実行時例外の処理

SCA の実行時例外には二つの型があります。

  • SCA_RuntimeException - SCA ランタイム内で問題が見つかった、あるいは問題が起こったであろうと考えられるということを通知します。 これは、さまざまな理由で発生しえます。 その多くは、対象がローカルであるかリモートサービスであるかにかかわらず発生します。 コンポーネント内のアノテーションのエラーや、 WSDL や PHP ファイルが存在しないなどといった内容です。 ウェブサービスの場合に SCA_RuntimeException がスローされるのは、 リモートのウェブサービスから SoapFault を受け取り、 その失敗コードの内容が「再試行しても成功しないであろう」と判断されるときです。

  • SCA_ServiceUnavailableException - これは SCA_RuntimeException のサブクラスです。 接続時やリモートサービスの使用時に問題が発生したものの、 再試行すれば成功する可能性がある場合にスローされます。 ウェブサービスの場合にこの例外がスローされるのは、 リモートのウェブサービスから SoapFault を受け取り、 その失敗コードの内容が「再試行すれば成功するであろう」と判断されるときです。

ビジネスロジックの例外の処理

ビジネスロジックの例外の定義やスローは、通常通りの方法でコンポーネントで行います。 そのコンポーネントがローカルにコールされるかリモートからコールされるかは関係ありません。 SCA ランタイムは、ローカルからコールされたコンポーネント内で発生した ビジネスロジック例外をキャッチしません。これは呼び出し元に返されます。 一方、コンポーネントがウェブサービス経由でコールされた場合は SCA ランタイムがそのビジネスロジック例外をキャッチし、再度スローします。 呼び出し元がその例外の定義を知っている (その例外の PHP クラス定義を含むファイルをインクルードしている) ものとして、再度スローされた冷害は元の例外と同じ内容を含みます。 つまり、たとえば getLine() メソッドや getFile() メソッドには ビジネスロジック内のどの位置で例外が発生したのかが含まれます。 例外は、soap fault の詳細フィールドにおいて "Client" というコードで渡されます。

定義済みクラス

SCA へのインターフェイスのほとんどは、SCA コンポーネントのアノテーションを用いて操作します。 そのため、パブリックなクラスやメソッドは少ししかありません。 スクリプトやコンポーネントからコールできるクラスは、 SCA クラスそのもののほかには プロキシクラスである SCA_LocalProxy と SCA_SoapProxy だけです。

SCA

SCA クラスのほとんどの作業は、SCA コンポーネントにファイル SCA.php をインクルードしたときに行われます。しかし、PHP スクリプトは SCA.php をインクルードしてから SCA クラスの getService() メソッドをコールし、 サービスのプロキシを取得することもできます。 コンポーネントは、これを行う必要はありません。というのも、 インスタンス変数と @reference アノテーションを定義することでプロキシを取得できるからです。

SDO を作成して呼び出し元に返す必要があるコンポーネントは、 データファクトリが必要となります。この目的のために、SCA クラスは createDataObject() メソッドをサポートしています。 これは、コンポーネントの @types アノテーションで定義したモデルに基づいた SDO を作成します。 createDataObject() への引数は、 SDO の XML データアクセスサービスと同じです。

メソッド

SCA_LocalProxy

getService() に 対象となるローカル PHP コンポーネントを指定してコールすると、 ローカルプロキシが返されます。 ローカルプロキシは、@reference および @binding.php アノテーションで定義されたコンポーネントのインスタンス変数に注入されます。 スクリプトあるいはコンポーネントがローカルプロキシをコールすると、 それが対象のコンポーネント自身に渡されます。

SDO を作成して呼び出し元に返す必要があるコンポーネントは、 データファクトリが必要となります。この目的のために、 SCA_LocalProxy クラスは createDataObject メソッドをサポートしています。 これは、コンポーネントの @types アノテーションで定義したモデルに基づいた SDO を作成します。 createDataObject への引数は、 SDO の XML データアクセスサービスと同じです。

メソッド

SCA_SoapProxy

getService() に 対象となる WSDL ファイルを指定してコールすると、 SOAP プロキシが返されます。 SOAP プロキシは、@reference および @binding.soap アノテーションで定義されたコンポーネントのインスタンス変数に注入されます。 スクリプトあるいはコンポーネントが SOAP プロキシをコールすると、 それがウェブサービスへの SOAP リクエストに変換され、 PHP の Soap 拡張モジュールの助けを得て対象のコンポーネントに渡されます。

SDO を作成して呼び出し元に返す必要があるコンポーネントは、 データファクトリが必要となります。この目的のために、 SCA_SoapProxy クラスは createDataObject メソッドをサポートしています。 これは、対象の WSDL で定義したモデルに基づいた SDO を作成します。 createDataObject への引数は、 SDO の XML データアクセスサービスと同じです。

メソッド

目次