Adding schema types in auto-generate WSDL
| From: | Paul Robinson | Date: | Fri, 12 Dec 2003 16:29:27 +0000 |
| Subject: | Adding schema types in auto-generate WSDL | ||
| Groups: | php.soap | ||
| Request: | Send a blank email to php-soap+get-1221@lists.php.net to get a copy of this message | ||
Hi guys,
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