Zend_Soap_AutoDiscover and eAccelerator


Hello! I want to tell you about a problem I encountered recently. I hope that my experience described in this article will help you to save hours and nerve cells those people who are currently developing a SOAP service using Zend Framework, and in particular of class Zend_Soap_AutoDiscover.
The problem is that Zend_Soap_AutoDiscover works correctly coupled with the use of known optimizer code eAccelerator. Namely, to be precise incorrect working method ReflectionClass::getDocComment(). But... everything in order.

When developing SOAP services in 99% of cases, a programmer faces the task of automatically generating a hefty WSDL documents, which are used as service descriptions. These WSDL descriptions contain data types and methods provided by the services. As far as I know, many programmers to solve this task use tools provided by Zend Framework, namely Zend_Soap_AutoDiscover (rumor has it that to solve this problem you can use the library PEAR SOAP, but in this case I can not say anything specific, because I do not used it).

I will not dwell on the description of how to use Zend_Soap, all of this is described in official documentation or in articles on this site — for example, here.

What is the main advantage of using Zend_Soap_AutoDiscover? Using this class we can automatically build the WSDL based on the classes that we specify as parameters. Appropriately WSDL will be tuned dynamically depending on the class changes (on the basis of which it is built). But there is one important point — Zend_Soap_AutoDiscover uses comments in the style of PHPDoc to define the data types of the method parameters. So the code should be well documented.

The following is an example of a simple client-server interaction.
Let's suppose we have 3 script:
soap-client.php client SOAP service
soap-server.php — implementation of the SOAP service
soap-server-model.php class, on the basis of which to build a SOAP service

Code listing of each script is easy and simple and is given below:

soap-client.php:
    the
  1. require_once 'Zend/Soap/Client.php';
  2. the

  3. $wsdlUri = 'http://localhost/soap-server.php?wsdl';
  4. try { the

  5. $client = new Zend_Soap_Client($wsdlUri);
  6. the
  7. echo $client->showSomething('test', 46);
  8. the
  9. echo '<br />';
  10. the
  11. echo 'the end';
  12. the
  13. } catch (Exception $e) {
  14. the
  15. echo 'Error: '. $e->getMessage();
  16. the
  17. }
* This source code was highlighted with Source Code Highlighter.


soap-server.php:
    the
  1. require_once 'soap-server-model.php';
  2. the

  3. $wsdlUri = 'http://localhost/soap-server.php?wsdl';
  4. if(isset($_GET['wsdl'])) { the

  5. require_once 'Zend/Soap/AutoDiscover.php';
  6. the
  7. $autodiscover = new Zend_Soap_AutoDiscover();
  8. the
  9. $autodiscover- > setClass('SoapModel');
  10. the
  11. $autodiscover- > handle();
  12. the
  13. } else {
  14. the
  15. require_once 'Zend/Soap/Server.php';
  16. the
  17. $soap = new Zend_Soap_Server($wsdlUri);
  18. the
  19. $soap- > setClass('SoapModel');
  20. the
  21. $soap->handle();
  22. the
  23. }
* This source code was highlighted with Source Code Highlighter.


