Package | system.web.services |
---|---|
Inheritance | class CWsdlGenerator » CComponent |
Since | 1.0 |
Source Code | framework/web/services/CWsdlGenerator.php |
typeName[]: maps to tns:typeNameArray
/ ** * A foo method. * @param string name of something * @param string value of something * @return string[] some array * @soap * / public function foo($name,$value) {...}
class Foo { / ** * @var string name of foo {nillable=1, minOccurs=0, maxOccurs=2} * @soap * / public $name; / ** * @var Member[] members of foo * @soap * / public $members; }In the above, the 'members' property is an array of 'Member' objects. Since 'Member' is not a primitive type, CWsdlGenerator will look further to find the definition of 'Member'.
/ * * @soap-indicator sequence * / class Foo { ... }For more on soap indicators, see See https://www.w3schools.com/schema/schema_complex_indicators.asp.
/ * * @soap-wsdl <xsd:sequence> * @soap-wsdl <xsd:element minOccurs="1" maxOccurs="1" nillable="false" name="name" type="xsd:string"/> * @soap-wsdl <xsd:choice minOccurs="1" maxOccurs="1" nillable="false"> * @soap-wsdl <xsd:element minOccurs="1" maxOccurs="1" nillable="false" name="age" type="xsd:integer"/> * @soap-wsdl <xsd:element minOccurs="1" maxOccurs="1" nillable="false" name="date_of_birth" type="xsd:date"/> * @soap-wsdl </xsd:choice> * @soap-wsdl </xsd:sequence> * / class User { / ** * @var string User name {minOccurs=1, maxOccurs=1} * @soap * / public $name; / ** * @var integer User age {nillable=0, minOccurs=1, maxOccurs=1} * @example 35 * @soap * / public $age; / ** * @var date User's birthday {nillable=0, minOccurs=1, maxOccurs=1} * @example 1980-05-27 * @soap * / public $date_of_birth; }In the example above, WSDL generator would inject under XML node <xsd:User> the code block defined by @soap-wsdl lines.
Property | Type | Description | Defined By |
---|---|---|---|
bindingStyle | array | soap:operation style | CWsdlGenerator |
bindingTransport | string | soap:operation transport | CWsdlGenerator |
namespace | string | the namespace to be used in the generated WSDL. | CWsdlGenerator |
operationBodyStyle | array | soap:body operation style options | CWsdlGenerator |
serviceName | string | the name of the generated WSDL. | CWsdlGenerator |
Property | Type | Description | Defined By |
---|---|---|---|
elements | array | CWsdlGenerator | |
messages | array | Map of request and response types for all operations. | CWsdlGenerator |
operations | array | List of recognized SOAP operations that will become remotely available. | CWsdlGenerator |
typeMap | CWsdlGenerator | ||
types | array | List of complex types used by operations. | CWsdlGenerator |
Method | Description | Defined By |
---|---|---|
__call() | Calls the named method which is not a class method. | CComponent |
__get() | Returns a property value, an event handler list or a behavior based on its name. | CComponent |
__isset() | Checks if a property value is null. | CComponent |
__set() | Sets value of a component property. | CComponent |
__unset() | Sets a component property to be null. | CComponent |
asa() | Returns the named behavior object. | CComponent |
attachBehavior() | Attaches a behavior to this component. | CComponent |
attachBehaviors() | Attaches a list of behaviors to the component. | CComponent |
attachEventHandler() | Attaches an event handler to an event. | CComponent |
buildHtmlDocs() | Generate human friendly HTML documentation for complex data types. | CWsdlGenerator |
canGetProperty() | Determines whether a property can be read. | CComponent |
canSetProperty() | Determines whether a property can be set. | CComponent |
detachBehavior() | Detaches a behavior from the component. | CComponent |
detachBehaviors() | Detaches all behaviors from the component. | CComponent |
detachEventHandler() | Detaches an existing event handler. | CComponent |
disableBehavior() | Disables an attached behavior. | CComponent |
disableBehaviors() | Disables all behaviors attached to this component. | CComponent |
enableBehavior() | Enables an attached behavior. | CComponent |
enableBehaviors() | Enables all behaviors attached to this component. | CComponent |
evaluateExpression() | Evaluates a PHP expression or callback under the context of this component. | CComponent |
generateWsdl() | Generates the WSDL for the given class. | CWsdlGenerator |
getEventHandlers() | Returns the list of attached event handlers for an event. | CComponent |
hasEvent() | Determines whether an event is defined. | CComponent |
hasEventHandler() | Checks whether the named event has attached handlers. | CComponent |
hasProperty() | Determines whether a property is defined. | CComponent |
raiseEvent() | Raises an event. | CComponent |
Method | Description | Defined By |
---|---|---|
addBindings() | CWsdlGenerator | |
addMessages() | CWsdlGenerator | |
addPortTypes() | CWsdlGenerator | |
addService() | CWsdlGenerator | |
addTypes() | CWsdlGenerator | |
buildDOM() | CWsdlGenerator | |
createOperationElement() | CWsdlGenerator | |
createPortElement() | CWsdlGenerator | |
getWsdlElementAttributes() | Parse attributes nillable, minOccurs, maxOccurs | CWsdlGenerator |
injectDom() | Import custom XML source node into WSDL document under specified target node | CWsdlGenerator |
processMethod() | CWsdlGenerator | |
processType() | CWsdlGenerator |
soap:operation style
soap:operation transport
Map of request and response types for all operations.
the namespace to be used in the generated WSDL. If not set, it defaults to the name of the class that WSDL is generated upon.
soap:body operation style options
List of recognized SOAP operations that will become remotely available. All methods with declared @soap parameter will be included here in the format operation1 => description1, operation2 => description2, ..
the name of the generated WSDL. If not set, it defaults to "urn:{$className}wsdl".
List of complex types used by operations. If an SOAP operation defines complex input or output type, all objects are included here containing all sub-parameters. For instance, if an SOAP operation "createUser" requires complex input object "User", then the object "User" will be included here with declared subparameters such as "firstname", "lastname", etc..
protected void addBindings(DOMDocument $dom)
| ||
$dom | DOMDocument | Represents an entire HTML or XML document; serves as the root of the document tree |
protected function addBindings($dom)
{
$binding=$dom->createElement('wsdl:binding');
$binding->setAttribute('name',$this->serviceName.'Binding');
$binding->setAttribute('type','tns:'.$this->serviceName.'PortType');
$soapBinding=$dom->createElement('soap:binding');
$soapBinding->setAttribute('style',$this->bindingStyle);
$soapBinding->setAttribute('transport',$this->bindingTransport);
$binding->appendChild($soapBinding);
$dom->documentElement->appendChild($binding);
foreach($this->operations as $name=>$operation)
$binding->appendChild($this->createOperationElement($dom,$name,$operation['headers']));
}
protected void addMessages(DOMDocument $dom)
| ||
$dom | DOMDocument | Represents an entire HTML or XML document; serves as the root of the document tree |
protected function addMessages($dom)
{
foreach($this->messages as $name=>$message)
{
$element=$dom->createElement('wsdl:message');
$element->setAttribute('name',$name);
foreach($this->messages[$name] as $partName=>$part)
{
if(is_array($part))
{
$partElement=$dom->createElement('wsdl:part');
$partElement->setAttribute('name',$partName);
if (isset($part['type']))
{
$partElement->setAttribute('type',$part['type']);
}
if (isset($part['element']))
{
$partElement->setAttribute('element',$part['element']);
}
$element->appendChild($partElement);
}
}
$dom->documentElement->appendChild($element);
}
}
protected void addPortTypes(DOMDocument $dom)
| ||
$dom | DOMDocument | Represents an entire HTML or XML document; serves as the root of the document tree |
protected function addPortTypes($dom)
{
$portType=$dom->createElement('wsdl:portType');
$portType->setAttribute('name',$this->serviceName.'PortType');
$dom->documentElement->appendChild($portType);
foreach($this->operations as $name=>$operation)
$portType->appendChild($this->createPortElement($dom,$name,$operation['doc']));
}
protected void addService(DOMDocument $dom, string $serviceUrl)
| ||
$dom | DOMDocument | Represents an entire HTML or XML document; serves as the root of the document tree |
$serviceUrl | string | Web service URL |
protected function addService($dom,$serviceUrl)
{
$service=$dom->createElement('wsdl:service');
$service->setAttribute('name', $this->serviceName.'Service');
$port=$dom->createElement('wsdl:port');
$port->setAttribute('name', $this->serviceName.'Port');
$port->setAttribute('binding', 'tns:'.$this->serviceName.'Binding');
$soapAddress=$dom->createElement('soap:address');
$soapAddress->setAttribute('location',$serviceUrl);
$port->appendChild($soapAddress);
$service->appendChild($port);
$dom->documentElement->appendChild($service);
}
protected void addTypes(DOMDocument $dom)
| ||
$dom | DOMDocument | Represents an entire HTML or XML document; serves as the root of the document tree |
protected function addTypes($dom)
{
if($this->types===array() && $this->elements===array())
return;
$types=$dom->createElement('wsdl:types');
$schema=$dom->createElement('xsd:schema');
$schema->setAttribute('targetNamespace',$this->namespace);
foreach($this->types as $phpType=>$xmlType)
{
if(is_string($xmlType) && strrpos($xmlType,'Array')!==strlen($xmlType)-5)
continue; // simple type
$complexType=$dom->createElement('xsd:complexType');
if(is_string($xmlType))
{
if(($pos=strpos($xmlType,'tns:'))!==false)
$complexType->setAttribute('name',substr($xmlType,4));
else
$complexType->setAttribute('name',$xmlType);
$arrayType = ($dppos=strpos($xmlType,':')) !==false ? substr($xmlType,$dppos + 1) : $xmlType; // strip namespace, if any
$arrayType = substr($arrayType,0,-5); // strip 'Array' from name
if ($this->operationBodyStyle['use'] == self::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('wsdl: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))
{
$complexType->setAttribute('name',$phpType);
if($xmlType['custom_wsdl']!==false)
{
$custom_dom=new DOMDocument();
$custom_dom->loadXML('<root xmlns:xsd="http://www.w3.org/2001/XMLSchema">'.$xmlType['custom_wsdl'].'</root>');
foreach($custom_dom->documentElement->childNodes as $el)
$this->injectDom($dom,$complexType,$el);
}else{
$all=$dom->createElement('xsd:' . $xmlType['indicator']);
if(!is_null($xmlType['minOccurs']))
$all->setAttribute('minOccurs',$xmlType['minOccurs']);
if(!is_null($xmlType['maxOccurs']))
$all->setAttribute('maxOccurs',$xmlType['maxOccurs']);
if(!is_null($xmlType['nillable']))
$all->setAttribute('nillable',$xmlType['nillable']);
foreach($xmlType['properties'] as $name=>$type)
{
$element=$dom->createElement('xsd:element');
if(!is_null($type[3]))
$element->setAttribute('minOccurs',$type[3]);
if(!is_null($type[4]))
$element->setAttribute('maxOccurs',$type[4]);
if(!is_null($type[2]))
$element->setAttribute('nillable',$type[2]);
$element->setAttribute('name',$name);
$element->setAttribute('type',$type[0]);
$all->appendChild($element);
}
$complexType->appendChild($all);
}
}
$schema->appendChild($complexType);
}
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);
}
protected DOMDocument buildDOM(string $serviceUrl, string $encoding)
| ||
$serviceUrl | string | Web service URL |
$encoding | string | encoding of the WSDL. Defaults to 'UTF-8'. |
{return} | DOMDocument |
protected function buildDOM($serviceUrl,$encoding)
{
$xml="<?xml version=\"1.0\" encoding=\"$encoding\"?>
<definitions name=\"{$this->serviceName}\" targetNamespace=\"{$this->namespace}\"
xmlns=\"http://schemas.xmlsoap.org/wsdl/\"
xmlns:tns=\"{$this->namespace}\"
xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\"
xmlns:soap-enc=\"http://schemas.xmlsoap.org/soap/encoding/\"></definitions>";
$dom=new DOMDocument();
$dom->formatOutput=true;
$dom->loadXml($xml);
$this->addTypes($dom);
$this->addMessages($dom);
$this->addPortTypes($dom);
$this->addBindings($dom);
$this->addService($dom,$serviceUrl);
return $dom;
}
public void buildHtmlDocs(bool $return=false)
| ||
$return | bool | If true, generated HTML output will be returned rather than directly sent to output buffer |
public function buildHtmlDocs($return=false)
{
$html='<html><head>';
$html.='<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
$html.='<style type="text/css">
table{border-collapse: collapse;background-color: #DDDDDD;}
tr{background-color: #FFFFFF;}
th{background-color: #EEEEEE;}
th, td{font-size: 12px;font-family: courier;padding: 3px;}
</style>';
$html.='</head><body>';
$html.='<h2>WSDL documentation for service '.$this->serviceName.'</h2>';
$html.='<p>Generated on '.date('d.m.Y H:i:s').'</p>';
$html.='<table border="0" cellspacing="1" cellpadding="1">';
$html.='<tr><td>';
if(!empty($this->types))
{
foreach($this->types as $object=>$options){
if(!is_array($options) || empty($options) || !is_array($options['properties']) || empty($options['properties'])){
continue;
}
$params=$options['properties'];
$html.="\n\n<h3>Object: {$object}</h3>";
$html.='<table border="1" cellspacing="1" cellpadding="1">';
$html.='<tr><th>#</th><th>Attribute</th><th>Type</th><th>Nill</th><th>Min</th><th>Max</th><th>Description</th><th>Example</th></tr>';
$c=0;
foreach($params as $param=>$prop){
++$c;
$html.="\n<tr>"
."\n\t<td>{$c}</td>"
."\n\t<td>{$param}</td>"
."\n\t<td>".(str_replace('xsd:','',$prop[0]))."</td>"
."\n\t<td>".$prop[2]."</td>"
."\n\t<td>".($prop[3]==null ? ' ' : $prop[3])."</td>"
."\n\t<td>".($prop[4]==null ? ' ' : $prop[4])."</td>"
."\n\t<td>{$prop[1]}</td>"
."\n\t<td>".(trim($prop[5])=='' ? ' ' : $prop[5])."</td>"
."\n</tr>";
}
$html.="\n</table><br/>";
}
}
else
$html.='No complex data type found!';
$html.='</td></tr></table></body></html>';
if($return)
return $html;
echo $html;
Yii::app()->end(); // end the app to avoid conflict with text/xml header
}
Generate human friendly HTML documentation for complex data types.
This method can be invoked either by inserting URL parameter "&makedoc" into URL link, e.g. "http://www.mydomain.com/soap/create?makedoc", or simply by calling from another script with argument $return=true.
Each complex data type is described in a separate HTML table containing following columns:
protected DOMElement createOperationElement(DOMDocument $dom, string $name, array $headers=NULL)
| ||
$dom | DOMDocument | Represents an entire HTML or XML document; serves as the root of the document tree |
$name | string | method name |
$headers | array | array like array('input'=>array(MESSAGE,PART),'output=>array(MESSAGE,PART)) |
{return} | DOMElement | a new instance of wsdl:operation element |
protected function createOperationElement($dom,$name,$headers=null)
{
$operation=$dom->createElement('wsdl:operation');
$operation->setAttribute('name', $name);
$soapOperation=$dom->createElement('soap:operation');
$soapOperation->setAttribute('soapAction', $this->namespace.'#'.$name);
if ($this->bindingStyle == self::STYLE_RPC)
{
$soapOperation->setAttribute('style', self::STYLE_RPC);
}
$input=$dom->createElement('wsdl:input');
$output=$dom->createElement('wsdl:output');
$soapBody=$dom->createElement('soap:body');
$operationBodyStyle=$this->operationBodyStyle;
if ($this->bindingStyle == self::STYLE_RPC && !isset($operationBodyStyle['namespace']))
{
$operationBodyStyle['namespace'] = $this->namespace;
}
foreach($operationBodyStyle as $attributeName=>$attributeValue)
{
$soapBody->setAttribute($attributeName, $attributeValue);
}
$input->appendChild($soapBody);
$output->appendChild(clone $soapBody);
if (is_array($headers))
{
if (isset($headers['input']) && is_array($headers['input']) && count($headers['input'])==2)
{
$soapHeader = $dom->createElement('soap:header');
foreach($operationBodyStyle as $attributeName=>$attributeValue) {
$soapHeader->setAttribute($attributeName, $attributeValue);
}
$soapHeader->setAttribute('message', $headers['input'][0]);
$soapHeader->setAttribute('part', $headers['input'][1]);
$input->appendChild($soapHeader);
}
if (isset($headers['output']) && is_array($headers['output']) && count($headers['output'])==2)
{
$soapHeader = $dom->createElement('soap:header');
foreach($operationBodyStyle as $attributeName=>$attributeValue) {
$soapHeader->setAttribute($attributeName, $attributeValue);
}
$soapHeader->setAttribute('message', $headers['output'][0]);
$soapHeader->setAttribute('part', $headers['output'][1]);
$output->appendChild($soapHeader);
}
}
$operation->appendChild($soapOperation);
$operation->appendChild($input);
$operation->appendChild($output);
return $operation;
}
protected DOMElement createPortElement(DOMDocument $dom, string $name, string $doc)
| ||
$dom | DOMDocument | Represents an entire HTML or XML document; serves as the root of the document tree |
$name | string | method name |
$doc | string | doc |
{return} | DOMElement | a new instance of wsdl:operation element filled by tns In/Out data |
protected function createPortElement($dom,$name,$doc)
{
$operation=$dom->createElement('wsdl:operation');
$operation->setAttribute('name',$name);
$input=$dom->createElement('wsdl:input');
$input->setAttribute('message', 'tns:'.$name.'In');
$output=$dom->createElement('wsdl:output');
$output->setAttribute('message', 'tns:'.$name.'Out');
$operation->appendChild($dom->createElement('wsdl:documentation',$doc));
$operation->appendChild($input);
$operation->appendChild($output);
return $operation;
}
public string generateWsdl(string $className, string $serviceUrl, string $encoding='UTF-8')
| ||
$className | string | class name |
$serviceUrl | string | Web service URL |
$encoding | string | encoding of the WSDL. Defaults to 'UTF-8'. |
{return} | string | the generated WSDL |
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;
if($this->namespace===null)
$this->namespace='urn:'.str_replace('\\','/',$className).'wsdl';
$reflection=new ReflectionClass($className);
foreach($reflection->getMethods() as $method)
{
if($method->isPublic())
$this->processMethod($method);
}
$wsdl=$this->buildDOM($serviceUrl,$encoding)->saveXML();
if(isset($_GET['makedoc']))
$this->buildHtmlDocs();
return $wsdl;
}
Generates the WSDL for the given class.
protected array getWsdlElementAttributes(string $comment)
| ||
$comment | string | Extracted PHPDoc comment |
{return} | array | array of [nillable, minOccurs, maxOccurs] |
protected function getWsdlElementAttributes($comment) {
$nillable=$minOccurs=$maxOccurs=null;
if(preg_match('/{(.+)}/',$comment,$attr))
{
if(preg_match_all('/((\w+)\s*=\s*(\w+))/mi',$attr[1],$attr))
{
foreach($attr[2] as $id=>$prop)
{
$prop=strtolower($prop);
$val=strtolower($attr[3][$id]);
if($prop=='nillable'){
if($val=='false' || $val=='true')
$nillable=$val;
else
$nillable=$val ? 'true' : 'false';
}elseif($prop=='minoccurs')
$minOccurs=intval($val);
elseif($prop=='maxoccurs')
$maxOccurs=($val=='unbounded') ? 'unbounded' : intval($val);
}
}
}
return array(
'nillable'=>$nillable,
'minOccurs'=>$minOccurs,
'maxOccurs'=>$maxOccurs
);
}
Parse attributes nillable, minOccurs, maxOccurs
protected void injectDom(DOMDocument $dom, DOMElement $target, DOMNode $source)
| ||
$dom | DOMDocument | XML WSDL document being generated |
$target | DOMElement | XML node, to which will be appended $source node |
$source | DOMNode | Source XML node to be imported |
protected function injectDom(DOMDocument $dom, DOMElement $target, DOMNode $source)
{
if ($source->nodeType!=XML_ELEMENT_NODE)
return;
$import=$dom->createElement($source->nodeName);
foreach($source->attributes as $attr)
$import->setAttribute($attr->name,$attr->value);
foreach($source->childNodes as $child)
$this->injectDom($dom,$import,$child);
$target->appendChild($import);
}
Import custom XML source node into WSDL document under specified target node
protected void processMethod(ReflectionMethod $method)
| ||
$method | ReflectionMethod | method |
protected function processMethod($method)
{
$comment=$method->getDocComment();
if(strpos($comment,'@soap')===false)
return;
$comment=strtr($comment,array("\r\n"=>"\n","\r"=>"\n")); // make line endings consistent: win -> unix, mac -> unix
$methodName=$method->getName();
$comment=preg_replace('/^\s*\**(\s*?$|\s*)/m','',$comment);
$params=$method->getParameters();
$message=array();
$headers=array();
$n=preg_match_all('/^@param\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches);
if($n>count($params))
$n=count($params);
if ($this->bindingStyle == self::STYLE_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.'In']=$message;
$n=preg_match_all('/^@header\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches);
for($i=0;$i<$n;++$i)
{
$name = $matches[1][$i];
$type = $this->processType($matches[1][$i]);
$doc = trim($matches[3][$i]);
if ($this->bindingStyle == self::STYLE_RPC)
{
$headers[$name]=array($type,$doc);
}
else
{
$this->elements[$name][$name]=array('type'=>$type);
$headers[$name] = array('element'=>$type);
}
}
if ($headers !== array())
{
$this->messages[$methodName.'Headers']=$headers;
$headerKeys = array_keys($headers);
$firstHeaderKey = reset($headerKeys);
$firstHeader = $headers[$firstHeaderKey];
}
else
{
$firstHeader = null;
}
if ($this->bindingStyle == self::STYLE_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
$doc='';
$this->operations[$methodName]=array(
'doc'=>$doc,
'headers'=>$firstHeader === null ? null : array('input'=>array($methodName.'Headers', $firstHeaderKey)),
);
}
protected mixed|string processType(string $type)
| ||
$type | string | PHP variable type |
{return} | mixed|string |
protected function processType($type)
{
if(isset(self::$typeMap[$type]))
return self::$typeMap[$type];
elseif(isset($this->types[$type]))
return is_array($this->types[$type]) ? 'tns:'.$type : $this->types[$type];
elseif(($pos=strpos($type,'[]'))!==false)
{ // array of types
$type=substr($type,0,$pos);
$this->types[$type.'[]']='tns:'.$type.'Array';
$this->processType($type);
return $this->types[$type.'[]'];
}
else
{ // process class / complex type
$type=Yii::import($type,true);
$class=new ReflectionClass($type);
$comment=$class->getDocComment();
$comment=strtr($comment,array("\r\n"=>"\n","\r"=>"\n")); // make line endings consistent: win -> unix, mac -> unix
$comment=preg_replace('/^\s*\**(\s*?$|\s*)/m','',$comment);
// extract soap indicator flag, if defined, e.g. @soap-indicator sequence
// see https://www.w3schools.com/schema/schema_complex_indicators.asp
if(preg_match('/^@soap-indicator\s+(\w+)\s*?(.*)$/im', $comment, $matches))
{
$indicator=$matches[1];
$attributes=$this->getWsdlElementAttributes($matches[2]);
}else{
$indicator='all';
$attributes=$this->getWsdlElementAttributes('');
}
$custom_wsdl=false;
if(preg_match_all('/^@soap-wsdl\s+(\S.*)$/im',$comment,$matches)>0)
$custom_wsdl=implode("\n", $matches[1]);
$this->types[$type]=array(
'indicator'=>$indicator,
'nillable'=>$attributes['nillable'],
'minOccurs'=>$attributes['minOccurs'],
'maxOccurs'=>$attributes['maxOccurs'],
'custom_wsdl'=>$custom_wsdl,
'properties'=>array()
);
foreach($class->getProperties() as $property)
{
$comment=$property->getDocComment();
if($property->isPublic() && strpos($comment,'@soap')!==false)
{
if(preg_match('/@var\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/mi',$comment,$matches))
{
$attributes=$this->getWsdlElementAttributes($matches[3]);
if(preg_match('/{(.+)}/',$comment,$attr))
$matches[3]=str_replace($attr[0],'',$matches[3]);
// extract PHPDoc @example
$example='';
if(preg_match("/@example[:]?(.+)/mi",$comment,$match))
$example=trim($match[1]);
$this->types[$type]['properties'][$property->getName()]=array(
$this->processType($matches[1]),
trim($matches[3]),
$attributes['nillable'],
$attributes['minOccurs'],
$attributes['maxOccurs'],
$example
); // name => type, doc, nillable, minOccurs, maxOccurs, example
}
}
}
return 'tns:'.$type;
}
}
Signup or Login in order to comment.