In my quest to produce an IMS Tool Interoperability Endpoint which requires a SOAP Server in PHP that handles the complex WSDL for IMS Tool Interoperability – I encountered NUSOAP. I must point out that I have never used nusoap until the last seven days – so I am *not an* expert.
But my general opinion is that nusoap is an awsome product – but poorly documented and not able to handle complex WSDL very well. It is like they did a superb job (I like the coding style of nusoap – the debugging feature is AWESOME) and built until it supported the WSDL that the nusoap folks had to work with and then stopped.
So most of my experience is getting nusoap to handle things that it did not handle out of the box.
I am happy to provide patches to the nusoap folks – but I am guessing that my inexperience with nusoap means that my patches would be architecturally “inelegant” and not good with the philosophy of nusoap – who knows. I may post a message to the nusoap dev list and watch the beatings begin.
The source is here (warning – this has lots of extra debugging code that I added to figure out the internals of nusoap operation – which I left in):
http://www-personal.umich.edu/~csev/sakai/imsti/
You can see the stuff online at:
http://www.sakaiproject.org/imsti-test
At the end of the day, it is no wonder that PHP folks really prefer REST. I was told that perl is better at handling SOAP – I would be curious. Paricularly because I would *love* to have a sample IMS TI tool in perl – but since I HATE the perl language this is not likely to be something that I would do.
Maybe my next foray will be trying to see if Ruby is better than PHP on SOAP parsing of complex and abstract types.
Working with complex SOAP is a pain in PHP – I used nusoap because it seemed to be a good place to start. My verison of nusoap is modified in two ways. I am not exactly sure if these modifications are right so folks should feel free to disagree with my mods or tell me that I am confused w.r.t SOAP.
Support for Multi-Part Messages
It does not appear that nusoap client handles SOAP of the following form:
<SOAP-ENV:Body>
<ProxyToolLaunchResponse>
<LaunchDirective>
<PerformRedirectAction>
http://127.0.0.1:80/~csev/nusoap/samples/LMSTool.php?session=6
</PerformRedirectAction>
</LaunchDirective>
</ProxyToolLaunchResponse>
<imsx_syncResponseHeaderInfo>
<imsx_version>1.0</imsx_version>
</imsx_syncResponseHeaderInfo>
</SOAP-ENV:Body>
The nusoap client parses it all and gets it all right and then only returns the first part. I added code to lib/nusoap.php at line 5953 to detect if there were multiple root documents after parsing and return an array of all of the root parts rather than just the first one.
Look for the string “theMessage”.
Support for Abstract Types
The IMS Tool Interoperability uses derived types and abstract classes. The nusoap outbound serialization seems to have NO support for this. Here is some sample WSDL:
<xs:complexType name=”OutcomeProfile.Type” abstract=”true”>
<xs:annotation>
<xs:documentation>
</xs:documentation>
</xs:annotation>
<xs:sequence>
</xs:sequence>
</xs:complexType>
<xs:complexType name=”MinimalOutcomeProfile.Type”>
<xs:annotation>
<xs:documentation>
</xs:documentation>
<xs:annotation>
<xs:complexContent>
<xs:extension base=”tns:OutcomeProfile.Type”>
<xs:sequence>
<xs:element ref=”tns:LaunchProfile” minOccurs = “1” maxOccurs = “1”/>
<xs:element ref=”tns:Grade” minOccurs = “1” maxOccurs = “1”/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name=”OutcomeMessageRequest”>
<xs:complexType>
<xs:sequence>
<xs:element ref=”tns:OutcomeProfile”/>
</xs:sequence>
</xs:complexType>
</xs:element>
The nusoap serializer wanted to serialize OutcomeMessageRequest so it went looking for OutcomeProfile – if you put this in your array labelled “OutcomeProfile” it would see the definition of the abstract class (OutcomeProfile.Type) and conclude there was nothing to serialize. If you labeled things as “MinimalOutcomeProfile.Type” then nusoap would not see any relationship between OutcomeMessage and MinimalOutcomeProfile.Type and again refuse to serialize.
The solution is a new feature I added to nusoap. The ideas is that you make an entry for “OutcomeProfile” and then within that, you point to the actual implementation of the type. Here is the syntax
I used:
‘SecurityProfile’ => array (
“SOAP:Implementation” => “SharedSecretSecurityProfile.Type”,
‘SharedSecretSecurityProfile.Type’ => array (
“MAC” => “00:ff:ff:ff:00:00”
)
)
You add a “SOAP:Implementation” tag within the entry for the abstract type and tell it the real implementation type – it is basically like a redirect – “Hey you are typing to serialize a SecurityProfile – but really the schema for this is actually a SharedSecretSecurityProfile.Type”. It works pretty well – I am not sure if I am generating good WSDL or not – I will have to validate this with Axis (have I ever told you how much I despise it when WSDL hackers go crazy and do elegant stuff?).
It serializes like this:
<SecurityProfile>
<MAC”>00:ff:ff:ff:00:00</MAC>
</SecurityProfile>
I don’t think this is quite right because there is no real indication of the implementing type for the consuming folks. But I was happy just to get this to work at all. If someone who is a SOAP/WSDL expert can tell me how to fix this let me know. nusoap is a big hog/monster but I think I have a handle on it. The code is around line 5569 of lib/nusoap.php. Look for the string “Redirecting”.
What about PHP5?
This is all PHP4 because it comes shipped on my Mac :). So I use nusoap. In PHP5 some of this is built in and so either it is magically fixed or – it is broken and not easily fixed.