First off, apologies for the length of this post, but this is a weird problem that I need to fix pretty soon, I've exhausted my other options and I thought it best to supply slightly too much detail than not enough. In fact, I've written a whole new mini-web service just to facilitate this post! :-)
I'm using PHP/PEAR to develop quite a complicated web service, and I've run into a bit of a problem. Basically, I'm auto-generating the WSDL but I'm returning what are really complexTypes in my data that client code (well behaved .NET code that is fully RPC/encoded aware - so it's not a document/literal problem) is barfing on. I think I can fix it just by adding a schema to handle arrays in the <types> section of the auto-generated WSDL. However, the addSchemaFromMap is undocumented and I'm not sure if that's what I need.
Here's a sample piece of code (not what my real code is, as that's much more complicated, and you don't need to see it) implementing a user lookup function that takes a userid for a particular user, but if that is sent as zero, all users are returned:
<?php require_once 'SOAP/Server.php';
// Then the class here class MemberDatabase { var $__dispatch_map = array();
function MemberDatabase() {
// Define the signature of the dispatch map
$this->__dispatch_map['GetUsers'] = array('in' => array('Userid' => 'integer'), 'out' => array('Member' => 'string')); }
// NOTE: I'm saying I'm returning a string, but I'm actually // going to return an array - this is needed because I have // several member "items" coming back...
function GetUsers($Userid){
// Do my SQL lookup based on $Userid $stmt = "SELECT * FROM Users"; if($Userid != 0) $stmt .= " WHERE Userid = '$Userid'";
$link = mysql_connect("mymachine", "myusername", "mypass"); mysql_select_db("StaffDB"); $result = mysql_query($stmt); // Do my query
$User = array(); // I'm going to fill this array with data... $ThisUser = array(); while($row = mysql_fetch_array($result, MYSQL_ASSOC)){ $ThisUser['Userid'] = $row['Userid']; $ThisUser['Name'] = $row['Name']; $ThisUser['Email'] = $row['Email']; $ThisUser['Department'] = $row['Department'];
$User[] = $ThisUser; } mysql_close(); // Tidy up before leaving here return $User; } } // Fire up PEAR::SOAP_Server $server = new SOAP_Server;
// Get the class in here $MemberDatabaseServer = new MemberDatabase();
// Add the object to SOAP server $server->addObjectMap($MemberDatabaseServer,'urn:MemberDatabase');
// Handle SOAP requests coming is as POST data if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD']=='POST') { $server->service($HTTP_RAW_POST_DATA); } else { // Deal with WSDL / Disco here require_once 'SOAP/Disco.php';
// Create the Disco server $disco = new SOAP_DISCO_Server($server,'MemberDatabase'); header("Content-type: text/xml"); if (isset($_SERVER['QUERY_STRING']) && strcasecmp($_SERVER['QUERY_STRING'],'wsdl')==0) { echo $disco->getWSDL(); // this doles out wsdl when ?wsdl on end } else { echo $disco->getDISCO(); } exit; } ?>
This is all based on code grabbed from PhpPatterns in an article on auto-generating WSDL. If the above code lives at say, http://my.url/memberdb.php then I can get WSDL by accessing http://my.url/memberdb.php?wsdl - and the above code would work and would generate:
<?xml version="1.0" ?>
<definitions name="MemberDatabase" targetNamespace="urn:MemberDatabase" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="urn:MemberDatabase" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://schemas.xmlsoap.org/wsdl/">
<types xmlns="http://schemas.xmlsoap.org/wsdl/" />
<portType name="MemberDatabasePort">
<operation name="GetUsers">
<input message="tns:GetUsersRequest" />
<output message="tns:GetUsersResponse" />
</operation>
</portType>
<binding name="MemberDatabaseBinding" type="tns:MemberDatabasePort">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="GetUsers">
<soap:operation soapAction="urn:MemberDatabase#memberdatabase#GetUsers" />
<input>
<soap:body use="encoded" namespace="urn:MemberDatabase" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
</input>
<output>
<soap:body use="encoded" namespace="urn:MemberDatabase" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
</output>
</operation>
</binding>
<service name="MemberDatabaseService">
<documentation />
<port name="MemberDatabasePort" binding="tns:MemberDatabaseBinding">
<soap:address location="http://www.iconoplex.co.uk/testusenet.php" />
</port>
</service>
<message name="GetUsersRequest">
<part name="Userid" type="xsd:integer" />
</message>
<message name="GetUsersResponse">
<part name="Member" type="xsd:string" />
</message>
</definitions>
That's all great, and if you start up XMLSPY and tell it to use that, it parses just fine, and it will even generate a SOAP Request for you:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<m:GetUsers xmlns:m="urn:MemberDatabase" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<Userid xsi:type="xsd:integer">0</Userid>
</m:GetUsers>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Now, remember I was telling the dispatch_map function I was returning a string, but really it was an array? Look what I get back when I send a request:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns4="urn:MINTALF" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns4:GetUsersResponse>
<return xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="Struct[2]" SOAP-ENC:offset="[0]">
<item>
<Userid xsi:type="xsd:string">1</Userid>
<Name xsi:type="xsd:string">Paul Robinson</Name>
<Email xsi:type="xsd:string">paul@iconoplex.co.uk</Email>
<Department xsi:type="xsd:string">1</Department>
</item>
<item>
<Userid xsi:type="xsd:string">2</Userid>
<Name xsi:type="xsd:string">Another Paul Robinson</Name>
<Email xsi:type="xsd:string">paul@iconoplex.co.uk</Email>
<Department xsi:type="xsd:string">1</Department>
</item>
</return>
</ns4:GetUsersResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The line that seems to be confusing the client code we're testing with is:
<return xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="Struct[2]" SOAP-ENC:offset="[0]">
What I'm thinking is how to add something into the WSDL defintion defining an array, and then setting the 'out' array to have
"Members" => "MyDefinedArray" instead of "string"
Suggestions please? If the correct answer helps me out and fixes the client code, if the sender should have an amazon wish list knocking around, I'm sure a small Christmas present would not be out of the question... it would be worth it to make this problem go away.
-- Paul Robinson
-- PHP Soap Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php