kawano

SOAP API を PHPで叩く

SOAP API を PHPで叩く

SOAP通信、WSDL文書の構造、PHPからの呼び出し方について、さーっと調べてみました。
結論から言うとSOAP、WSDLの詳細を知らなくてもPHPのSOAPクライアントライブラリを使えば手軽に通信はできると思いました...(汗)
WSDLの構造が複雑でした。よりわかりやすいREST APIに取って代わられたのも納得です。

SOAP とは

  • Single Object Access Protocolの略で通信プロトコルのひとつです。
  • APIの実装で使います。ネットワーク内の異なるコンピュータ上同士で情報を交換する技術です。
  • やりとりする情報のフォーマットはXMLです。
  • HTTPやSMTPプロトコルを下位プロトコルとして使います。
  • SOAPメッセージをおくるとSOAPレスポンスが返されます。

SOAPリクエストフォーマット(例)

Envelope(封筒)のなかに、Header と Body があります。
Bodyの中に送信するものを指定します。

<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
 <env:Header>
 </env:Header>
 <env:Body>
 </env:Body>
</env:Envelope>

SOAPレスポンスフォーマット(例)

Envelope(封筒)のなかにBodyがあります

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Body>
  </env:Body>
</env:Envelope>

WSDLとは

  • Web Service Description Language。その名の通り、Webサービスが"記述"されているXMLのことです。(全APIの使い方が書かれたドキュメントみたいなイメージ)
  • SOAP APIを使うときの多くはこの文書をサービス提供者からもらうことになります。SOAPのEnvelope(封筒)のBodyに入れて送信します。
  • xmlの上部から定義をし、その内容を後で参照...という書き方をします。
  • 抽象的な定義、具体的な定義の2パートで構成されています。

構造

<wsdl:definition> ... ルート要素。
    |_<wsdl:types> ... APIパラメタのデータ型。
    |_<wsdl:message> ... operation要素で使うAPIのパラメタ。
        |_<wsdl:part> ... types要素で定義したtype属性をもつ。
    |_<wsdl:portType> ... operation要素(APIメソッド)の集合
        |_<wsdl:operation> ... APIのメソッドのようなもの。
            |_<wsdl:input> ... リクエスト時のパラメタ。
            |_<wsdl:output> ... 成功時に返されるパラメタ。
            |_<wsdl:fault> ... 失敗時に返されるパラメタ。
---↑抽象的な定義↑---------------------------------------
---↓具体的な定義↓---------------------------------------
    |_<wsdl:binding> ... bindingのために使う外部テクノロジーの定義。つまりWSDLをやり取りする方法。多くの場合SOAP。
        |_<wsdl:operation> ... operation要素で定義したそれぞれの操作のバインディング指定。
            |_<wsdl:input>
            |_<wsdl:output>
            |_<wsdl:fault>
    |_<wsdl:service> ... binding要素で定義したものがportに指定されている。
        |_<wsdl:port>

フォーマット(単純化した例)

Webサービスのキホン(4) WSDL:Webサービスのインターフェイス - @ITにあるリスト5の内容は実運用で近い形で単純化されていて(まだ)分かり易かったです。

<wsdl:definitions targetNamespace="このWSDLで参照するtargetNamespaceのURL" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"  xmlns:impl="ネームスペース" xmlns:xsd="http://www.w3.org/2001/XMLSchema"  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:types>
        <xsd:schema elementFormDefault="qualified"  targetNamespace="タイプで参照するネームスペース" xmlns="http://www.w3.org/2001/XMLSchema">
            <element name="データ型A">
                <xsd:complexType>
                    <element name="intDataA" type="xsd:int">
                    </element>
                </xsd:complexType>
            </element>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="リクエストA">
        <wsdl:part name="" element="データ型A">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="レスポンスA">
        <wsdl:part name="" element="データ型A">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="フォールトA">
        <wsdl:part name="" element="データ型A">
        </wsdl:part>
    </wsdl:message>
    <wsdl:portType name="ポートタイプA">
        <wsdl:operation name="操作A" parameterOrder="パラメタA パラメタB">
            <wsdl:input message="impl:リクエストA">
            </wsdl:input>
            <wsdl:output message="impl:レスポンスA">
            </wsdl:output>
            <wsdl:fault message="impl:フォールトA">
            </wsdl:fault>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="バインディングA" type="impl:ポートタイプA">
        <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="操作A">
            <wsdlsoap:operation soapAction=""/>
            <wsdl:input>
                <wsdlsoap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <wsdlsoap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault>
                <wsdlsoap:body use="literal"/>
            </wsdl:fault>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="サービスA">
        <wsdl:port name="ポートA" binding="impl:バインディングA">
            <wsdlsoap:address location="SOAPサーバのロケーションURL"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

SOAP通信とWSDLの関係

こちらの図がとても分かり易かったです。
SOAPという封筒のフォーマットで、HTTPという手段を使い、WSDLの内容を実行すると言うイメージです。

  1. WSDLに記述された操作をプログラムで実行
  2. WSDLで指定されたリモートサーバへその旨リクエスト。このとき通信方法としてSOAPを使う。
  3. リクエストを受け取ったサーバは内容を解析し、実際の処理(COM,CORBA,JavaBeansなどの言語で書かれている)を実行。
  4. サーバはクライアントへSOAPのフォーマットでレスポンスを返す。

PHPでSOAP

PHP SOAP拡張モジュール を使うことで、SOAPクライアント/サーバの実装ができます。
実のところWSDLやSOAPの構造をあまり知らなくても手軽に使うことができると思いました。

SOAPクライアントを用いてWSDLのAPIを叩く

今回はWSDLモードで動かします。

  1. SOAP共通ライブラリを読み込む(未インストールの場合)
  2. SoapClientインスタンスを生成
    コンストラクタの第一引数にはWSDL文書の場所, 第二引数にはoptionsを指定。第二引数は必須ではありませんが、今回は失敗した時に辿れるようtrace指定と、wsdlのキャッシュを行わない設定にしました。
  3. __setLocation()でSOAP APIのエンドポイントをクライアントへ指定。
  4. APIサーバにあるWSDL文書のメソッドを実行(この例でいうとupdateメソッド)。
  5. 実行に失敗した場合にSoapFaultオブジェクトを取得、メッセージをログファイルへ出力。
<?php

~ 略 ~

$log = 'ログファイルへのパス' ;
try {
    $WSDL = 'WSDL文書へのパス';
    $soapClient = new SoapClient($WSDL, array('trace' => true, 'cache_wsdl' => WSDL_CACHE_NONE));
    $soapClient->__setLocation('SOAP APIのエンドポイントURLを指定');
    $soapClient->update('リクエストパラメタ');
} catch (\SoapFault $soapFault) {
    $msg = $soapFault->detail->fault->errorMessage;
    $code = $soapFault->detail->fault->errorCode;
    $detail = "Message : {$msg}, Code : {$code}";
    file_put_contents($log, date('Y-m-d H:i:s').' ERROR details='.$detail);
}

参考

IBM developerWorks WSDLによるWebサービスの配置 シリーズ
atmarkit Webサービスのキホン シリーズ
PHP:SOAP Manual
w3cのwsdlページ

お問い合わせはこちら