Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

enh #94, added support for document/literal WSDL in generator, inspir…

…ed by Zend 2.0 Soap Autodiscover class
  • Loading branch information...
commit a61b6de16ef4044230cca6f54f241ede6b9b5705 1 parent 2370a9e
Jan Waś authored
Showing with 134 additions and 27 deletions.
  1. +1 −0  CHANGELOG
  2. +133 −27 framework/web/services/CWsdlGenerator.php
1  CHANGELOG
View
@@ -19,6 +19,7 @@ Version 1.1.14 work in progress
- Bug #2078: Fixed problem with "undefined" parameter in query string when using CListView or CGridView with enableHistory (Parpaing)
- Bug #2086: Fixed .hgignore rule for assets folder (GeXu3, Koduc)
- Bug #2112: Fixed broken yiic shell CRUD command (mbischof)
+- Enh #94: Web services: Implement document/literal encoding for WDSL (nineinchnick)
- Enh: Better CFileLogRoute performance (Qiang, samdark)
- Enh #1847: Added COutputCache::varyByLanguage to generate separate cache for different languages (Obramko)
- Enh #1948: Tidy up and improve html5 input support in CHtml and CActiveForm (phpnode)
160 framework/web/services/CWsdlGenerator.php
View
@@ -101,6 +101,21 @@ class CWsdlGenerator extends CComponent
* If not set, it defaults to "urn:{$className}wsdl".
*/
public $serviceName;
+ /**
+ * @var array
+ * soap:body operation style options
+ */
+ public $operationBodyStyle = array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/");
+ /**
+ * @var array
+ * soap:operation style
+ */
+ public $bindingStyle = 'rpc';
+ /**
+ * @var string
+ * soap:operation transport
+ */
+ public $bindingTransport = 'http://schemas.xmlsoap.org/soap/http';
protected static $typeMap=array(
'string'=>'xsd:string',
@@ -121,6 +136,7 @@ class CWsdlGenerator extends CComponent
protected $operations;
protected $types;
+ protected $elements;
protected $messages;
/**
@@ -134,6 +150,7 @@ public function generateWsdl($className, $serviceUrl, $encoding='UTF-8')
{
$this->operations=array();
$this->types=array();
+ $this->elements=array();
$this->messages=array();
if($this->serviceName===null)
$this->serviceName=$className;
@@ -167,10 +184,26 @@ protected function processMethod($method)
$n=preg_match_all('/^@param\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches);
if($n>count($params))
$n=count($params);
- for($i=0;$i<$n;++$i)
- $message[$params[$i]->getName()]=array($this->processType($matches[1][$i]), trim($matches[3][$i])); // name => type, doc
+ if ($this->bindingStyle == 'rpc')
+ {
+ for($i=0;$i<$n;++$i)
+ $message[$params[$i]->getName()]=array(
+ 'type'=>$this->processType($matches[1][$i]),
+ 'doc'=>trim($matches[3][$i]),
+ );
+ }
+ else
+ {
+ $this->elements[$methodName] = array();
+ for($i=0;$i<$n;++$i)
+ $this->elements[$methodName][$params[$i]->getName()]=array(
+ 'type'=>$this->processType($matches[1][$i]),
+ 'nillable'=>$params[$i]->isOptional(),
+ );
+ $message['parameters'] = array('element'=>'tns:'.$methodName);
+ }
- $this->messages[$methodName.'Request']=$message;
+ $this->messages[$methodName.'In']=$message;
if(preg_match('/^@return\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches))
$return=array($this->processType($matches[1]),trim($matches[2])); // type, doc
@@ -178,6 +211,28 @@ protected function processMethod($method)
$return=null;
$this->messages[$methodName.'Response']=array('return'=>$return);
+ if ($this->bindingStyle == 'rpc')
+ {
+ if(preg_match('/^@return\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches))
+ $return=array(
+ 'type'=>$this->processType($matches[1]),
+ 'doc'=>trim($matches[2]),
+ );
+ else
+ $return=null;
+ $this->messages[$methodName.'Out']=array('return'=>$return);
+ }
+ else
+ {
+ if(preg_match('/^@return\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches))
+ {
+ $this->elements[$methodName.'Response'][$methodName.'Result']=array(
+ 'type'=>$this->processType($matches[1]),
+ );
+ }
+ $this->messages[$methodName.'Out']=array('parameters'=>array('element'=>'tns:'.$methodName.'Response'));
+ }
+
if(preg_match('/^\/\*+\s*([^@]*?)\n@/s',$comment,$matches))
$doc=trim($matches[1]);
else
@@ -279,7 +334,7 @@ protected function buildDOM($serviceUrl,$encoding)
*/
protected function addTypes($dom)
{
- if($this->types===array())
+ if($this->types===array() && $this->elements===array())
return;
$types=$dom->createElement('wsdl:types');
$schema=$dom->createElement('xsd:schema');
@@ -295,21 +350,33 @@ protected function addTypes($dom)
$complexType->setAttribute('name',substr($xmlType,4));
else
$complexType->setAttribute('name',$xmlType);
- $complexContent=$dom->createElement('xsd:complexContent');
- $restriction=$dom->createElement('xsd:restriction');
- $restriction->setAttribute('base','soap-enc:Array');
- $attribute=$dom->createElement('xsd:attribute');
- $attribute->setAttribute('ref','soap-enc:arrayType');
- $attribute->setAttribute('wsdl:arrayType',substr($xmlType,0,strlen($xmlType)-5).'[]');
$arrayType = ($dppos=strpos($xmlType,':')) !==false ? substr($xmlType,$dppos + 1) : $xmlType; // strip namespace, if any
$arrayType = substr($arrayType,0,-5); // strip 'Array' from name
- $arrayType = (isset(self::$typeMap[$arrayType]) ? 'xsd:' : 'tns:') .$arrayType.'[]';
- $attribute->setAttribute('wsdl:arrayType',$arrayType);
-
- $restriction->appendChild($attribute);
- $complexContent->appendChild($restriction);
- $complexType->appendChild($complexContent);
+ if ($this->operationBodyStyle['use'] == 'encoded')
+ {
+ $complexContent=$dom->createElement('xsd:complexContent');
+ $restriction=$dom->createElement('xsd:restriction');
+ $restriction->setAttribute('base','soap-enc:Array');
+ $attribute=$dom->createElement('xsd:attribute');
+ $attribute->setAttribute('ref','soap-enc:arrayType');
+ $attribute->setAttribute('arrayType',(isset(self::$typeMap[$arrayType]) ? 'xsd:' : 'tns:') .$arrayType.'[]');
+
+ $restriction->appendChild($attribute);
+ $complexContent->appendChild($restriction);
+ $complexType->appendChild($complexContent);
+ }
+ else
+ {
+ $sequence=$dom->createElement('xsd:sequence');
+ $element=$dom->createElement('xsd:element');
+ $element->setAttribute('name','item');
+ $element->setAttribute('type',(isset(self::$typeMap[$arrayType]) ? self::$typeMap[$arrayType] : 'tns:'.$arrayType));
+ $element->setAttribute('minOccurs','0');
+ $element->setAttribute('maxOccurs','unbounded');
+ $sequence->appendChild($element);
+ $complexType->appendChild($sequence);
+ }
}
elseif(is_array($xmlType))
{
@@ -331,9 +398,32 @@ protected function addTypes($dom)
$complexType->appendChild($all);
}
$schema->appendChild($complexType);
- $types->appendChild($schema);
}
-
+ foreach($this->elements as $name=>$parameters)
+ {
+ $element=$dom->createElement('xsd:element');
+ $element->setAttribute('name',$name);
+ $complexType=$dom->createElement('xsd:complexType');
+ if (!empty($parameters))
+ {
+ $sequence=$dom->createElement('xsd:sequence');
+ foreach($parameters as $paramName=>$paramOpts)
+ {
+ $innerElement=$dom->createElement('xsd:element');
+ $innerElement->setAttribute('name',$paramName);
+ $innerElement->setAttribute('type',$paramOpts['type']);
+ if (isset($paramOpts['nillable']) && $paramOpts['nillable'])
+ {
+ $innerElement->setAttribute('nillable','true');
+ }
+ $sequence->appendChild($innerElement);
+ }
+ $complexType->appendChild($sequence);
+ }
+ $element->appendChild($complexType);
+ $schema->appendChild($element);
+ }
+ $types->appendChild($schema);
$dom->documentElement->appendChild($types);
}
@@ -352,7 +442,14 @@ protected function addMessages($dom)
{
$partElement=$dom->createElement('wsdl:part');
$partElement->setAttribute('name',$partName);
- $partElement->setAttribute('type',$part[0]);
+ if (isset($part['type']))
+ {
+ $partElement->setAttribute('type',$part['type']);
+ }
+ if (isset($part['element']))
+ {
+ $partElement->setAttribute('element',$part['element']);
+ }
$element->appendChild($partElement);
}
}
@@ -383,9 +480,9 @@ protected function createPortElement($dom,$name,$doc)
$operation->setAttribute('name',$name);
$input = $dom->createElement('wsdl:input');
- $input->setAttribute('message', 'tns:'.$name.'Request');
+ $input->setAttribute('message', 'tns:'.$name.'In');
$output = $dom->createElement('wsdl:output');
- $output->setAttribute('message', 'tns:'.$name.'Response');
+ $output->setAttribute('message', 'tns:'.$name.'Out');
$operation->appendChild($dom->createElement('wsdl:documentation',$doc));
$operation->appendChild($input);
@@ -404,8 +501,8 @@ protected function addBindings($dom)
$binding->setAttribute('type','tns:'.$this->serviceName.'PortType');
$soapBinding=$dom->createElement('soap:binding');
- $soapBinding->setAttribute('style','rpc');
- $soapBinding->setAttribute('transport','http://schemas.xmlsoap.org/soap/http');
+ $soapBinding->setAttribute('style',$this->bindingStyle);
+ $soapBinding->setAttribute('transport',$this->bindingTransport);
$binding->appendChild($soapBinding);
$dom->documentElement->appendChild($binding);
@@ -424,15 +521,24 @@ protected function createOperationElement($dom,$name)
$operation->setAttribute('name', $name);
$soapOperation = $dom->createElement('soap:operation');
$soapOperation->setAttribute('soapAction', $this->namespace.'#'.$name);
- $soapOperation->setAttribute('style','rpc');
+ if ($this->bindingStyle == 'rpc')
+ {
+ $soapOperation->setAttribute('style','rpc');
+ }
$input = $dom->createElement('wsdl:input');
$output = $dom->createElement('wsdl:output');
$soapBody = $dom->createElement('soap:body');
- $soapBody->setAttribute('use', 'encoded');
- $soapBody->setAttribute('namespace', $this->namespace);
- $soapBody->setAttribute('encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/');
+ $operationBodyStyle = $this->operationBodyStyle;
+ if ($this->bindingStyle == 'rpc' && !isset($operationBodyStyle['namespace']))
+ {
+ $operationBodyStyle['namespace'] = $this->namespace;
+ }
+ foreach($operationBodyStyle as $attributeName=>$attributeValue)
+ {
+ $soapBody->setAttribute($attributeName, $attributeValue);
+ }
$input->appendChild($soapBody);
$output->appendChild(clone $soapBody);