soap-server-model.php:

    class SoapModel

    { the

  1. /**
  2. * Method for testing a SOAP server with Zend_Soap_AutoDiscover

    *

    * param string $word

    * param int $num

    * return string

    public function showSomething($word, $num)

    {

    return 'Server said: '. $word. ' '. $num; the

  3. }
  4. the
  5. }
* This source code was highlighted with Source Code Highlighter.


In php.ini settings SOAP (client-side) are as follows:
[soap]
soap.wsdl_cache_enabled=0
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=18000
soap.wsdl_cache_limit = 0

that is, for testing purposes, disable caching of WSDL

It would seem everything is cool, run the client script and instead of the expected "Server said: test 46" only see "the end". Watch the returned WSDL (in the example it is available at the URL: localhost/soap-server.php?wsdl&a=1), and what do we see — instead of the expected:

    <?xml version="1.0"?>

    <definitions 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">

    <types>

    <xsd:schema targetNamespace="localhost/soap-server.php"/>

    </types>

    <portType name="SoapModelPort">

    <operation name="showSomething">

    <documentation>Method for testing a SOAP server with Zend_Soap_AutoDiscover</documentation>

    <input message="tns:showSomethingIn"/>

    <output message="tns:showSomethingOut"/>

    </operation>

    </portType>

    <binding name="SoapModelBinding" type="tns:SoapModelPort">

    <soap:binding style="rpc" transport="schemas.xmlsoap.org/soap/http"/>

    <operation name="showSomething"><soap:operation soapAction="localhost/soap-server.php#showSomething"/>

    <input>

    <soap:body use="encoded" encodingStyle="schemas.xmlsoap.org/soap/encoding" namespace="localhost/soap-server.php"/>

    </input>

    <output>

    <soap:body use="encoded" encodingStyle="schemas.xmlsoap.org/soap/encoding" namespace="localhost/soap-server.php"/>

    </operation>

    </binding>

    <service name="SoapModelService">

    <port name="SoapModelPort" binding="tns:SoapModelBinding">

    <soap:address location="localhost/soap-server.php"/>

    </port>

    </service>

    <message name="showSomethingIn">

    <part name="word" type="xsd:string"/>

    <part name="num" type="xsd:int"/>

    </message>

    <message name="showSomethingOut">

    <part name="return" type="xsd:string"/>

    </message>

    </definitions>

* This source code was highlighted with Source Code Highlighter.


I get the following code:

    <?xml version="1.0"?>

    <definitions 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">

    <types>

    <xsd:schema targetNamespace="localhost/soap-server.php"/>

    </types>

    <portType name="SoapModelPort">

    <operation name="showSomething">

    <documentation>showSomething</documentation>

    <input message="tns:showSomethingIn"/>

    </operation>

    </portType>

    <binding name="SoapModelBinding" type="tns:SoapModelPort">

    <soap:binding style="rpc" transport="schemas.xmlsoap.org/soap/http"/>

    <operation name="showSomething">

    <input>

    <soap:body use="encoded" encodingStyle="schemas.xmlsoap.org/soap/encoding" namespace="localhost/soap-server.php"/>

    </input>

    </operation>

    </binding>

    <service name="SoapModelService">

    <port name="SoapModelPort" binding="tns:SoapModelBinding">

    <soap:address location="localhost/soap-server.php"/>

    </port>

    </service>

    <message name="showSomethingIn">

    <part name="word" type="xsd:anyType"/>

    <part name="num" type="xsd:anyType"/>

    </message>

    </definitions>

* This source code was highlighted with Source Code Highlighter.


In General, after a long search of malfunctions, study the manuals and screams WTF o.O it turned out that everything works fine after disabling eAccelerator. Why is this happening? Zend_Soap_AutoDiscover in its internal implementation uses the mechanism of Reflections (to be precise it is the class Zend_Server_Reflection), to retrieve the settings of the comments to the methods. eAccelerator, in turn, after the first access to the script during the creation of bytecode removes all comments, with the result that we have the following the above trabble. It is logical, but is not trivial.

What are the solutions?
1. To unsubscribe from eAccelerator. And use for example APC (personally checked with APC such problems were not observed).
2. Use the filters eAccelerator-and, not to optimize files with important comments. This is done like this:
ini_set('eaccelerator.filter', '!soap-server-model.php');

3. The decision from the user red_pilot: when set to configurate eAccelerator with the switch with-eaccelerator-doc-comment-inclusion, that is:
./configure --with-eaccelerator-doc-comment-inclusion


PS In the above identified problem is a little deeper than just "some kind of class in that Zend is not working with eAccelerator". Though it's not often, nevertheless, the mechanisms for Reflections (ReflectionClass::getDocComment()) used in design, and often comments to classes can play a role in developing the functionality.

P. S. S. As often happens, the tool allegedly used eAccelerator on production servers, and very rarely, when someone sets their own locale. In the end, is running on the locale code that may be quite nontrivial to work on production.

P. S. S. S. Zend_Soap, like most of the other Zend component can be used separately from the Framework. For example, in the current development project we are using only 3 components ZF, namely, Zend_Db, Zend_Form and Zend_Soap. And if, for example, someone wants to use himself Zend_Soap_AutoDiscover, it is not necessary that the whole project was in ZF. It is sufficient to transfer all necessary classes in ZF. In truth, in addition to using super-helpful Zend_Soap_AutoDiscover, the rest of the Zend_Soap weakly extends the built-in Soap functionality. So if you are puzzled by the creation of Soap services, it is unlikely to be much useful to change the native SoapClient Zend_Soap_Client.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

March Habrameeting in Kiev

PostgreSQL load testing using JMeter, Yandex.Tank and Overload

Monitoring PostgreSQL with Zabbix