Oracle + PHP Cookbook

PHPにおけるOracleを基盤としたSOAPサービスの作成


著者:John Coggeshall

PHP5におけるネイティブSOAPサポートの活用

関連するダウンロード・リンク
 Oracle Database 10g Express Edition
Zend Core for Oracle
 Apache Web Server 1.3.x

2006年9月公開

Webサービスを介して他のインターネット・ベースのWebアプリケーションに対してデータおよび機能の提供をおこなう能力は、本格的な開発の要件として急速に広がっています。 OracleではWebサービスをホストする手段を多数提供していますが、ユーザーがWebアプリケーションの開発にPHPを使用している場合には、こうしたアプローチがもっとも効果的であるとは限りません。 このレシピでは、データのバックエンドとしてOracleを使用し、SOAPクライアントおよびサーバーをPHPで開発する手順を段階を追って説明していきます。

また、この課題に関する解決策を十分に理解するには、PHPスクリプト実行のライフサイクルとWebサーバーがそのライフサイクルに与える影響を理解する必要があります。そのため、このレシピでは最初にこの点について押さえておきます。

必要なコンポーネント

まず、ここでは非常に単純なデータベース・バックエンドを使用して、出版されている書籍に関する基本情報を1つの表に保存します。この表は、以下のCREATE文で表されます。

CREATE TABLE books(isbn VARCHAR(32) PRIMARY KEY,
                   author VARCHAR(50),
                   title VARCHAR(50),
                   price FLOAT);
この表はSOAPサーバーのデータソースとして機能し、そのSOAPサーバーが、1つ以上のSOAPクライアントに対して、それぞれの使用に適したデータを提供します。 実際のアプリケーションで使用するデータベースはもっと複雑なものが多いと思われますが、その場合も、ここで説明する手法を適用することは可能です。

データベースを作成したら(できれば、ダミー・データをあらかじめセットしてください)、準備完了です。PHPでのSOAPサーバーの開発について見ていきましょう。

PHPでSOAPサービスを使用する方法

PHPでSOAPサービスの開発をおこなう場合はいくつかの選択肢がありますが、それらはすべてSoapServer PHPクラスを中心としています。 このクラスは、あらゆるPHPベースのSOAPサービスにおいてもっとも重要なクラスで、構文は以下のようになります。

$server = new SoapServer($wsdl [, $options]);
上記の$wsdlは、ホストされているサービスが記述されているWeb Service Description Language(WSDL)ドキュメントの位置を示し、$optionsは、サービス作成時に考慮すべき設定オプションが含まれたキーと値のペアの配列です。 WSDLドキュメントについては後で詳しく説明しますので、まず新しいSOAPサービスを作成する場合に使用できるオプションについて見ていきます。
  • soap_version: クライアントとの通信時に使用するSOAPプロトコルのバージョン。 使用可能なオプションは、SOAPバージョン1.1の場合は定数SOAP_1_1、SOAPバージョン1.2の場合は定数SOAP_1_2。
  • encoding: このSOAPサービスで使用する文字エンコード(文字列ISO-8859-1など)。
  • actor: このSOAPサービスのアクターURI。
  • classmap: WSDLデータ型をPHPのクラス名にマッピングするキー/値ペアの配列そのものを指す。 使用時には、これらのクラスはPHPにより、接続クライアントに対して、WSDLで定義された型として表される。
上記のオプションを使用して、bookman.wsdlという名前のWSDLドキュメントを使用しSOAP v1.2プロトコルでSOAPサービスを作成する場合には、以下のようにサーバーを構成します。
$server = new SoapServer(a??bookman.wsdla??, array(a??soap_versiona?? => SOAP_1_2));
プロセスの次のステップは、サービス・メソッドの作成です。 PHPでは、この処理を2つのプライマリ・メソッドを使用して実行できます。 1つ目は、もっとも柔軟性の高い方法で、以下に示すとおり、addFunction()メソッドを使用してクライアントに公開する関数名を渡し、サービスでホストする各関数を手動で指定します。
function add($a, $b) {
    return $a + $b;
}

