Adding schema types in auto-generate WSDL

From: 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

« previous php.soap (#1221) next »