The case
The WsdlToPhp project exists since July 2012, several developers worked on it, some pushed improvements and some others submitted issues on the Github project.
We tested it with numerous WSDL and we are pretty proud of its generecity and its capability to adapt to pretty any WSDL.
Moreover, the several provided options allows to respond to any WSDL specifications and tricky specific.
But since a few months, at least one known WSDL has not been managed adequately. Indeed, the OpenTravel Alliance WSDL is pretty interesting because it defines many elements and operations (among several schemas). The issue is that several structures are named identically but their definition is distinct. To be more clear, let's look to two distinct definitions for the same element.
RoomStays in OTA_HotelAvailRS.xsd
This schema defines the RoomStays element for a response.
<xs:element name="RoomStays" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="en">A collection of details on the Room Stay including Guest Counts, Time Span of this Room Stay, and financial information related to the Room Stay, including Guarantee, Deposit and Payment and Cancellation Penalties.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="RoomStay" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation xml:lang="en">Details on the Room Stay including Guest Counts, Time Span of this Room Stay, and financial information related to the Room Stay, including Guarantee, Deposit and Payment and Cancellation Penalties.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:complexContent>
<xs:extension base="RoomStayType">
<xs:sequence>
<xs:element name="Reference" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="en">Information by which this availability quote can be later cross-referenced.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:complexContent>
<xs:extension base="UniqueID_Type">
<xs:attribute name="DateTime" type="xs:dateTime" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">The date and time at which this availability quote was made available.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="ServiceRPHs" type="ServiceRPHsType" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="en">A container for the unique references to the services for the room stay.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
<xs:attribute name="IsAlternate" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">Indicates the RoomStay is an alternate offer. Default=false.</xs:documentation>
<xs:documentation xml:lang="en">
<LegacyDefaultValue>false</LegacyDefaultValue>
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="AvailabilityStatus" type="RateIndicatorType" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">Used to specify an availability status at the room stay level for a property.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attributeGroup ref="ResponseTypeGroup" />
<xs:attribute name="RoomStayCandidateRPH" type="RPH_Type" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">A reference to a requested room stay candidate from the SearchCriteria.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="MoreDataEchoToken" type="StringLength1to128" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">The availability response returns the attribute if there is additional data that could not fit within the availability response. The text value should be echoed in the availability request to indicate where to begin the next block of availability data. </xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="InfoSource" type="InfoSourceType" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">Used to specify the source of the data being exchanged as determined by trading partners. </xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="RPH" type="RPH_Type" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">Identifies the room stay for use in the hotel stay.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="AvailableIndicator" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">May be used as a simple true/false to indicate availability or used in conjunction with @AvailabilityStatus to indicate if restricted rate qualifications have been met e.g. Exclusive and Available (true) vs. Exclusive but not Available (false).</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="MoreIndicator" type="StringLength1to128" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">A text field used to indicate that there are additional rates that cannot fit in the availability response. The text returned should be meaningful in identifying where to begin the next block of data and sent in the availability request.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SortOrder" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">The specified sort order for the room stay results. Use is defined by trading partners.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
RoomStays in OTA_HotelCommonTypes.xsd
This schema defines the RoomStays element as a common type.
<xs:element name="RoomStays" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="en">A collection of RoomStay.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="RoomStay" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation xml:lang="en">Details the success, failure and warnings for the RoomStay.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:complexContent>
<xs:extension base="RoomStayType">
<xs:sequence minOccurs="0">
<xs:element name="HotelReservationIDs" type="HotelReservationIDsType" minOccurs="0" />
<xs:element name="RoomShares" type="RoomSharesType" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="en">Collection of shared rooms for room stay.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="UniqueID" type="UniqueID_Type" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="en">Provides a mechanism for uniquely identifying a room stay (e.g. this would be useful for a modification).</xs:documentation>
</xs:annotation>
</xs:element>
<xs:choice minOccurs="0">
<xs:sequence>
<xs:element name="Success" type="SuccessType" />
<xs:element name="Warnings" type="WarningsType" minOccurs="0" />
</xs:sequence>
<xs:element name="Errors" type="ErrorsType" />
</xs:choice>
</xs:sequence>
<xs:attribute name="RoomStay" type="ActionType" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">This attribute indicates the change to the reservation.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
RoomStays in OTA_HotelReservation.xsd
This schema defines the RoomStays within a ComplexType.
<xs:element name="RoomStays" type="RoomStaysType" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="en">Collection of room stays.</xs:documentation>
</xs:annotation>
</xs:element>
As you can see, the same element is defined differently between the first two schemas - the third is not really important because it only documents the RoomStays element within another complex type.
It takes sense in the fact that the two elements are not used in the same schema so they are not used in the same context/ComplexType element. It also means that OTA distinguishes the element structure between two distinct contexts/ComplexType elements. It's great theoretically and conceptually but not cool for the WsdlToPhp generator.
Possible fixes
Indeed, how can we programmatically know what element is expected as the response? The solution could be one of them:
- distinguish classmap per:
- call type
- called Web service location
- prefix/suffix the generated class name with:
- its parent ComplexType element
- a string containing an incremental number
- your idea?!
The WsdlToPhp generator generates only one class for each defined element even if their definition is distinct. Moreover, the benefits of the generator is that it generates the classmap which is used to map defined elements in the WSDL to PHP classes.
So when you call the Web service, you receive the response as PHP known objects - corresponding to generated classes - and not stdClass objects or stantard arrays. But there can be only one entry per element in the classmap because it is an array with the defined element name as a key which the value is the generated class name.
Regarding the previous explanation, prefixing/suffixing the generated class name with an string would be ineficient because the classmap would then always have one entry per defined element and not multiple entry per generated classes.
What do you think?
Now that you have all the elements in your hand, you can participate on the issue directly on the WsdlToPhp Github issue tracker or by posting comments below.
Edit on 10 September 2013: the case has been solved, please read how we solved this case.