$server->addFunction(a??adda??);
また、次のように関数名の配列を渡すことで、複数の関数を追加することもできます。
function add($a, $b) {
    return $a + $b;
}

function sub($a, $b) {
    return $a - $b;
}

$server->addFunction(array(a??adda??, a??suba??));
最後に、次のように関数名の代わりに特別な定数SOAP_FUNCTIONS_ALLを渡すことで、定義済みの関数すべてをエクスポートできます。
function add($a, $b) {
    return $a + $b;
}

function sub($a, $b) {
    return $a - $b;
}

$server->addFunction(SOAP_FUNCTIONS_ALL);
上記のサンプルで示されているように、SOAPサービスとして公開されている関数は、ルーチンPHP関数と同じように見えます。 しかし定義上は、SOAPサービスのコンテキストで使用される関数に適用されるルールの中には、ルーチンPHP関数には適用されないものもいくつかあります。具体的には、以下の点が挙げられます。
  • サーバーにより提供されるWSDLドキュメントにおける定義と同じ入力パラメータを、同じ順序で受け入れる必要がある。
  • 出力をおこなってはならない(print、echoなど)。
  • 1つ以上の値を返さなければならない(複数値は、キーと値のペアによる連想配列の形式で返す)。
構造上も見た目の点でも、公開するすべてのサービス・コールを手続き型の関数で表現することは賢明ではありません。このため、PHPでもオブジェクトを使用してSOAPサービスを表す手段が提供されています。 addClass()メソッドを使用すると、以下に示すように、1つのクラスをSOAPサービスの関数全体を示すように割り当てることができます。この関数ではすべてのパブリック・メソッドが自動的にサービス・コールとして公開されます。
class math {
    public function add($a, $b) {
        return $a + $b;
    }

    public function sub($a, $b) {
        return $a - $b;
    }
}

$server->addClass("math");
このメソッドについては後で説明しますが、もっとも的確でモジュール化が進んだメソッドです。

SOAPサーバーを完成させるには、接続SOAPクライアントから出される可能性のある着信要求をすべて処理するように指示する必要があります。 この処理は、パラメータの必要がないhandle()メソッドで実行します。

要約すると、PHPでのSOAPサーバーの作成は、以下のサンプルで示すように簡単に実行可能であるといえます。

<?php

class math {
    public function add($a, $b) {
        return $a + $b;
    }

    public function sub($a, $b) {
        return $a - $b;
    }
}

$server = new SoapServer(a??math.wsdla??);
$server->addClass(a??matha??);
$server->handle();

?>

問題発生時の処理

PHPでSOAPサービスを作成する場合の問題としてまだ取り上げていない点に、障害発生時のクライアントへのエラー報告があります。 SOAPプロトコルの仕様では、要求処理中のエラーに対し、要求元のクライアントに特別なSOAP障害レスポンスを返すという処理が必要とされます。 PHPでは、次のように、SoapFaultクラスのインスタンスをスローし、エラー・コードに加えてエラーの内容を示すオプションのエラー・メッセージをクライアントに提供することで、この処理を実行します。

public Function div($a, $b) {
    if($b == 0) {
        throw new SoapFault(-1, a??Cannot divide by zero!a??);
    }
    
    return $a / $b;
}
SoapFaultに対する2つのパラメータは、使用しているアプリケーション自体で表現されるもの以外の意味をもたないことに注意してください。 したがって、ここではエラー・コードとしてマイナス1(-1)を使用しましたが、他の整数値を任意に選ぶことも可能です。

WSDLの生成

前述のサンプルは、SOAPサービスを作成するための完全なPHPスクリプトといっても過言ではありませんが、WSDLドキュメントの問題にはまったく対応していません。 WSDLドキュメントはこのプロセス全体の重要なコンポーネントであることを考慮し、WSDLドキュメントの生成についても説明していきます。

