The OpenTravel Alliance case has been solved, let's celebrate the first release!

  06 September 2013       This post has 0 comment       By Mikaël DELSOL

As the title says, the OpenTravel Alliance issue has been solved so we released the first stable version of the WsdlToPhp project. That does not mean it was not stable before because it worked for many cases and it still works for even better!

The OpenTravel Alliance case

Months ago, we faced an issue we didn't know how to fix it. Indeed, if you read the post about this issue, we needed to really understand the case and we needed to have substantial knowledge to figure this out the best way we could.

Very recently, in fact today, we faced the same issue when helping someone on stackoverflow. The person was trying to call a Web service for which the WSDL declared these identically named elements:

<s:element name="Create"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="request" type="tns:Create" /> </s:sequence> </s:complexType> </s:element> <s:complexType name="Create"> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="Details" type="tns:ArrayOfDetailItem" /> <s:element minOccurs="0" maxOccurs="1" name="UserID" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Password" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="TestMode" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="AccountNumber" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="CompanyName" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Street" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Box" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="City" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="State" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Zipcode" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="ContactName" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="ContactPhone" type="s:decimal" /> <s:element minOccurs="0" maxOccurs="1" name="PickupDate" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="ReadyTime" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="CloseTime" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="SpecialInstructions" type="s:string" /> </s:sequence> </s:complexType>

As we can see, the two elements are declared with an identical name but with a different structure. One is declared as an element and the second one is declared as a complexType.

The name of these two elements, Create, is used to declare the parameter that has to be used to call the Create operation:

<wsdl:message name="CreateSoapIn"> <wsdl:part name="parameters" element="tns:Create" /> </wsdl:message> <wsdl:portType name="PickupSoap"> <wsdl:operation name="Create"> <wsdl:input message="tns:CreateSoapIn" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="PickupSoap" type="tns:PickupSoap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="Create"> <soap:operation soapAction="http://www.SaiaSecure.com/WebService/Pickup/Create" style="document" /> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:binding name="PickupSoap12" type="tns:PickupSoap"> <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="Create"> <soap12:operation soapAction="http://www.SaiaSecure.com/WebService/Pickup/Create" style="document" /> <wsdl:input> <soap12:body use="literal" /> </wsdl:input> <wsdl:output> <soap12:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="Pickup"> <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Saia Pickup</wsdl:documentation> <wsdl:port name="PickupSoap" binding="tns:PickupSoap"> <soap:address location="http://www.saiasecure.com/webservice/pickup/soap.asmx" /> </wsdl:port> <wsdl:port name="PickupSoap12" binding="tns:PickupSoap12"> <soap12:address location="http://www.saiasecure.com/webservice/pickup/soap.asmx" /> </wsdl:port> </wsdl:service>

So, it's pretty confusing when we look to this because we could imagine that we have to send a Create structure as declared by the complexType but in fact not at all. To call the Create operation, you have to send the Create complexType within the first declared Create structure.

As you may know, when we generate the PHP class representing the element, we catch its structure from the SoapClient::__getTypes() method. This method returns an array with all the declared structure without any contextual information nor the complexType information. In fact it returns each element declaration with each declared structure. Our issue was that we had decided at the beginning that when an element had been retrieved succesfully, when we hit a new one with the same name, we avoided it. And it was useless in the end.

This is how we figured this out. As the two elements are named identically but must exist at the same time with a different structure, we simply decided to merge the two elements by adding all the structure declarations to the same final PHP class. It has as effect that a class may contain numerous attributes depending on how many times it has been declared differently.

The consequence for this actual case is that when you call the Create operation, you have to set a Create instance with all the attributes except the request attribute then set a new Create instance for which only the request attribute is set with the first Create instance (we know, it's a little bit confusing...). Finally, pass the second Create instance to the Create method as:

// Service instance $saiaSecureServiceCreate = new SaiaSecureServiceCreate($wsdl); // Set the first Create instance $firstCreateInstance = new SaiaSecureStructCreate(''); $firstCreateInstance->setAccountNumber(''); // call all the required setters to set the values to send // Set the second Create instance with null values $secondCreateInstance = new SaiaSecureStructCreate(null); // Set the request attributes with the first Create instance $secondCreateInstance->setRequest($firstCreateInstance); // Call the Create operation if($saiaSecureServiceCreate->Create($secondCreateInstance)) print_r($saiaSecureServiceCreate->getResult()); else print_r($saiaSecureServiceCreate->getLastError());

Such a release for us to finally find a suitable fix to this issue.

Finally the first release is here, the v1.0 version

Releasing a version is always exciting. Releasing the first one is even more exciting.

When a release is created, it means that a lot of work has been accomplished. It also often means that a lot of issues have been fixed, a lot of improvement have been made and new features have been added. All of these points are true for the WsdlToPhp project. It was not perfect at the beginning and even if it still may not be perfect for everyone, it is working pretty good. We had tested it many many times on many SOAP Web services.

Moreover, this site has shown us that a lot of people wanted to do what we aimed to: easily communicate with a SOAP Web service by having all the classes prepared to do so. The classes are not only prepared to easily communicate with a SOAP Web service, they also help understand how to call it. They contain the informations that can facilitate you the understanding of themselves. By generating the PHP doc block comments and in addition by adding the informations that are contained by the WSDL, the classes can be easily used and understood.

In addition to that, the auto generated file named sample-*.php (* stands for the package name you choose to use for the generation) is a pretty good starting point to call any operation provided by the WSDL.

Let's enjoy the first WsdlToPhp release named v1.0.

What's next?

Next is the launch of the new features for the site.

The new features will be soon online and we hope you'll be happy to use them.