📜 ⬆️ ⬇️

Zend_Soap_AutoDiscover and eAccelerator


Hello! I want to talk about one problem that I encountered recently. I hope that my experience described in this article will help save hours and nerve cells to those who develop SOAP services using the Zend Framework, and in particular the Zend_Soap_AutoDiscover class.
The problem is that Zend_Soap_AutoDiscover does not work correctly together with the well-known code optimizer eAccelerator . Namely, to be precise, the ReflectionClass :: getDocComment () method does not work correctly. But first things first.

When developing SOAP services in 99% of cases, the programmer is faced with the task of automatically generating hefty WSDL documents, which are used as a description of services. Such WSDLs contain descriptions of data types and methods that are provided by the services. As far as I know, many programmers use the tools provided by the Zend Framework to solve this problem, namely Zend_Soap_AutoDiscover (it is rumored that PEAR SOAP can be used to solve this problem, but in this case I can’t say anything specific, because used it).

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

What is the main advantage of using Zend_Soap_AutoDiscover? With this class, we can automatically build WSDL, based on the classes that we specify as parameters. Accordingly, WSDL will be dynamically rebuilt depending on the change of classes (on the basis of which it is built). But there is one important point - Zend_Soap_AutoDiscover uses PHPDoc comments to define the data types of the parameters of the methods. Therefore, the code should be well documented.
')
The following is an example of a simple client-server interaction.
Suppose we have 3 scripts:
soap-client.php - SOAP service client
soap-server.php - SOAP service implementation
soap-server-model.php - the class on which the SOAP service is based

The code listing of each script is simple and straightforward and is presented below:

soap-client.php :
  1. require_once 'Zend / Soap / Client.php' ;
  2. $ wsdlUri = 'http: //localhost/soap-server.php? wsdl' ;
  3. try {
  4. $ client = new Zend_Soap_Client ($ wsdlUri);
  5. echo $ client-> showSomething ( 'test' , 46);
  6. echo '<br />' ;
  7. echo 'the end' ;
  8. } catch (Exception $ e) {
  9. echo 'Error:' . $ e-> getMessage ();
  10. }
* This source code was highlighted with Source Code Highlighter .


soap-server.php :
  1. require_once 'soap-server-model.php' ;
  2. $ wsdlUri = 'http: //localhost/soap-server.php? wsdl' ;
  3. if (isset ($ _ GET [ 'wsdl' ])) {
  4. require_once 'Zend / Soap / AutoDiscover.php' ;
  5. $ autodiscover = new Zend_Soap_AutoDiscover ();
  6. $ autodiscover-> setClass ( 'SoapModel' );
  7. $ autodiscover-> handle ();
  8. } else {
  9. require_once 'Zend / Soap / Server.php' ;
  10. $ soap = new Zend_Soap_Server ($ wsdlUri);
  11. $ soap-> setClass ( 'SoapModel' );
  12. $ soap-> handle ();
  13. }
* This source code was highlighted with Source Code Highlighter .


soap-server-model.php :
  1. class SoapModel
  2. {
  3. / **
  4. * Method for testing SOAP server with Zend_Soap_AutoDiscover
  5. *
  6. * param string $ word
  7. * param int $ num
  8. * return string
  9. * /
  10. public function showSomething ($ word, $ num)
  11. {
  12. return 'Server said:' . $ word. '' . $ num;
  13. }
  14. }
* This source code was highlighted with Source Code Highlighter .


