4

this question might be asked, but it is so hard to search for, I just can not find anything about it. Plus it is not easy to ask.

I'm using Zend SOAP's autodiscover to re-create our old SOAP interface (because of switching to micro services and re-working everything).

So far it's working like a charm. But I have one problem in recreating the SOAP response of some services when using lists/arrays.

The old response XML of a SOAP request looked like this. It contains two <SMSEntry>s in the <SMSEntries> list.

<SOAP-ENV:Envelope>
   <SOAP-ENV:Body>
      <ns1:getSMSByTimeSpanResult>
         <AmountOfEntries>2</AmountOfEntries>
         <SMSEntries>
            <SMSEntry></SMSEntry>
            <SMSEntry></SMSEntry>
         </SMSEntries>
      </ns1:getSMSByTimeSpanResult>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

But the recreated response looks like this. It contains two <item>s of type SMSEntry in the <SMSentries> list.

<SOAP-ENV:Envelope>
   <SOAP-ENV:Body>
      <ns1:getSMSByTimeSpanResponse>
         <return xsi:type="ns1:getSMSByTimeSpanResponse">
            <AmountOfEntries xsi:type="xsd:int">2</AmountOfEntries>
            <SMSEntries SOAP-ENC:arrayType="ns1:SMSEntry[2]" xsi:type="ns1:ArrayOfSMSEntry">
               <item xsi:type="ns1:SMSEntry"></item>
               <item xsi:type="ns1:SMSEntry"></item>
            </SMSEntries>
            <DataEx xsi:nil="true"/>
         </return>
      </ns1:getSMSByTimeSpanResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

I have no control of the clients. They might be checking for a SMSEntry with comparing the string. I want to use the class name SMSEntry for the XML-tag name.

Second, I would like to leave out the additional, wrapping everything, <return> tag.

I am using the autodiscover like this:

use Zend\Soap\AutoDiscover;
use Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex;

$autoDiscover = new AutoDiscover(new ArrayOfTypeComplex());
$autoDiscover->setClass(new Standard($sm));

The getSMSByTimeSpanResponse is defined like this:

Standard.php

/**
 * Class getSMSByTimeSpanResponse
 *
 * @package LgxServiceManager\Service
 */
class getSMSByTimeSpanResponse
{
    /**
     * @var int
     */
    public $AmountOfEntries;

    /**
     * @var \LgxServiceManager\Service\SMSEntry[]
     */
    public $SMSEntries;
}

/**
 * Class SMSEntry
 *
 * @package LgxServiceManager\Service
 */
class SMSEntry
{
}

Does anybody have any idea on how to this?

I found some code in the library\Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence.php:122 Where the _addSequenceType() method is setting an attribute hardcoded:

$element->setAttribute('name', 'item');

But this is in type "Sequence" not type "Complex" like I'm using...

Thank you in advance, Philipp


\EDIT

oh man... I just discovered another structure which I don't know how to create with Zend SOAP's autodiscover...

 <mainMember1>SERIALNUMBER</mainMember1>
 <mainMember2>NAMEOFPRODUCT</mainMember2>
 <mainMember3>000000-000-0</mainMember3>
 <Rules>
    <RuleEntry>
       <singleValue>allow</singleValue>
       <ResourceList>
          <Name>generic</Name>
          <ResourceEntry>[...]</ResourceEntry>
          <ResourceEntry>[...]</ResourceEntry>
          <ResourceEntry>[...]</ResourceEntry>
       </ResourceList>
       <ResourceList>
          <Name>default</Name>
          <ResourceEntry>[...]</ResourceEntry>
          <ResourceEntry>[...]</ResourceEntry>
          <ResourceEntry>[...]</ResourceEntry>
       </ResourceList>
    </RuleEntry>
 </Rules>

As you can see, there is a <singleValue> inside the <RuleEntry> but multiple <ResourceList>s. The same structure is used inside the resource lists: One <Name> and multiple <ResourceEntry>...

Is this even possible to handle with autodiscover?

Philipp Gächter
  • 941
  • 8
  • 12
  • 1
    Maybe this gives you some hint: http://stackoverflow.com/questions/17307571/zend-framework-2-soap-autodiscover-and-complex-types – Stephan Weinhold Apr 29 '15 at 12:50
  • 1
    Thank you. I figured that out by myself and use this already as described in my question's code. My WSDL looks totally fine. But the response XML than has those "item" elements... – Philipp Gächter Apr 30 '15 at 10:25
  • Irrespective of answers, I feel your pain. Anytime anyone should hint at using SOAP in the future, you have to very quickly shout "use REST!" at them. – icc97 Jun 23 '17 at 07:34

2 Answers2

3

I know this is an old thread, but I am trying to help followers that have the same problem I was facing.

Nowadays we use Laminas, but the principle is the same.

another way to solve the original problem is to do a little change to the vendor\Laminas\Laminas-Soap\src\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence.php:124 with a code like this:

        // Paulo
        // $element->setAttribute('name', 'item');   // Original Code
           $element->setAttribute('name', substr($childType, 4));
        // Paulo End

Hope this helps everyone

2

For anyone still having above problem: Default response of stdClass() like this:

$array = ['lets', 'test', 'it'];
$response = new stdClass();
$response->results = $array;

Will be like:

 <return xsi:type="SOAP-ENC:Struct">
    <results SOAP-ENC:arrayType="xsd:string[3]" xsi:type="SOAP-ENC:Array">
       <item xsi:type="xsd:string">lets</item>
       <item xsi:type="xsd:string">test</item>
       <item xsi:type="xsd:string">it</item>
    </results>
 </return>

What we can do - we can change array to ArrayObject():

    $array = ['lets', 'test', 'it'];

    $response = new stdClass();
    $response->results = new ArrayObject();
    foreach($array as $item) {
      $response->results->append($item);
    }

which return:

         <return xsi:type="SOAP-ENC:Struct">
            <results xsi:type="SOAP-ENC:Struct">
               <BOGUS xsi:type="xsd:string">lets</BOGUS>
               <BOGUS xsi:type="xsd:string">test</BOGUS>
               <BOGUS xsi:type="xsd:string">it</BOGUS>
            </results>
         </return>

And finally the icing on the cake:

$array = ['lets', 'test', 'it'];

$response = new stdClass();
$response->results = new ArrayObject();
foreach($array as $item) {
    $soapVar = new SoapVar($item,XSD_STRING,NULL,NULL,'result');
    $response->results->append($soapVar);
}

It will return:

 <return xsi:type="SOAP-ENC:Struct">
    <results xsi:type="SOAP-ENC:Struct">
       <result xsi:type="xsd:string">lets</result>
       <result xsi:type="xsd:string">test</result>
       <result xsi:type="xsd:string">it</result>
    </results>
 </return>

As you can see the fifth argument tells what will be the key of xml element. You need to be aware of second argument too, becaouse it tells which is the type of variable. You can find more here: https://secure.php.net/manual/en/class.soapvar.php

dudzio1222
  • 44
  • 4