残念なことに、Javaや.NETサービスなどの強力に型付けされた言語では可能ですが、PHPは性質的に型をもたないため、PHPを使用してオンザフライでWSDLドキュメントの自動生成を実行するための合理的な方法はありません。 WSDLドキュメントでは各パラメータの型を指定する必要がありますが、このスクリプトでは変数$aおよび$bにより型定義情報が提供されていないため、パラメータ型を表す代替手段が必要となります。 これについては、以下のようにさまざまな方法があります。

  • WSDLドキュメントをユーザーが手作業で記述する。
  • WebベースのWSDLジェネレータを使用して、各メソッドと型定義情報の入力を手動でおこなうことでファイルを生成する。
  • Zend Studioの自動WSDLジェネレータを使用する。
上記の3つのオプションはすべて完全に実行可能ですが、ここでは次の2つの理由から、Zend StudioのWSDLジェネレータを使用してWSDLドキュメントを生成する方法について説明していきます。 1つは、Zend Studioは、現時点でWSDドキュメントを生成するもっとも簡単で信頼性の高い方法であること、そしてもう1つは、ほとんどすべてのPHPショップで入手可能であるという点です。

Zend StudioのWSDLジェネレータを使用してWSDLドキュメントを生成するには、まず公開する各メソッドに対してそれぞれのパラメータの型定義情報を識別し、PHPDocと呼ばれるインラインのドキュメント・コメント(一般的なJavaDocのPHPバージョン)を使用して値を返す必要があります。 PHPDocは、解析可能な特定の構文を使用して各関数の最初に置かれる簡単なブロック・コメントで、ドキュメントの自動生成に使用されます。 Zend Studioでも、この情報を使用して、WSDLドキュメントを生成するために必要な型定義情報を収集します。

前述のサンプルの続きを見ていきます。以下は、前に使用したのと同じmathクラスですが、今回はPHPDocコメントを使用しています。

/**
 * A simple math utility class
 * @author John Coggeshall john@zend.com
 */
class math {
    /**
     * Add two integers together
     *
     * @param integer $a The first integer of the addition
     * @param integer $b The second integer of the addition
     * @return integer The sum of the provided integers
     */
    public function add($a, $b) {
        return $a + $b;
    }

    /**
     * Subtract two integers from each other
     *
     * @param integer $a The first integer of the subtraction
     * @param integer $b The second integer of the subtraction
     * @return integer The difference of the provided integers
     */
    public function sub($a, $b) {
        return $a - $b;
    }
}
このようにPHPDocコメントを挿入すると、Zend Studioにおいて、このクラスに対する適切なWSDLドキュメントを自動的に生成することができるようになります。この処理をおこなうには、Zend Studioの「Tools」メニューから「WSDL Generator」を実行します。

図1

PHPDocコメントを適切な場所に挿入すると、SOAPサーバーのWSDLドキュメントを生成するために必要となる単調で手間のかかる作業が軽減され、簡単なステップ・バイ・ステップ・ウィザードの手順に従っていくだけで生成が可能となります。 生成が完了すると、Zend Studioで参照用にWSDLドキュメントが開き、ユーザーはこれを任意の場所に保存できます。

生成したドキュメントは、サービスを使用する可能性のあるSOAPクライアントからアクセスできる場所かつサーバーからのアクセスも可能な場所に置く必要があります(サーバーでは、クラスのインスタンス化の際に必要となります)。 通常は、SOAPサービスをホストしているエンドポイントのPHPスクリプトと同じ場所にWSDLドキュメントをデプロイすることが、もっとも簡単な方法です。

独自のBookManagerクラスの作成

以上でPHPにおけるSOAPサービスの実装に必要な点に関する説明はすべて終わりましたので、ここで、もう一度データベースに話を戻しましょう。 ここではBookManagerという名前のクラスを作成しました。 このクラスは、前述のサンプルにおけるmathクラスと同じ目的を果たすものですが、ここでは、データベースとのデータのやりとりをおこない、SOAPサービスを提供するために設計されています。このSOAPサービスにより、一般的なメンテナンスを実行し、このチュートリアルの冒頭で説明した書籍の基本情報に関する表(books表)への問合せをおこなうことが可能となります。 具体的には、BookManagerクラスは、SOAP呼出しとして公開する以下のメソッドを実装します。

