2007/08/07

SoapServerのclassmapオプションとtypemapオプションに関する覚書

ポスト @ 0:51:11 , 修正 @ 2007/08/07 1:11:31 | ,     このエントリーを含むはてなブックマーク

ふと気がつくと、SoapServerのオプションが増えていたので、それの覚書
ref - http://php.net/soap

SoapServerのオプション(コンストラクタoption)についてはマニュアルを確認してもらうとして、SoapServerのオプションにある、classmapとtypemapについて忘れない内に書いておく
ちなみに、このclassmapとtypemapはWSDLモードでしか動かないです。

SoapServerのコンストラクタでは、次のような引数とともにオプションを渡すことが可能です。
今回はこのclassmaptypemapについて少し

$server = new SoapServer($wsdl, array(
    'encoding' => 'UTF-8',
    'version' => SOAP_1_2,
    'uri' => 'urn:HogeNamespace',
    'actor' => 'urn:HogeNameSpace',
    'typemap' => array(
        array(
            'type_ns' => 'urn:HogeNameSpace',
            'type_name' => 'keyElement',
            'from_xml'  => array('SoapTypeUtils', 'toKeyElement')
        )
    ),
    'classmap' => array(
        'keyElement' => 'MyKeyEntity'
    )
));

classmapオプション

classmapオプションはその名のまま、指定されたclassにmappingを行うオプションです。
たとえば、次のようなrequestのをマッピングするには、classmapオプションは次のようになります。

class MyKeyEntity {
    public $user;
    public $key;
    public $timestamp;
}

$server = new SoapServer($wsdl, array(
    'encoding' => 'UTF-8',
    'version' => SOAP_1_2,
    'uri' => 'urn:HogeNamespace',
    'actor' => 'urn:HogeNameSpace',
    'classmap' => array(
        'keyElement' => 'MyKeyEntity'
    )
));
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:HogeNamespace">
    <SOAP-ENV:Body>
        <SOAP-ENV:add>
            <keyElement xsi:type="ns1:keyElement">
                <user>nowel</user>
                <key>dhaTXKD</key>
                <timestamp>1186410253</timestamp>
            </keyElement>
        </SOAP-ENV:add>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

最後にWSDLを書きますが、targetNamespaceが一致しないとうまく変換してくれないです。

typemapオプション

typemapオプションはclassmapと似ていますが、どうやらclassmapのように完全指定の名空間以外について変換できるロジックを委譲してくれる仕組っぽいです(あまり調べてない)
次のrequestについて、typemapオプションを使った例を書きます。

$server = new SoapServer($wsdl, array(
    'encoding' => 'UTF-8',
    'version' => SOAP_1_2,
    'uri' => 'urn:HogeNamespace',
    'actor' => 'urn:HogeNameSpace',
    'typemap' => array(
        array(
            'type_ns' => 'urn:HogeNameSpace',
            'type_name' => 'keyElement',
            'from_xml'  => array('SoapTypeUtils', 'toKeyElement')
        )
    )
));

class MyKeyEntity {
    public $user;
    public $key;
    public $timestamp;
}