7 comments on commit a61b6de

Marco van 't Wout

You could use the following PHP defines instead of doing string comparisons:

SOAP_RPC
SOAP_DOCUMENT

SOAP_ENCODED
SOAP_LITERAL

Jan Waś

I don't think so, because I put bindingStyle and operationBodyStyle directly in the WSDL and bindingStyle needs to be a string of either 'rpc' or 'document' and operationBodyStyle's 'use' key neends to be either 'encoded' or 'literal' .

I've tried to search over php docs to see the usage of those constants but couldn't find none.

Marco van 't Wout

These core PHP constants are defined here: http://php.net/manual/en/soap.constants.php

Maybe it's good to define and use your own consts in this file, for example:

STYLE_RPC = 'rpc'
STYLE_DOCUMENT = 'document'
USE_ENCODED = 'encoded'
USE_LITERAL = 'literal'

Jan Waś

Yeah, and they are defined as integers. I really don't think that using a constant instead of a string is much different. There are only two variants. There won't be more, there are only legacy SOAP apps left and everybody is doing the new stuff as RESTful. Besides, if we would use document/literal as the defaults probably no one would wan't to change it back to rpc/encoded.

Anyway, I just hope this gets merged as it is.

Marco van 't Wout

When you are using (lots of) comparisons such as "if ($bindingStyle == 'rpc'), in my opinoin it is much cleaner to use "if ($bindingStyle == self::STYLE_RPC)"

It doesn't really matter if there are two options or ten, using these consts softly restricts the possible values, and it is less error prone.

Jan Waś

You are right, thanks for convincing me. Now I don't even know why I was opposing this. Fixed in commit 3c94a7a.

Marco van 't Wout

Details matter :) Thanks for updating!

Please sign in to comment.
Something went wrong with that request. Please try again.