みなさんこんにちは! 最近出会った問題についてお話したいと思います。 この記事で説明した私の経験が、Zend Framework、特にZend_Soap_AutoDiscoverクラスを使用してSOAPサービスを開発している人々の時間と神経細胞の節約に役立つことを願っています。
問題は、Zend_Soap_AutoDiscoverが正しく動作せず、よく知られているコードオプティマイザーeAcceleratorと組み合わされていることです。 つまり、正確には、 ReflectionClass :: getDocComment()メソッドは正しく機能しません。 しかし...まず最初に。
ケースの99%でSOAPサービスを開発する場合、プログラマーは、サービスの説明として使用される膨大なWSDLドキュメントを自動的に生成するタスクに直面します。 このようなWSDLには、サービスによって提供されるデータ型とメソッドの説明が含まれています。 私の知る限り、多くのプログラマーはこの問題を解決するためにZend Frameworkが提供するツール、つまりZend_Soap_AutoDiscoverを使用しています( PEAR SOAPライブラリを使用してこのような問題を解決できるという噂がありますが、この場合、私は特定のことは言えないので、それを使用しました)。
Zend_Soapの使用方法については説明しません。これらのすべては、 公式ドキュメントまたはこのサイトの記事(たとえば、ここ)で完全に説明されています 。
Zend_Soap_AutoDiscoverを使用する主な利点は何ですか? このクラスを使用すると、パラメーターとして指定したクラスに基づいてWSDLを自動的に構築できます。 したがって、WSDLは、クラスの変更に基づいて(ビルドの基礎に基づいて)動的に再構築されます。 ただし、重要な点が1つあります。Zend_Soap_AutoDiscoverは、PHPDocのスタイルで記述されたコメントを使用して、メソッドパラメーターのデータ型を決定します。 したがって、コードは十分に文書化する必要があります。
以下は、単純なクライアント/サーバー相互作用の例です。
3つのスクリプトがあるとします。
soap-client.php-SOAPサービスクライアント
soap-server.php-SOAPサービスの実装
soap-server-model.php-SOAPサービスが構築される基礎となるクラス
各スクリプトのコードリストはシンプルでわかりやすく、以下に示します。
soap-client.php :
*このソースコードは、 ソースコードハイライターで強調表示されました。
- require_once 'Zend / Soap / Client.php' ;
- $ wsdlUri = 'http://localhost/soap-server.php?wsdl' ;
- {
- $ client = new Zend_Soap_Client($ wsdlUri);
- echo $ client-> showSomething( 'test' 、46);
- echo '<br />' ;
- echo 'the end' ;
- } catch (例外$ e){
- echo 'エラー:' 。 $ e-> getMessage();
- }
soap-server.php :
*このソースコードは、 ソースコードハイライターで強調表示されました。
- require_once 'soap-server-model.php' ;
- $ wsdlUri = 'http://localhost/soap-server.php?wsdl' ;
- if (isset($ _ GET [ 'wsdl' ])){
- require_once 'Zend / Soap / AutoDiscover.php' ;
- $ autodiscover = new Zend_Soap_AutoDiscover();
- $ autodiscover-> setClass( 'SoapModel' );
- $ autodiscover-> handle();
- } else {
- require_once 'Zend / Soap / Server.php' ;
- $ soap = new Zend_Soap_Server($ wsdlUri);
- $ soap-> setClass( 'SoapModel' );
- $ soap->ハンドル();
- }
soap-server-model.php :
*このソースコードは、 ソースコードハイライターで強調表示されました。
- クラス SoapModel
- {
- / **
- * Zend_Soap_AutoDiscoverを使用してSOAPサーバーをテストする方法
- *
- * param string $ word
- * param int $ num
- * 戻り文字列
- * /
- パブリック 関数 showSomething($ word、$ num)
- {
- 「サーバーが言った:」を 返し ます 。 $単語。 」 $ num;
- }
- }
php.iniでは、SOAP設定(クライアント側)は次のとおりです。
[石鹸]
soap.wsdl_cache_enabled = 0
soap.wsdl_cache_dir = "/ tmp"
soap.wsdl_cache_ttl = 18000
soap.wsdl_cache_limit = 0
つまり、テスト目的で、WSDLキャッシングを無効にします
クライアントスクリプトを実行すると、期待どおりの "サーバーが言った:テスト46"ではなく、 "最後"だけが表示されます。 返されたWSDLを確認し(この例では、URLで利用可能です: localhost / soap-server.php?Wsdl&a = 1 )、表示されるのは予想されたものではありません :
*このソースコードは、 ソースコードハイライターで強調表示されました。
- <? xml バージョン = "1.0" ? >
- < 定義 xmlns = " schemas.xmlsoap.org/wsdl " xmlns:tns = " localhost / soap-server.php " xmlns:soap = " schemas.xmlsoap.org/wsdl/soap " xmlns:xsd = " www.w3。 org / 2001 / XMLSchema " xmlns:soap-enc =" schemas.xmlsoap.org/soap/encoding " xmlns:wsdl =" schemas.xmlsoap.org/wsdl " name =" SoapModel " targetNamespace =" localhost / soap-server。 php " >
- < タイプ >
- < xsd:schema targetNamespace = " localhost / soap-server.php " />
- </ タイプ >
- < portType name = "SoapModelPort" >
- < 操作 名 = "showSomething" >
- < documentation > Zend_Soap_AutoDiscoverを使用してSOAPサーバーをテストする方法</ documentation >
- < 入力 メッセージ = "tns:showSomethingIn" />
- < 出力 メッセージ = "tns:showSomethingOut" />
- </ 操作 >
- </ portType >
- < バインディング 名 = "SoapModelBinding" タイプ = "tns:SoapModelPort" >
- < soap:バインディング スタイル = "rpc" transport = " schemas.xmlsoap.org/soap/http " />
- < 操作 名 = "showSomething" > < soap:操作 soapAction = " localhost / soap-server.php#showSomething " />
- < 入力 >
- < soap:body use = "encoded" encodingStyle = " schemas.xmlsoap.org/soap/encoding " namespace = " localhost / soap-server.php " />
- </ 入力 >
- < 出力 >
- < soap:body use = "encoded" encodingStyle = " schemas.xmlsoap.org/soap/encoding " namespace = " localhost / soap-server.php " />
- </ 出力 >
- </ 操作 >
- </ バインディング >
- < サービス 名 = "SoapModelService" >
- < ポート 名 = "SoapModelPort" binding = "tns:SoapModelBinding" >
- < soap:アドレスの 場所 = " localhost / soap-server.php " />
- </ ポート >
- </ サービス >
- < メッセージ 名 = "showSomethingIn" >
- < パーツ 名 = "単語" タイプ = "xsd:文字列" />
- < パーツ 名 = "num" タイプ = "xsd:int" />
- </ メッセージ >
- < メッセージ 名 = "showSomethingOut" >
- < パーツ 名 = "return" type = "xsd:string" />
- </ メッセージ >
- </ 定義 >
次のコードが発行されます。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- <? xml バージョン = "1.0" ? >
- < 定義 xmlns = " schemas.xmlsoap.org/wsdl " xmlns:tns = " localhost / soap-server.php " xmlns:soap = " schemas.xmlsoap.org/wsdl/soap " xmlns:xsd = " www.w3。 org / 2001 / XMLSchema " xmlns:soap-enc =" schemas.xmlsoap.org/soap/encoding " xmlns:wsdl =" schemas.xmlsoap.org/wsdl " name =" SoapModel " targetNamespace =" localhost / soap-server。 php " >
- < タイプ >
- < xsd:schema targetNamespace = " localhost / soap-server.php " />
- </ タイプ >
- < portType name = "SoapModelPort" >
- < 操作 名 = "showSomething" >
- < ドキュメント > showSomething </ ドキュメント >
- < 入力 メッセージ = "tns:showSomethingIn" />
- </ 操作 >
- </ portType >
- < バインディング 名 = "SoapModelBinding" タイプ = "tns:SoapModelPort" >
- < soap:バインディング スタイル = "rpc" transport = " schemas.xmlsoap.org/soap/http " />
- < 操作 名 = "showSomething" >
- < soap:operation soapAction = " localhost / soap-server.php#showSomething " />
- < 入力 >
- < soap:body use = "encoded" encodingStyle = " schemas.xmlsoap.org/soap/encoding " namespace = " localhost / soap-server.php " />
- </ 入力 >
- </ 操作 >
- </ バインディング >
- < サービス 名 = "SoapModelService" >
- < ポート 名 = "SoapModelPort" binding = "tns:SoapModelBinding" >
- < soap:アドレスの 場所 = " localhost / soap-server.php " />
- </ ポート >
- </ サービス >
- < メッセージ 名 = "showSomethingIn" >
- < パーツ 名 = "単語" タイプ = "xsd:anyType" />
- < パーツ 名 = "num" タイプ = "xsd:anyType" />
- </ メッセージ >
- </ 定義 >
一般に、WTF oOのマニュアルと悲鳴を調べて、誤動作を長時間検索した後、eAcceleratorをオフにするとすべてが正常に機能することが判明しました。 なぜこれが起こっているのですか? 内部実装のZend_Soap_AutoDiscoverは、 Reflectionsメカニズム(正確にはZend_Server_Reflectionクラスがこれを担当しています)を使用して、メソッドコメントからパラメーターを取得します。 eAcceleratorは、バイトコードの作成中にスクリプトを最初に呼び出した後、すべてのコメントを削除します。その結果、上記の以下の問題が発生します。 論理的ですが、非常に重要です。
解決策は何ですか?
1. eAcceleratorを拒否します。 そして、たとえばAPCを使用します(私は個人的にチェックしました-APCにはそのような問題はありませんでした)。
2.コメントが重要なファイルを最適化しないために、eAcceleratorフィルターを使用します。 これは次のように行われます。
ini_set( 'eaccelerator.filter'、 '!soap-server-model.php');
3. red_pilotユーザーからの解決策:インストール中に、with-eaccelerator-doc-comment-inclusionキーを使用してeAcceleratorを構成します。
./configure -–with-eaccelerator-doc-comment-inclusion
PS一般に、指摘された問題は、「ある種のZendクラスがeAcceleratorで動作しない」よりも少し深いです。 これはあまり頻繁ではありませんが、開発ではReflectionsメカニズム( ReflectionClass :: getDocComment() )が使用され、多くの場合、クラスに関するコメントが開発された機能に関与します。
PSSよくあることですが、eAcceleratorのようなツールは本番サーバーで使用されます。誰かがロケールにインストールするときはめったにありません。 その結果、ロケールで完全に機能するコードは、完全に非自明な方法で本番環境で機能しない場合があります。
PSSS Zend_Soapは、他のほとんどのZendコンポーネントと同様に、フレームワーク自体とは別に使用できます。 そのため、たとえば、現在の開発プロジェクトでは、Zend_Db、Zend_Form、Zend_Soapの3つのZFコンポーネントのみを使用しています。 また、たとえば、誰かがZend_Soap_AutoDiscoverに参加したい場合、プロジェクト全体がZFにある必要はありません。 必要なすべてのZFクラスを転送するだけで十分です。 実際、非常に便利なZend_Soap_AutoDiscoverを使用することに加えて、Zend_Soapは組み込みのSoap機能を弱く拡張します。 したがって、Soapサービスの作成に困惑していない場合、ネイティブSoapClientをZend_Soap_Clientに変更することはあまり有用ではありません。