class SoapTypeUtils {
    public static function toKeyElement($xml){
        $std = simplexml_load_string($xml);
        $key = new MyKeyEntity;
        $key->user = (string) $std->user;
        $key->key = (string) $std->key;
        $key->timestamp = (double) $std->timestamp;
        return $key;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
        <SOAP-ENV:add>
            <keyElement>
                <user>nowel</user>
                <key>j6SZmtu</key>
                <timestamp>1186413799</timestamp>
            </keyElement>
        </SOAP-ENV:add>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

typemapオプションは複数のxsd:complexTypeについて、変換ロジックを埋め込めるので、ちょっと便利です。
また、名空間が複数存在する場合に使えそうです。

classmapとtypemapに関する注意点

以下のWSDLの場合に、名空間に注意が必要です。注意というかSoapClient::__getTypesに表示されるように正しくwsdlの記述をしないと、classmapとtypemapの変換をおこなってくれません。

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="$name"
             targetNamespace="$namespace"
             xmlns:impl="$namespace"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             xmlns="http://schemas.xmlsoap.org/wsdl/">
    <types>
	    <xsd:schema targetNamespace="$namespace">
	        <xsd:complexType name="keyElement">
	            <xsd:all>
	                <xsd:element name="user" nillable="false" type="xsd:string"/>
	                <xsd:element name="key" nillable="false" type="xsd:string"/>
	                <xsd:element name="timestamp" nillable="false" type="xsd:long"/>
	            </xsd:all>
	        </xsd:complexType>
	        <xsd:complexType name="RemoteAuthenticationException" />
	        <xsd:complexType name="ValidateException" />
	        <xsd:complexType name="RemoteException" />
	    </xsd:schema>
    </types>
    <message name="addRequest">
        <part name="keyElement" type="impl:keyElement" />
    </message>
    <message name="addResponse">
        <part name="return" type="xsd:boolean" />
    </message>
    <message name="RemoteAuthenticationException">
        <part name="fault" type="impl:RemoteAuthenticationException" />
    </message>
    <message name="RemoteException">
        <part name="fault" type="impl:RemoteException" />
    </message>
    <message name="ValidateException">
        <part name="fault" type="impl:ValidateException" />
    </message>
    <portType name="$portType">
        <operation name="add">
            <input message="impl:addRequest" name="addRequest" />
            <output message="impl:addResponse" name="addResponse" />
            <fault message="impl:ValidateException" name="ValidateException"/>
            <fault message="impl:RemoteException" name="RemoteException"/>
        </operation>
    </portType>
    <binding name="$binding" type="impl:$portType">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="add">
            <soap:operation soapAction=""/>
            <input name="addRequest">
                <soap:body namespace="$namespace" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" />
            </input>
            <output name="addResponse">
                <soap:body namespace="$namespace" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" />
            </output>
            <fault name="RemoteAuthenticationException">
                <soap:fault namespace="$namespace" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" />
            </fault>
            <fault name="RemoteException">
                <soap:fault namespace="$namespace" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" />
            </fault>
            <fault name="ValidateException">
                <soap:fault namespace="$namespace" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" />
            </fault>
        </operation>
    </binding>
    <service name="$service">
        <port binding="impl:$binding" name="$port">
            <soap:address location="$location"/>
        </port>
    </service>
</definitions>

やはり、重要なのは、xsd:schema targetNamespace<part name="keyElement" type="impl:keyElement" />の部分ですかね。
ちょっとサンプルとは違うns使ってますけど。

また、このWSDLをSoapClientで読み込んで、__getTypes()をすると次のようなtypesが定義されます。(classmapやtypemapはこれにマッピングするのですね)

  keyElement login(string $user, string $password)
  boolean add(keyElement $keyElement)
  boolean update(keyElement $keyElement)
  boolean remove(keyElement $keyElement)
  boolean logout(keyElement $keyElement)

  struct keyElement {
     string user;
     string key;
     long timestamp;
  }
  struct RemoteAuthenticationException {}
  struct ValidateException {}
  struct RemoteException {}

おまけ、SoapServer::setObjectメソッド

ドキュメントに書かれてなかったですが、php-version/ext/soap/testsでsetObjectなるメソッドがあったので、使ってみると、これが非常に便利でした。
既存のSoapServer::setClassはクラスを指定した上で、コンストラクタパラメータの指定しかできなかったですが、setOjectは既に、生成済みのオブジェクトについてマッピングを行ってくれます。
素晴らしいです。

たとえば、こんなクラスをPOPOで用意する(POPOじゃないけど ^^;)

class HogeService implements IService {

    private $hogeDao;

    public function setHogeDao(HogeDao $dao){
        $this->hogeDao = $dao;
    }

    public function login($user, $password){
        // do something
    }

    public function add(MyKeyElement $element){
        $this->hogeDao->insert($element);
    }

    public function logout($user){
        // do something
    }
}

あとは、S2Containerで管理する

<component class="HogeService">
    <property name="hogeDao">
        <component class="HogeDao">
            <aspect>dao.interceptor</aspect>
        </component>
    </property>
</component>

んで、どっかにinjectするもよし、コンテナから取り出してもよし。SoapServer::setObjectするだけで、Soapのマッピングを行ってくださる。

S2Container_SingletonS2ContainerFactory::init();
$container = S2Container_SingletonS2ContainerFactory::getContainer();
$service = $container->getComponent('HogeService');

$server = new SoapServer(...);
$server->setObject($service);
$server->handle();

SoapServerは何気に便利なのでした。


Trackback

No Trackbacks

Track from Your Website

http://blog.xole.net/trackback/tb.php?id=588

Comment

No Comments

Post Your Comment


*は入力必須です。E-Mailは公開されません。

1 + 2 =