In php.ini, the SOAP settings (on the 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 WSDL caching

It would seem that everything is cool, we run the client script and instead of the expected “Server said: test 46” we see only the “end”. We look at the returned WSDL (in the example it is available at the URL: localhost / soap-server.php? Wsdl & a = 1 ), and what we see instead of the expected one:

  1. <? xml version = "1.0" ? >
  2. < 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 » >
  3. < types >
  4. < xsd: schema targetNamespace = " localhost / soap-server.php " />
  5. </ types >
  6. < portType name = "SoapModelPort" >
  7. < operation name = "showSomething" >
  8. < documentation > Method for testing SOAP server with Zend_Soap_AutoDiscover </ documentation >
  9. < input message = "tns: showSomethingIn" />
  10. < output message = "tns: showSomethingOut" />
  11. </ operation >
  12. </ portType >
  13. < binding name = "SoapModelBinding" type = "tns: SoapModelPort" >
  14. < soap: binding style = "rpc" transport = " schemas.xmlsoap.org/soap/http " />
  15. < operation name = "showSomething" > < soap: operation soapAction = " localhost / soap-server.php # showSomething " />
  16. < input >
  17. < soap: body use = "encoded" encodingStyle = " schemas.xmlsoap.org/soap/encoding " namespace = " localhost / soap-server.php " />
  18. </ input >
  19. < output >
  20. < soap: body use = "encoded" encodingStyle = " schemas.xmlsoap.org/soap/encoding " namespace = " localhost / soap-server.php " />
  21. </ output >
  22. </ operation >
  23. </ binding >
  24. < service name = "SoapModelService" >
  25. < port name = "SoapModelPort" binding = "tns: SoapModelBinding" >
  26. < soap: address location = " localhost / soap-server.php " />
  27. </ port >
  28. </ service >
  29. < message name = "showSomethingIn" >
  30. < part name = "word" type = "xsd: string" />
  31. < part name = "num" type = "xsd: int" />
  32. </ message >
  33. < message name = "showSomethingOut" >
  34. < part name = "return" type = "xsd: string" />
  35. </ message >
  36. </ definitions >
* This source code was highlighted with Source Code Highlighter .


the following code is issued:

  1. <? xml version = "1.0" ? >
  2. < 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 » >
  3. < types >
  4. < xsd: schema targetNamespace = " localhost / soap-server.php " />
  5. </ types >
  6. < portType name = "SoapModelPort" >
  7. < operation name = "showSomething" >
  8. < documentation > showSomething </ documentation >
  9. < input message = "tns: showSomethingIn" />
  10. </ operation >
  11. </ portType >
  12. < binding name = "SoapModelBinding" type = "tns: SoapModelPort" >
  13. < soap: binding style = "rpc" transport = " schemas.xmlsoap.org/soap/http " />
  14. < operation name = "showSomething" >
  15. < soap: operation soapAction = " localhost / soap-server.php # showSomething " />
  16. < input >
  17. < soap: body use = "encoded" encodingStyle = " schemas.xmlsoap.org/soap/encoding " namespace = " localhost / soap-server.php " />
  18. </ input >
  19. </ operation >
  20. </ binding >
  21. < service name = "SoapModelService" >
  22. < port name = "SoapModelPort" binding = "tns: SoapModelBinding" >
  23. < soap: address location = " localhost / soap-server.php " />
  24. </ port >
  25. </ service >
  26. < message name = "showSomethingIn" >
  27. < part name = "word" type = "xsd: anyType" />
  28. < part name = "num" type = "xsd: anyType" />
  29. </ message >
  30. </ definitions >
* This source code was highlighted with Source Code Highlighter .


In general, after a long troubleshooting, studying the manuals and screams of WTF oO, it turned out that everything works fine after turning off the eAccelerator. Why is this happening? Zend_Soap_AutoDiscover in its internal implementation uses the Reflections mechanism (to be exact, the Zend_Server_Reflection class is responsible for this) to get parameters from the comments to the methods. eAccelerator, in turn, after the first access to the script during the creation of the bytecode, deletes all comments, as a result of which we have the following trabble described above. It is logical, but rather nontrivial.

What are the solutions?
1. Abandon eAccelerator. And use, for example, APC (I personally checked that there were no such problems with APC).
2. Use eAccelerator's filters in order not to optimize files where comments are important. This is done like this:
ini_set ('eaccelerator.filter', '! soap-server-model.php');

3. Solution from the user red_pilot : when installing, configure the eAccelerator with the key with-eaccelerator-doc-comment-inclusion, that is:
./configure -–with-eaccelerator-doc-comment-inclusion


PS In general, the above mentioned problem is somewhat deeper than just “some kind of Zend class doesn’t work with eAccelerator”. Let it not often, but nevertheless the mechanisms Reflections ( ReflectionClass :: getDocComment () ) are used in the development, and often comments to classes can play a role in the developed functionality.

PSS As is often the case, tools like Tipo eAccelerator are used on production servers, and very rarely when someone installs them on their locale. As a result, ideally running locale code may not work at all in production.

PSSS Zend_Soap, like most other Zend components, can be used separately from the Framework itself. For example, in the current development project we use only 3 components of ZF, namely Zend_Db, Zend_Form and Zend_Soap. And if, for example, someone wants to run Zend_Soap_AutoDiscover, it’s not at all necessary that the whole project be on ZF. It suffices to transfer all the necessary classes ZF. In truth, besides using the super-useful Zend_Soap_AutoDiscover, the rest of Zend_Soap weakly expands the built-in Soap functionality. So if you are not puzzled by the creation of Soap-services, it is unlikely to be very useful to change the native SoapClient to Zend_Soap_Client .

Source: https://habr.com/ru/post/122710/


All Articles