Home » Web Services » SOAP » Bug #978
Implement document/literal server
Details
| Request #978 | Implement document/literal server |
|---|---|
| Submitted | 2004-03-09 07:39 UTC |
| From | xiangweizhuo at hotmail dot com |
| Status | Wont fix |
| Package | SOAP |
| PHP Version | 4.3.4 |
| OS | window |
| Roadmaps | (Not assigned) |
Comments
[2004-03-09 07:39 UTC] xiangweizhuo at hotmail dot com
Description:
------------
The current SOAP package doesn't seem to implement the document/literal server.
From some of the comments in the package, adding the options
$options = array('use' => 'literal', 'style' => 'document');
to the SOAP_Server does not produce a document/literal WSDL schema.
So, it a document/literal server implementable with the current SOAP package? (SOAP SOAP-0.8RC3.tgz)
Reproduce code:
---------------
//From the server.php example in the SOAP package.
require_once 'SOAP/Server.php';
$options = array('use' => 'literal', 'style' => 'document');
$server = new SOAP_Server($options);
.
.
.
Expected result:
----------------
A document/literal (WSDL schema) SOAP service.
Actual result:
--------------
A RPC/Encoded (WSDL schema) SOAP service
[2004-04-06 00:16 UTC] xiangweizhuo at hotmail dot com
Yes, generating document/literal style WSDL from PHP would be the best. Similar to the rpc/encoded way of creating the WSDL but for document/literal if possible.
Cheer, Wei.
[2005-03-03 19:52 UTC] thatcoder at gmail dot com
Any luck on implementation of this?
[2005-07-18 21:01 UTC] mw dot trash at gmail dot com
any eta on when this may be fixed?
[2006-01-25 19:47 UTC] gnomebubble at gmail dot com
A temporary solution would be to apply this patch this patch to Disco.php. It's much more than a hack, but it's not a full solution either, since there's a really nasty case of (newer) doubled code in WSDL.php which I only noticed later. The problem is, though, that WSDL.php's WSDL code cannot generate a WSDL schema (at least not that I could find how to do it), so you still need Disco.php (which seems out of date). On the other hand WSDL.php is apparently used by Server.php for converting PHP types to SOAP types, so there may be some discrepancies between the two.
The real proper solution (in case someone cares to implement it), would be coding Document/Literal support into WSDL.php (which was designed with RPC/Encoded in mind) and then creating a newfangled Disco_Server which generates WSDL code based on the WSDL class in WSDL.php.
Just a note: This patch is the result of a few hours of coding. It's probably not devoid of bugs. I only tested it with Visual Studio .NET 2005. I'm not sure if it works with earlier versions, but it should, as I tried to model the output, as far as possible, after ASP.Net's output.
In any case, working this out proved out to be easier than I thought. Unlike what some people said, Document/Literal is NOT a lot more complicated than RPC/Encoded. In fact, it seemed to me to be slightly simpler, and I was working on the schema code - the message parsing and generation code is obviously a great deal simpler in Document/Literal.
==================================
End rant and here's the patch:
--- Old/Disco.php Tue Jan 17 05:28:04 2006
+++ New/Disco.php Tue Jan 17 12:07:23 2006
@@ -41,7 +41,8 @@
function SOAP_DISCO_Server($soap_server, $service_name, $service_desc = '',
- $import_ns = null)
+ $import_ns = null, $service_ns = null, $options = array(
+ 'style' => 'rpc', 'use' => 'encoded'))
{
parent::SOAP_Base_Object('Server');
@@ -49,11 +50,12 @@
|| !get_class($soap_server) == 'soap_server') return;
$this->_service_name = $service_name;
- $this->_service_ns = "urn:$service_name";
+ $this->_service_ns = isset($service_ns) ? $service_ns : "urn:$service_name";
$this->_service_desc = $service_desc;
$this->import_ns = isset($import_ns) ? $import_ns : $this->import_ns;
$this->soap_server = $soap_server;
$this->host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
+ $this->options = $options;
}
function getDISCO()
@@ -125,7 +127,7 @@
$this->_bindingname = $this->_service_name . 'Binding';
$this->_wsdl['definitions']['binding']['attr']['name'] = $this->_bindingname;
$this->_wsdl['definitions']['binding']['attr']['type'] = 'tns:' . $this->_portname;
- $this->_wsdl['definitions']['binding']['soap:binding']['attr']['style'] = 'rpc';
+ $this->_wsdl['definitions']['binding']['soap:binding']['attr']['style'] = $this->options['style'];
$this->_wsdl['definitions']['binding']['soap:binding']['attr']['transport'] = SCHEMA_SOAP_HTTP;
// SERVICE
@@ -183,6 +185,51 @@
return $schema;
}
+ function _generateStruct(&$ctype, $members)
+ {
+ // Generate member sequence (if $members is a non-empty array)
+ if (is_array($members) && count($members) > 0) {
+ $seq =& $ctype['sequence'][];
+ $seq['attr'] = '';
+ foreach ($members as $name => $type) {
+ list($typens, $type) = $this->_getTypeNs($type);
+ $param_element =& $seq['element'][];
+ $param_element['attr']['minOccurs'] = '1';
+ $param_element['attr']['maxOccurs'] = '1';
+ $param_element['attr']['name'] = $name;
+ $param_element['attr']['type'] = $typens . ':' . $type;
+ }
+ }
+ }
+
+ function _generateArray(&$ctype, $type, $name = null)
+ {
+ // array(array()) hack
+ if (is_array($type) && count($type) == 1)
+ {
+ foreach ($type as $xname => $xtype)
+ break;
+ $type = $xtype;
+
+ if (!is_int($namex)) // Associative array
+ $name = $xname;
+
+ }
+
+ // If no name is specified: follow the .Net convention of naming item element by type
+ if (!$name)
+ $name = $type;
+
+ list($typens, $type) = $this->_getTypeNs($type);
+ $seq =& $ctype['sequence'][];
+ $seq['attr'] = '';
+ $el =& $seq['element'][];
+ $el['attr']['minOccurs'] = '0';
+ $el['attr']['maxOccurs'] = 'unbounded';
+ $el['attr']['name'] = $name;
+ $el['attr']['type'] = $typens . ':' . $type;
+ }
+
function addSchemaFromMap(&$map)
{
if (!$map) {
@@ -201,18 +248,32 @@
$ctype['attr']['name'] = $type;
foreach ($_type_def as $_varname => $_vartype) {
if (!is_int($_varname)) {
- list($_vartypens,$_vartype) = $this->_getTypeNs($_vartype);
- $ctype['all']['attr'] = '';
- $el =& $ctype['all']['element'][];
- $el['attr']['name'] = $_varname;
- $el['attr']['type'] = $_vartypens . ':' . $_vartype;
+ // Struct-style complex type
+ if ($this->options['use'] == 'literal') {
+ $this->_generateStruct(&$ctype, $_type_def);
+ break;
+ }
+ else {
+ list($_vartypens,$_vartype) = $this->_getTypeNs($_vartype);
+ $ctype['all']['attr'] = '';
+ $el =& $ctype['all']['element'][];
+ $el['attr']['name'] = $_varname;
+ $el['attr']['type'] = $_vartypens . ':' . $_vartype;
+ }
} else {
- $ctype['complexContent']['attr'] = '';
- $ctype['complexContent']['restriction']['attr']['base'] = 'SOAP-ENC:Array';
- foreach ($_vartype as $array_var => $array_type) {
- list($_vartypens, $_vartype) = $this->_getTypeNs($array_type);
- $ctype['complexContent']['restriction']['attribute']['attr']['ref'] = 'SOAP-ENC:arrayType';
- $ctype['complexContent']['restriction']['attribute']['attr']['wsdl:arrayType'] = $_vartypens . ':' . $_vartype . '[]';
+ // Array-style complex type
+ if ($this->options['use'] == 'literal') {
+ $this->_generateArray(&$ctype, $_vartype);
+ break;
+ }
+ else {
+ $ctype['complexContent']['attr'] = '';
+ $ctype['complexContent']['restriction']['attr']['base'] = 'SOAP-ENC:Array';
+ foreach ($_vartype as $array_var => $array_type) {
+ list($_vartypens, $_vartype) = $this->_getTypeNs($array_type);
+ $ctype['complexContent']['restriction']['attribute']['attr']['ref'] = 'SOAP-ENC:arrayType';
+ $ctype['complexContent']['restriction']['attribute']['attr']['wsdl:arrayType'] = $_vartypens . ':' . $_vartype . '[]';
+ }
}
}
}
@@ -220,6 +281,38 @@
}
}
+ function _generateMessage(&$message, $message_name, $param_types)
+ {
+ $message['attr']['name'] = $message_name;
+
+ // This code handles RPC/Encoded and Document/Literal-wrapped
+ // (Microsoft .Net-style). Any other style will be rendered
+ // as RPC/Encoded.
+ if ($this->options['style'] == 'document' &&
+ $this->options['use'] == 'literal')
+ {
+ // Generate reference to message parameters element
+ $part =& $message['part'][];
+ $part['attr']['name'] = 'parameters';
+ $part['attr']['element'] = $message_name . 'Params';
+
+ // Generate schema definition for the parameters element
+ $schema =& $this->_getSchema($this->_service_ns);
+ $element =& $schema['element'][];
+ $element['attr']['name'] = $message_name . 'Params';
+ $ctype =& $element['complexType'];
+ $ctype['attr'] = '';
+ $this->_generateStruct(&$ctype, $param_types);
+ }
+ else // RPC/Encoded: inline message parts
+ foreach ($param_types as $name => $type) {
+ list($typens, $type) = $this->_getTypeNs($type);
+ $part =& $message['part'][];
+ $part['attr']['name'] = $name;
+ $part['attr']['type'] = $typens . ':' . $type;
+ }
+ }
+
function addMethodsFromMap(&$map, $namespace, $classname = null)
{
if (!$map) {
@@ -232,29 +325,24 @@
} else {
$method_namespace = $namespace;
}
- // INPUT
- if (isset($method_types['in']) && is_array($method_types['in'])) {
- $input_message =& $this->_wsdl['definitions']['message'][];
- $input_message['attr']['name'] = $method_name . 'Request';
- foreach ($method_types['in'] as $name => $type) {
- list($typens, $type) = $this->_getTypeNs($type);
- $part =& $input_message['part'][];
- $part['attr']['name'] = $name;
- $part['attr']['type'] = $typens . ':' . $type;
- }
- }
- // OUTPUT
- if (isset($method_types['out']) && is_array($method_types['out'])) {
- $output_message =& $this->_wsdl['definitions']['message'][];
- $output_message['attr']['name'] = $method_name . 'Response';
- foreach ($method_types['out'] as $name => $type) {
- list($typens, $type) = $this->_getTypeNs($type);
- $part =& $output_message['part'][];
- $part['attr']['name'] = $name;
- $part['attr']['type'] = $typens . ':' . $type;
- }
- }
+ // INPUT MESSAGE
+ if (isset($method_types['in']) && is_array($method_types['in']))
+ $param_types = $method_types['in'];
+ else
+ $param_types = array();
+ $input_message_name = $method_name . 'Request';
+ $input_message =& $this->_wsdl['definitions']['message'][];
+ $this->_generateMessage(&$input_message, $input_message_name, $param_types);
+
+ // OUTPUT MESSAGE
+ if (isset($method_types['out']) && is_array($method_types['out']))
+ $param_types = $method_types['out'];
+ else
+ $param_types = array();
+ $output_message_name = $method_name . 'Response';
+ $output_message =& $this->_wsdl['definitions']['message'][];
+ $this->_generateMessage(&$output_message, $output_message_name, $param_types);
// PORTTYPES
$operation =& $this->_wsdl['definitions']['portType']['operation'][];
@@ -262,11 +350,11 @@
// INPUT
$operation['input']['attr']['message'] = 'tns:'
- . $input_message['attr']['name'];
+ . $input_message_name;
// OUTPUT
$operation['output']['attr']['message'] = 'tns:'
- . $output_message['attr']['name'];
+ . $output_message_name;
// BINDING
$binding =& $this->_wsdl['definitions']['binding']['operation'][];
@@ -276,15 +364,17 @@
// INPUT
$binding['input']['attr'] = '';
- $binding['input']['soap:body']['attr']['use'] = 'encoded';
+ $binding['input']['soap:body']['attr']['use'] = $this->options['use'];
$binding['input']['soap:body']['attr']['namespace'] = $method_namespace;
- $binding['input']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING;
+ if ($this->options['use'] == 'encoded')
+ $binding['input']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING;
// OUTPUT
$binding['output']['attr'] = '';
- $binding['output']['soap:body']['attr']['use'] = 'encoded';
+ $binding['output']['soap:body']['attr']['use'] = $this->options['use'];
$binding['output']['soap:body']['attr']['namespace'] = $method_namespace;
- $binding['output']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING;
+ if ($this->options['use'] == 'encoded')
+ $binding['output']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING;
}
}
@@ -355,6 +445,7 @@
function _getTypeNs($type)
{
+ // TODO: Allow parsing of simpleType class instead of string
preg_match_all("'\{(.*)\}'sm", $type, $m);
if (isset($m[1][0]) && $m[1][0] != '') {
if (!array_key_exists($m[1][0],$this->namespaces)) {
@@ -364,6 +455,8 @@
}
$typens = $this->namespaces[$m[1][0]];
$type = ereg_replace($m[0][0],'',$type);
+ } else if (count($m = explode(':', $type, 2)) > 1) {
+ list($typens, $type) = $m;
} else {
$typens = 'xsd';
}