0 follower

CWsdlGenerator

Package system.web.services
Inheritance class CWsdlGenerator » CComponent
Since 1.0
Source Code 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 https://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().

Public Properties

Hide inherited properties

PropertyTypeDescriptionDefined 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

Protected Properties

Hide inherited properties

PropertyTypeDescriptionDefined 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

Public Methods

Hide inherited methods

MethodDescriptionDefined 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

Protected Methods

Hide inherited methods

MethodDescriptionDefined 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

Property Details

bindingStyle property
public array $bindingStyle;

soap:operation style

bindingTransport property
public string $bindingTransport;

soap:operation transport

elements property
protected array $elements;

messages property
protected array $messages;

Map of request and response types for all operations.

namespace property
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 property
public array $operationBodyStyle;

soap:body operation style options

operations property
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 property
public string $serviceName;

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

typeMap property
protected static $typeMap;

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

Method Details

addBindings() method
protected void addBindings(DOMDocument $dom)
$dom DOMDocument Represents an entire HTML or XML document; serves as the root of the document tree
Source Code: framework/web/services/CWsdlGenerator.php#712 (show)
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() method
protected void addMessages(DOMDocument $dom)
$dom DOMDocument Represents an entire HTML or XML document; serves as the root of the document tree
Source Code: framework/web/services/CWsdlGenerator.php#647 (show)
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() method
protected void addPortTypes(DOMDocument $dom)
$dom DOMDocument Represents an entire HTML or XML document; serves as the root of the document tree
Source Code: framework/web/services/CWsdlGenerator.php#677 (show)
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() method
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
Source Code: framework/web/services/CWsdlGenerator.php#796 (show)
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() method
protected void addTypes(DOMDocument $dom)
$dom DOMDocument Represents an entire HTML or XML document; serves as the root of the document tree
Source Code: framework/web/services/CWsdlGenerator.php#533 (show)
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() method
protected DOMDocument buildDOM(string $serviceUrl, string $encoding)
$serviceUrl string Web service URL
$encoding string encoding of the WSDL. Defaults to 'UTF-8'.
{return} DOMDocument
Source Code: framework/web/services/CWsdlGenerator.php#506 (show)
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() method
public void buildHtmlDocs(bool $return=false)
$return bool If true, generated HTML output will be returned rather than directly sent to output buffer
Source Code: framework/web/services/CWsdlGenerator.php#830 (show)
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() method
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
Source Code: framework/web/services/CWsdlGenerator.php#735 (show)
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() method
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
Source Code: framework/web/services/CWsdlGenerator.php#692 (show)
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() method
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
Source Code: framework/web/services/CWsdlGenerator.php#236 (show)
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() method
protected array getWsdlElementAttributes(string $comment)
$comment string Extracted PHPDoc comment
{return} array array of [nillable, minOccurs, maxOccurs]
Source Code: framework/web/services/CWsdlGenerator.php#450 (show)
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() method
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
Source Code: framework/web/services/CWsdlGenerator.php#485 (show)
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() method
protected void processMethod(ReflectionMethod $method)
$method ReflectionMethod method
Source Code: framework/web/services/CWsdlGenerator.php#265 (show)
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() method
protected mixed|string processType(string $type)
$type string PHP variable type
{return} mixed|string
Source Code: framework/web/services/CWsdlGenerator.php#367 (show)
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;
    }
}