addBook($isbn, $author, $title, $price); // Adds a Book to the database
delBook($isbn); // Deletes a book by ISBN number
findBookISBNByAuthor($author); // Returns an array of ISBN numbers of books written by a         
                               // specific author
findBookISBNByTitle($title); // Returns an array of ISBN numbers of books whose title 
                             // matches the substring provided
getBookByISBN($isbn); // Returns the details of the book identified by ISBN
listAllBooks(); // Returns an array of all ISBN numbers in the database
このクラス自体には他にもいくつかメソッドがありますが、宣言されるパブリック・メソッドは上記の6つのメソッドのみ(コンストラクタを除く)であり、結果として、この6つだけがSOAPサービスとして公開されます。 特にこれらがすべて基本的には同じ形式であることを考え合わせると、この6つのメソッドそれぞれについて詳細に説明することは、このチュートリアルの目的としては行きすぎですが、完全を期してdelBook()メソッドについて触れておきます。
/**
         * Delete a book from the database by ISBN
         *
         * @param string $isbn The ISBN serial number of the book to delete
         * 
         * @return mixed SOAP Fault on error, true on success
         */
        public function delBook($isbn) {
                
                $query = "DELETE FROM books
                                WHERE isbn = :isbn";
                
                $stmt = oci_parse($this->getDB(), $query);
                
                if(!$stmt) {
                        throw new SoapFault(-1, "Failed to prepare query (reason: " .  
                                         oci_error($stmt) . ")");
                }
                
                oci_bind_by_name($stmt, "isbn", $isbn, 32);

                if(!oci_execute($stmt)) {
                        oci_rollback($this->getDB());
                        throw new SoapFault(-1, "Failed to execute query (reason: " . 
                                          oci_error($stmt) . ")");
                }
                
                oci_commit($this->getDB());
                
                return true;
        }
上記のメソッドは、PHP用のOracle APIに精通しているユーザーにとっては、きわめて簡単なものだと思います。 そうではない方は、ここで、この関数におけるいくつかの重要なポイントを、oci_parse()メソッドから順に確認していきましょう。 oci_parse()メソッドは、文字列の形式でSQL問合せを(必要な場合は、問合せの各変数に対してプレースホルダも一緒に)受け取り、それを表す文リソースを返します。 ここから、この文リソースのプレースホルダを、oci_bind_by_name()メソッドを使用してPHP変数に直接マッピングすることができます。oci_bind_by_name()メソッドは、パラメータとして文、プレースホルダ名、対応するPHP変数に加え、オプションとして問合せにおける列の最大長を受け取ります。 PHPで各プレースホルダがPHP変数にバインドされると、文を実行して結果に対して処理をおこなうことが可能となります。 この操作は表に対する 書込み操作であるため、データベースへの変更をコミットし、成功のステータスを返すことで、関数の実行は正常に完了します。

参考までに、BookManagerクラス全体と、このクラスを使用してSOAPサービスを公開するサーバー・スクリプトを以下に示します。

ご使用のブラウザはインライン・フレームをサポートしていないようです。 このリストを参照するには こちらをクリックしてください。

PHPによるSOAPクライアントの作成

PHPでのSOAPサービス作成について説明したので、サーバーの通信手段を確保するために、SOAPクライアントを作成する方法についても確認しておきます。

PHP SOAP実装を使用してSOAPを介したリモート・プロシージャ・コールを実行する方法は多数ありますが、WSDLドキュメントを使用する方法をお勧めします。 すでにSOAPサービスを使用するようにこのドキュメントを生成してあるため、簡単に利用できます。

PHPでSOAPクライアントを作成するには、以下のコンストラクタをもつSoapClientクラスのインスタンスを作成する必要があります。

