вторник, юни 23, 2009

PHP, Web services and Subclassed types in the WSDL


Problem: Use web service from PHP 5+ with SoapClient with the following specific case of WSDL: some types in the WSDL are abstract base types for other types that inherit them. The Web Service function itself accept base type parameter (and it can accept all objects that inherit from the base type), but a CONCRETE (extends the base) type should be specified when passing the request to the web service.

Example WSDL:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="mywsdl">
<!------------------ types ------------------->
<wsdl:types>
<xs:schema targetNamespace = "http://www.mydomain.com"
xmlns = "http://schemas.xmlsoap.org/wsdl/"
xmlns:apachesoap = "http://xml.apache.org/xml-soap"
xmlns:soapenc = "http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap = "http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tMyType = "http://www.mydomain.com"
elementFormDefault = "qualified"
attributeFormDefault = "unqualified">

<xs:complexType name="MachineType" abstract="true">
<xs:sequence>
<xs:element name="name" type="xsi:string" minOccurs="1"/>
<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>

<xs:complexType name="ComputerType">
<xs:complexContent>
<xs:extension base="MachineType">
<xs:sequence>
<xs:element name="architecture" type="xsi:string" minOccurs="1"/>
<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>

<xs:complexType name="CarType">
<xs:complexContent>
<xs:extension base="MachineType">
<xs:sequence>
<xs:element name="engineType" type="xsi:string" minOccurs="1"/>
<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>

<xs:element name="myMethodNameRequest" type="tMyType:MyMethodNameRequest"/>
<!------------------ the function accept base type object! ------------------->
<xs:complexType name="MyMethodNameRequest">
<xs:sequence>
<xs:element name="machine" type="tMyType:MachineType"/>
<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>

</xs:schema>
</wsdl:types>
<!-------------- end of types --------------->

<!----------------- messages ---------------->
<wsdl:message name="myMethodNameRequest">
<wsdl:part name="parameters" element="tMyType:myMethodNameRequest"/>
</wsdl:message>

<!----------------- port ---------------->
<wsdl:portType name="myPortType">
<wsdl:operation name="myMethodName">
<wsdl:input name="myMethodNameRequest" message="tMyType:myMethodNameRequest"/>
<wsdl:output/>
</wsdl:operation>
</wsdl:portType>

<!----------------- binding ---------------->
<wsdl:binding name="mySOAPhttpBinding" type="tMyType:myPortType">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<!-- start operations -->
<wsdl:operation name="myMethodName">
<wsdlsoap:operation soapAction="myMethodName"/>
<wsdl:input name="myMethodNameRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output/>
</wsdl:operation>
</wsdl:binding>

<!----------------- service ---------------->
<wsdl:service name="myserv">
<wsdl:port name="myPort" binding="tMyType:mySOAPhttpBinding">
<wsdlsoap:address location="http://mydomain.com/wsdl/"/>
</wsdl:port>
</wsdl:service>

</wsdl:definitions>

Request object is created as usual. The difference is that before calling the service method, the request object should be encoded with the appropriate concrete type. If there are nested (sub) objects in the request object "tree", which also inherit from different types and you need to pass the objects as different concrete types, they should also be encoded separately:


define(WSDL_URL, "http://www.mydomain.com/webservicepath?wsdl");

# create request parameters
$params = new stdClass();
$params->machine->name = "My computer";
$params->machine->architecture = "Intel x86";

$params->machine = new SoapVar($params->machine, SOAP_ENC_OBJECT, "ComputerType", "http://www.mydomain.com");

# create soap client object
$soap_client = new SoapClient(WSDL_URL, array("connection_timeout"=>5, 'trace'=>1));

try
{
# call the method of the service
$result = $soap_client->myMethodName(params);
}
catch(SoapFault $exception)
{
echo $exception;
}


If the object is not encoded properly, the line in italic above will be removed from the request ($params), because the method generally accept "MachineType" object which doesn't have element "architecture". This encoding process can be compared to Casting/Type Conversion in other programming languages.

Няма коментари: