system system.base system.caching system.caching.dependencies system.collections system.console system.db system.db.ar system.db.schema system.db.schema.cubrid system.db.schema.mssql system.db.schema.mysql system.db.schema.oci system.db.schema.pgsql system.db.schema.sqlite system.gii system.i18n system.i18n.gettext system.logging system.test system.utils system.validators system.web system.web.actions system.web.auth system.web.filters system.web.form system.web.helpers system.web.renderers system.web.services system.web.widgets system.web.widgets.captcha system.web.widgets.pagers zii.behaviors zii.widgets zii.widgets.grid zii.widgets.jui

CWsdlGenerator

system.web.services
继承 class CWsdlGenerator » CComponent
可用自 1.0
源码 framework/web/services/CWsdlGenerator.php
CWsdlGenerator generates the WSDL for a given service class.

The WSDL generation is based on the doc comments found in the service class file. In particular, it recognizes the '@soap' tag in the comment and extracts API method and type definitions.

In a service class, a remote invokable method must be a public method with a doc comment block containing the '@soap' tag. In the doc comment, the type and name of every input parameter and the type of the return value should be declared using the standard phpdoc format.

CWsdlGenerator recognizes the following primitive types (case-sensitive) in the parameter and return type declarations:
  • str/string: maps to xsd:string;
  • int/integer: maps to xsd:int;
  • float/double: maps to xsd:float;
  • bool/boolean: maps to xsd:boolean;
  • date: maps to xsd:date;
  • time: maps to xsd:time;
  • datetime: maps to xsd:dateTime;
  • array: maps to xsd:string;
  • object: maps to xsd:struct;
  • mixed: maps to xsd:anyType.


If a type is not a primitive type, it is considered as a class type, and CWsdlGenerator will look for its property declarations. Only public properties are considered, and they each must be associated with a doc comment block containg the '@soap' tag. The doc comment block should declare the type of the property.

CWsdlGenerator recognizes the array type with the following format:
typeName[]: maps to tns:typeNameArray


The following is an example declaring a remote invokable method:
/ **
  * A foo method.
  * @param string name of something
  * @param string value of something
  * @return string[] some array
  * @soap
  * /
public function foo($name,$value) {...}


And the following is an example declaring a class with remote accessible properties:
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'.

Optionally, extra attributes (nillable, minOccurs, maxOccurs) can be defined for each property by enclosing definitions into curly brackets and separated by comma like so:

{[attribute1 = value1][, attribute2 = value2], ...}

where the attribute can be one of following:
  • nillable = [0|1|true|false]
  • minOccurs = n; where n>=0
  • maxOccurs = n; where [n>=0|unbounded]


Additionally, each complex data type can have assigned a soap indicator flag declaring special usage for such a data type. A soap indicator must be declared in the doc comment block with the '@soap-indicator' tag. Following soap indicators are currently supported:
  • all - (default) allows any sorting order of child nodes
  • sequence - all child nodes in WSDL XML file will be expected in predefined order
  • choice - supplied can be either of the child elements
The Group indicators can be also injected via custom soap definitions as XML node into WSDL structure.

In the following example, class Foo will create a XML node <xsd:Foo><xsd:sequence> ... </xsd:sequence></xsd:Foo> with children attributes expected in pre-defined order.
/ *
  * @soap-indicator sequence
  * /
class Foo {
    ...
}
For more on soap indicators, see See http://www.w3schools.com/schema/schema_complex_indicators.asp.

Since the variability of WSDL definitions is virtually unlimited, a special doc comment tag '@soap-wsdl' can be used in order to inject any custom XML string into generated WSDL file. If such a block of the code is found in class's comment block, then it will be used instead of parsing and generating standard attributes within the class. This gives virtually unlimited flexibility in defining data structures of any complexity. Following is an example of defining custom piece of WSDL XML node:
/ *
  * @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.

By inserting into SOAP URL link the parameter "?makedoc", WSDL generator will output human-friendly overview of all complex data types rather than XML WSDL file. Each complex type is described in a separate HTML table and recognizes also the '@example' PHPDoc tag. See buildHtmlDocs().

公共属性

隐藏继承的属性

属性类型描述被定义在
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

受保护的属性

隐藏继承的属性

属性类型描述被定义在
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

公共方法

隐藏继承的方法

方法描述被定义在
__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

受保护的方法

隐藏继承的方法

方法描述被定义在
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

属性详情

bindingStyle 属性
public array $bindingStyle;

soap:operation style

bindingTransport 属性
public string $bindingTransport;

soap:operation transport

elements 属性
protected array $elements;

messages 属性
protected array $messages;

Map of request and response types for all operations.

namespace 属性
public string $namespace;

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.

operationBodyStyle 属性
public array $operationBodyStyle;

soap:body operation style options

operations 属性
protected array $operations;

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, ..

serviceName 属性
public string $serviceName;

the name of the generated WSDL. If not set, it defaults to "urn:{$className}wsdl".

typeMap 属性
protected static $typeMap;

types 属性
protected array $types;

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..

方法详情

addBindings() 方法
protected void addBindings(DOMDocument $dom)
$dom DOMDocument Represents an entire HTML or XML document; serves as the root of the document tree
源码: framework/web/services/CWsdlGenerator.php#712 (显示)
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']));
}

addMessages() 方法
protected void addMessages(DOMDocument $dom)
$dom DOMDocument Represents an entire HTML or XML document; serves as the root of the document tree
源码: framework/web/services/CWsdlGenerator.php#647 (显示)
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);
    }
}

addPortTypes() 方法
protected void addPortTypes(DOMDocument $dom)
$dom DOMDocument Represents an entire HTML or XML document; serves as the root of the document tree
源码: framework/web/services/CWsdlGenerator.php#677 (显示)
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']));
}

addService() 方法
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
源码: framework/web/services/CWsdlGenerator.php#796 (显示)
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);
}

addTypes() 方法
protected void addTypes(DOMDocument $dom)
$dom DOMDocument Represents an entire HTML or XML document; serves as the root of the document tree
源码: framework/web/services/CWsdlGenerator.php#533 (显示)
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);
}

buildDOM() 方法
protected DOMDocument buildDOM(string $serviceUrl, string $encoding)
$serviceUrl string Web service URL
$encoding string encoding of the WSDL. Defaults to 'UTF-8'.
{return} DOMDocument
源码: framework/web/services/CWsdlGenerator.php#506 (显示)
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;
}

buildHtmlDocs() 方法
public void buildHtmlDocs(bool $return=false)
$return bool If true, generated HTML output will be returned rather than directly sent to output buffer
源码: framework/web/services/CWsdlGenerator.php#830 (显示)
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 '&nbsp;' $prop[3])."</td>"
                            
."\n\t<td>".($prop[4]==null '&nbsp;' $prop[4])."</td>"
                            
."\n\t<td>{$prop[1]}</td>"
                            
."\n\t<td>".(trim($prop[5])=='' '&nbsp;' $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:

  • # - attribute ID
  • Attribute - attribute name, e.g. firstname
  • Type - attribute type, e.g. integer, date, tns:SoapPovCalculationResultArray
  • Nill - true|false - whether the attribute is nillable
  • Min - minimum number of occurrences
  • Max - maximum number of occurrences
  • Description - Detailed description of the attribute.
  • Example - Attribute example value if provided via PHPDoc property @example.

createOperationElement() 方法
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
源码: framework/web/services/CWsdlGenerator.php#735 (显示)
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;
}

createPortElement() 方法
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
源码: framework/web/services/CWsdlGenerator.php#692 (显示)
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;
}

generateWsdl() 方法
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
源码: framework/web/services/CWsdlGenerator.php#236 (显示)
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.

getWsdlElementAttributes() 方法
protected array getWsdlElementAttributes(string $comment)
$comment string Extracted PHPDoc comment
{return} array array of [nillable, minOccurs, maxOccurs]
源码: framework/web/services/CWsdlGenerator.php#450 (显示)
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

injectDom() 方法
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
源码: framework/web/services/CWsdlGenerator.php#485 (显示)
protected function injectDom(DOMDocument $domDOMElement $targetDOMNode $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

processMethod() 方法
protected void processMethod(ReflectionMethod $method)
$method ReflectionMethod method
源码: framework/web/services/CWsdlGenerator.php#265 (显示)
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)),
    );
}

processType() 方法
protected mixed|string processType(string $type)
$type string PHP variable type
{return} mixed|string
源码: framework/web/services/CWsdlGenerator.php#367 (显示)
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 http://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;
    }
}