Using PHP SoapClient classmap option with WSDL containing an element and complexType with the same name

stereoscott picture stereoscott · Sep 24, 2010 · Viewed 16k times · Source

I've encountered a few different WSDL files that contain an element and a complexType with the same name. For example, http://soap.search.msn.com/webservices.asmx?wsdl has two entities named "SearchResponse":

In this scenario, I can't figure out how to properly map those entities to PHP classes using the SoapClient() "classmaps" option.

The PHP manual says this:

The classmap option can be used to map some WSDL types to PHP classes. This option must be an array with WSDL types as keys and names of PHP classes as values.

Unfortunately, since there are two WSDL types with the same key ('SearchResponse'), I can't figure out how to differentiate between the two SearchResponse entities and assign them to their corresponding PHP classes.

For example, here is the relevant snippet of the example WSDL:

<xsd:complexType name="SearchResponse">
    <xsd:sequence>
        <xsd:element minOccurs="1" maxOccurs="1" name="Responses" type="tns:ArrayOfSourceResponseResponses"/>
    </xsd:sequence>
</xsd:complexType>

<xsd:element name="SearchResponse">
    <xsd:complexType>
        <xsd:sequence>
            <xsd:element minOccurs="1" maxOccurs="1" name="Response" type="tns:SearchResponse"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:element>

And here is the PHP that obviously would not work since the classmaps keys are the same:

<?php $server = new SoapClient("http://soap.search.msn.com/webservices.asmx?wsdl", array('classmap' => array('SearchResponse' => 'MySearchResponseElement', 'SearchResponse' => 'MySearchResponseComplexType'))); ?>

In searching for a solution, I found Java Web Services handles this by allowing you to specify a custom suffix to the "Element" or "ComplexType" entities.

http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/1.5/tutorial/doc/JAXBUsing4.html#wp149350

So, right now I feel like there is just no way to do it with PHP's SoapClient, but I'm curious if anyone out there can offer any advice. FWIW, I cannot edit the remote WDSL.

Any ideas???

Answer

Kayla Rose picture Kayla Rose · Oct 8, 2010

This is off the top of my head, but I think you could have both SearchResponse types map to MY_SearchResponse and try and abstract the difference between the two. It's kludgy, but something like this maybe?

<?php
//Assuming SearchResponse<complexType> contains SearchReponse<element> which contains and Array of SourceResponses
//You could try abstracting the nested Hierarchy like so:
class MY_SearchResponse
{
   protected $Responses;
   protected $Response;

   /**
    * This should return the nested SearchReponse<element> as a MY_SearchRepsonse or NULL
    **/
   public function get_search_response()
   {
      if($this->Response && isset($this->Response))
      {
         return $this->Response; //This should also be a MY_SearchResponse
      }
      return NULL;
   }

   /**
    * This should return an array of SourceList Responses or NULL
    **/
   public function get_source_responses()
   {
      //If this is an instance of the top SearchResponse<complexType>, try to get the SearchResponse<element> and it's source responses
      if($this->get_search_response() && isset($this->get_search_response()->get_source_responses()))
      {
         return $this->get_search_response()->get_source_responses();
      }
      //We are already the nested SearchReponse<element> just go directly at the Responses
      elseif($this->Responses && is_array($this->Responses)
      {
         return $this->Responses;
      }
      return NULL;
   }
}

class MY_SourceResponse
{
  //whatever properties SourceResponses have
}

$client = new SoapClient("http:/theurl.asmx?wsdl", array('classmap' => array('SearchResponse' => 'MY_SearchResponse', 'SourceResponse' => 'MY_SourceResponse')));