$client = new SoapClient($wsdl [, $options]);
SoapServerクラスの場合と同様に、$wsdlパラメータは、アクセスを受けるサービスのWSDLドキュメントの位置を示し、オプションのパラメータ$optionsは、クライアント接続を構成するキーと値のペアの配列となります。 以下は、使用可能なオプションの一部です(完全なリストについては www.php.net/を参照してください)。
  • soap_version: 使用するSOAPプロトコルのバージョン、定数SOAP_1_1またはSOAP_1_2のいずれかを使用可能
  • login: SOAPサーバーでHTTP認証を使用する場合に使用するログイン名
  • password: SOAPサーバーでHTTP認証を使用する場合に使用するパスワード
  • proxy_host: プロキシ・サーバーを介して接続する場合のサーバーのアドレス
  • proxy_port: プロキシ・サーバーを介して接続する場合のプロキシのリスニング・ポート
  • proxy_login: プロキシ・サーバーを介して接続する場合に、ログインに使用するユーザー名
  • proxy_password: プロキシ・サーバーを介して接続する場合に、ログインに使用するパスワード
  • local_cert: セキュアなHTTP(https)を介して通信をおこなうSOAPサーバーに接続する場合の、ローカル認証ファイルの場所
  • passphrase: 認証ファイルがある場合に、local_certと一緒に使用してその認証ファイル用のパスフレーズを提供する
  • compression: trueに設定されている場合、PHPにおいて圧縮されたHTTP要求を使用してSOAPサーバーとの通信が試行される
  • classmap: クライアントで使用するために、WSDLデータ型をPHPクラスにマッピングするキー/値ペアの配列
PHPのSOAPクライアントがWSDLドキュメントを使用してインスタンス化されると、返されたクライアント・オブジェクトを使用して、ネイティブのPHPコールと同じようにSOAPサーバーで公開されているメソッドを呼び出したり、発生するSOAP障害をネイティブのPHP例外と同じように処理したりすることができます。 たとえば、最初のmathクラスを使用したSOAPサービスのサンプルの場合、完全なPHP SOAPクライアントは以下のようになります。
<?php

    $client = new SoapClient(a??http://www.example.com/math.wsdla??);
    
    try {
        $result = $client->div(10,rand(0,5); // will cause a Soap Fault if divide by zero
        print a??The answer is: $resulta??;
    } catch(SoapFault $f) {
        print a??Sorry an error was caught executing your request: {$e->getMessage()}a??;
    }
?>

上記のとおり、SoapClientクラスによるSOAPサービスへのアクセスは、SOAPサービスがPHPで実装されているかどうかに関係なく簡単です。 実際、SOAPサービスを介して、書籍データベースのWebベース管理システムを作成することは、取るに足りない作業といってもいいでしょう。 以下に示すとおり、簡単な問合せインタフェースのロジックとインタフェースを開発し、実際にSOAPサービスと直接連携させるには、明らかにより多くのコーディングが必要でした。

ご使用のブラウザはインライン・フレームをサポートしていないようです。 このリストを参照するには こちらをクリックしてください。

これを実行すると、見栄えはあまりよくありませんが、PHPを基盤としたWebサービスを介した、Oracleデータベースの機能的なインタフェースとなります。

図2

結論

以上で、PHPを使用して、Oracleを基盤としたデータベースとSOAPの機能と組み合わた、強力なWebサービスを作成するために必要なことをすべて学習しました。 インターネットが進化して「Web 2.0」ともいわれる次世代のWebへと近づくにつれて、こうしたサービスが、クライアント側でのリッチなインターネット体験を保証するサービス指向アーキテクチャの重要な側面となりつつあります。 この記事では、PHPにおけるSOAPの機能の詳細すべてを説明したわけではありませんが、まれな状況(WSDLドキュメントを使用しないサービスへの接続など)でのみ使用される機能に関することを除き、重要な点についてはカバーしています。 John Coggeshall氏[ http://www.coggeshall.org/]は、 Zend Technologies社の上級技術コンサルタントを務め、世界中のクライアントに専門的なサービスを提供しています。 1997年にPHPに入社して以来、Sams Publishing社、Apress社、O'Reilly社をはじめとする業界最大手の出版社から、PHPテクノロジーに関する3冊の書籍と100を超える記事を著しています。 また、PHPコアにおいてもtidy拡張の作者として貢献しているほか、Zend Education Advisory Boardのメンバーとして、そして世界各地で開催されているPHP関連の会議で講演をおこなうなど、意欲的に活動しています。

ご意見ご感想をお寄せください。