diff --git a/IWebServiceProvider.php b/IWebServiceProvider.php index 3752bdd..7c65e21 100644 --- a/IWebServiceProvider.php +++ b/IWebServiceProvider.php @@ -18,15 +18,15 @@ */ interface IWebServiceProvider { - /** - * This method is invoked before the requested remote method is invoked. - * @param WebService $service the currently requested Web service. - * @return boolean whether the remote method should be executed. - */ - public function beforeWebMethod($service); - /** - * This method is invoked after the requested remote method is invoked. - * @param WebService $service the currently requested Web service. - */ - public function afterWebMethod($service); + /** + * This method is invoked before the requested remote method is invoked. + * @param WebService $service the currently requested Web service. + * @return boolean whether the remote method should be executed. + */ + public function beforeWebMethod($service); + /** + * This method is invoked after the requested remote method is invoked. + * @param WebService $service the currently requested Web service. + */ + public function afterWebMethod($service); } \ No newline at end of file diff --git a/WebService.php b/WebService.php index e77e197..e848edc 100644 --- a/WebService.php +++ b/WebService.php @@ -31,265 +31,265 @@ */ class WebService extends \yii\base\Component { - const SOAP_ERROR=1001; - /** - * @var string|object the web service provider class or object. - * If specified as a class name, it can be a path alias. - */ - public $provider; - /** - * @var string the URL for WSDL. This is required by {@link run()}. - */ - public $wsdlUrl; - /** - * @var string the URL for the Web service. This is required by {@link generateWsdl()} and {@link renderWsdl()}. - */ - public $serviceUrl; - /** - * @var integer number of seconds that the generated WSDL can remain valid in cache. Defaults to 0, meaning no caching. - */ - public $wsdlCacheDuration=0; - /** - * @var string the ID of the cache application component that is used to cache the generated WSDL. - * Defaults to 'cache' which refers to the primary cache application component. - * Set this property to false if you want to disable caching WSDL. - */ - public $cacheID='cache'; - /** - * @var string encoding of the Web service. Defaults to 'UTF-8'. - */ - public $encoding='UTF-8'; - /** - * A list of classes that are declared as complex types in WSDL. - * This should be an array with WSDL types as keys and names of PHP classes as values. - * A PHP class can also be specified as a path alias. - * @var array - * @see http://www.php.net/manual/en/soapserver.soapserver.php - */ - public $classMap=array(); - /** - * @var string actor of the SOAP service. Defaults to null, meaning not set. - */ - public $actor; - /** - * @var string SOAP version (e.g. '1.1' or '1.2'). Defaults to null, meaning not set. - */ - public $soapVersion; - /** - * @var integer the persistence mode of the SOAP server. - * @see http://www.php.net/manual/en/soapserver.setpersistence.php - */ - public $persistence; - /** - * WSDL generator configuration. This property may be useful in purpose of enhancing features - * of the standard {@link WsdlGenerator} class by extending it. For example, some developers may need support - * of the xsd:xsd:base64Binary elements. Another use case is to change initial values - * at instantiation of the default {@link WsdlGenerator}. The value of this property will be passed - * to {@link \Yii::createObject()} to create the generator object. Default value is 'WsdlGenerator'. - * @var string|array - * @since 1.1.12 - */ - public $generatorConfig; + const SOAP_ERROR=1001; + /** + * @var string|object the web service provider class or object. + * If specified as a class name, it can be a path alias. + */ + public $provider; + /** + * @var string the URL for WSDL. This is required by {@link run()}. + */ + public $wsdlUrl; + /** + * @var string the URL for the Web service. This is required by {@link generateWsdl()} and {@link renderWsdl()}. + */ + public $serviceUrl; + /** + * @var integer number of seconds that the generated WSDL can remain valid in cache. Defaults to 0, meaning no caching. + */ + public $wsdlCacheDuration=0; + /** + * @var string the ID of the cache application component that is used to cache the generated WSDL. + * Defaults to 'cache' which refers to the primary cache application component. + * Set this property to false if you want to disable caching WSDL. + */ + public $cacheID='cache'; + /** + * @var string encoding of the Web service. Defaults to 'UTF-8'. + */ + public $encoding='UTF-8'; + /** + * A list of classes that are declared as complex types in WSDL. + * This should be an array with WSDL types as keys and names of PHP classes as values. + * A PHP class can also be specified as a path alias. + * @var array + * @see http://www.php.net/manual/en/soapserver.soapserver.php + */ + public $classMap=array(); + /** + * @var string actor of the SOAP service. Defaults to null, meaning not set. + */ + public $actor; + /** + * @var string SOAP version (e.g. '1.1' or '1.2'). Defaults to null, meaning not set. + */ + public $soapVersion; + /** + * @var integer the persistence mode of the SOAP server. + * @see http://www.php.net/manual/en/soapserver.setpersistence.php + */ + public $persistence; + /** + * WSDL generator configuration. This property may be useful in purpose of enhancing features + * of the standard {@link WsdlGenerator} class by extending it. For example, some developers may need support + * of the xsd:xsd:base64Binary elements. Another use case is to change initial values + * at instantiation of the default {@link WsdlGenerator}. The value of this property will be passed + * to {@link \Yii::createObject()} to create the generator object. Default value is 'WsdlGenerator'. + * @var string|array + * @since 1.1.12 + */ + public $generatorConfig; - private $_method; + private $_method; - /** - * Constructor. - * @param mixed $provider the web service provider class name or object - * @param string $wsdlUrl the URL for WSDL. This is required by {@link run()}. - * @param string $serviceUrl the URL for the Web service. This is required by {@link generateWsdl()} and {@link renderWsdl()}. - */ - public function __construct($provider,$wsdlUrl,$serviceUrl) - { - $this->provider=$provider; - $this->wsdlUrl=$wsdlUrl; - $this->serviceUrl=$serviceUrl; - $this->generatorConfig=WsdlGenerator::className(); - } + /** + * Constructor. + * @param mixed $provider the web service provider class name or object + * @param string $wsdlUrl the URL for WSDL. This is required by {@link run()}. + * @param string $serviceUrl the URL for the Web service. This is required by {@link generateWsdl()} and {@link renderWsdl()}. + */ + public function __construct($provider,$wsdlUrl,$serviceUrl) + { + $this->provider=$provider; + $this->wsdlUrl=$wsdlUrl; + $this->serviceUrl=$serviceUrl; + $this->generatorConfig=WsdlGenerator::className(); + } - /** - * The PHP error handler. - * @param CErrorEvent $event the PHP error event - */ - public function handleError($event) - { - $event->handled=true; - $message=$event->message; - if(YII_DEBUG) - { - $trace=debug_backtrace(); - if(isset($trace[2]) && isset($trace[2]['file']) && isset($trace[2]['line'])) - $message.=' ('.$trace[2]['file'].':'.$trace[2]['line'].')'; - } - throw new CException($message,self::SOAP_ERROR); - } + /** + * The PHP error handler. + * @param CErrorEvent $event the PHP error event + */ + public function handleError($event) + { + $event->handled=true; + $message=$event->message; + if(YII_DEBUG) + { + $trace=debug_backtrace(); + if(isset($trace[2]) && isset($trace[2]['file']) && isset($trace[2]['line'])) + $message.=' ('.$trace[2]['file'].':'.$trace[2]['line'].')'; + } + throw new \Exception($message,self::SOAP_ERROR); + } - /** - * Generates and displays the WSDL as defined by the provider. - * @see generateWsdl - */ - public function renderWsdl() - { - $wsdl=$this->generateWsdl(); - $response=\Yii::$app->response; - $response->charset=$this->encoding; - $response->format=Response::FORMAT_RAW; - $response->headers->add('Content-Type', 'text/xml'); - // header('Content-Length: '.(function_exists('mb_strlen') ? mb_strlen($wsdl,'8bit') : strlen($wsdl))); - return $wsdl; - } + /** + * Generates and displays the WSDL as defined by the provider. + * @see generateWsdl + */ + public function renderWsdl() + { + $wsdl=$this->generateWsdl(); + $response=\Yii::$app->response; + $response->charset=$this->encoding; + $response->format=Response::FORMAT_RAW; + $response->headers->add('Content-Type', 'text/xml'); + // header('Content-Length: '.(function_exists('mb_strlen') ? mb_strlen($wsdl,'8bit') : strlen($wsdl))); + return $wsdl; + } - /** - * Generates the WSDL as defined by the provider. - * The cached version may be used if the WSDL is found valid in cache. - * @return string the generated WSDL - * @see wsdlCacheDuration - */ - public function generateWsdl() - { - if(is_object($this->provider)) - $providerClass=get_class($this->provider); - else{ - \Yii::autoload($this->provider); - $providerClass=$this->provider; - } - if($this->wsdlCacheDuration>0 && $this->cacheID!==false && ($cache=\Yii::$app->get($this->cacheID,false))!==null) - { - $key='Yii.WebService.'.$providerClass.$this->serviceUrl.$this->encoding; - if(($wsdl=$cache->get($key))!==false) - return $wsdl; - } - $generator=\Yii::createObject($this->generatorConfig); - $wsdl=$generator->generateWsdl($providerClass,$this->serviceUrl,$this->encoding); - if(isset($key)) - $cache->set($key,$wsdl,$this->wsdlCacheDuration); - return $wsdl; - } + /** + * Generates the WSDL as defined by the provider. + * The cached version may be used if the WSDL is found valid in cache. + * @return string the generated WSDL + * @see wsdlCacheDuration + */ + public function generateWsdl() + { + if(is_object($this->provider)) + $providerClass=get_class($this->provider); + else{ + \Yii::autoload($this->provider); + $providerClass=$this->provider; + } + if($this->wsdlCacheDuration>0 && $this->cacheID!==false && ($cache=\Yii::$app->get($this->cacheID,false))!==null) + { + $key='Yii.WebService.'.$providerClass.$this->serviceUrl.$this->encoding; + if(($wsdl=$cache->get($key))!==false) + return $wsdl; + } + $generator=\Yii::createObject($this->generatorConfig); + $wsdl=$generator->generateWsdl($providerClass,$this->serviceUrl,$this->encoding); + if(isset($key)) + $cache->set($key,$wsdl,$this->wsdlCacheDuration); + return $wsdl; + } - /** - * Handles the web service request. - */ - public function run() - { - $response=\Yii::$app->response; - $response->format=Response::FORMAT_RAW; - $response->charset=$this->encoding; - $response->headers->add('Content-Type', 'text/xml'); - if(YII_DEBUG) - ini_set("soap.wsdl_cache_enabled",0); - $server=new \SoapServer($this->wsdlUrl,$this->getOptions()); - // \Yii::$app->on($name, $behavior)EventHandler('onError',array($this,'handleError')); - try - { - if($this->persistence!==null) - $server->setPersistence($this->persistence); - if(is_string($this->provider)) - $provider=\Yii::createObject($this->provider); - else - $provider=$this->provider; + /** + * Handles the web service request. + */ + public function run() + { + $response=\Yii::$app->response; + $response->format=Response::FORMAT_RAW; + $response->charset=$this->encoding; + $response->headers->add('Content-Type', 'text/xml'); + if(YII_DEBUG) + ini_set("soap.wsdl_cache_enabled",0); + $server=new \SoapServer($this->wsdlUrl,$this->getOptions()); + // \Yii::$app->on($name, $behavior)EventHandler('onError',array($this,'handleError')); + try + { + if($this->persistence!==null) + $server->setPersistence($this->persistence); + if(is_string($this->provider)) + $provider=\Yii::createObject($this->provider); + else + $provider=$this->provider; - if(method_exists($server,'setObject')) - { - if (is_array($this->generatorConfig) && isset($this->generatorConfig['bindingStyle']) - && $this->generatorConfig['bindingStyle']==='document') - { - $server->setObject(new DocumentSoapObjectWrapper($provider)); - } - else - { - $server->setObject($provider); - } - } - else - { - if (is_array($this->generatorConfig) && isset($this->generatorConfig['bindingStyle']) - && $this->generatorConfig['bindingStyle']==='document') - { - $server->setClass(DocumentSoapObjectWrapper::className(),$provider); - } - else - { - $server->setClass(SoapObjectWrapper::className(),$provider); - } - } + if(method_exists($server,'setObject')) + { + if (is_array($this->generatorConfig) && isset($this->generatorConfig['bindingStyle']) + && $this->generatorConfig['bindingStyle']==='document') + { + $server->setObject(new DocumentSoapObjectWrapper($provider)); + } + else + { + $server->setObject($provider); + } + } + else + { + if (is_array($this->generatorConfig) && isset($this->generatorConfig['bindingStyle']) + && $this->generatorConfig['bindingStyle']==='document') + { + $server->setClass(DocumentSoapObjectWrapper::className(),$provider); + } + else + { + $server->setClass(SoapObjectWrapper::className(),$provider); + } + } - if($provider instanceof IWebServiceProvider) - { - if($provider->beforeWebMethod($this)) - { - $server->handle(); - $provider->afterWebMethod($this); - } - } - else - $server->handle(); - } - catch(\Exception $e) - { - if($e->getCode()!==self::SOAP_ERROR) // non-PHP error - { - // only log for non-PHP-error case because application's error handler already logs it - // php <5.2 doesn't support string conversion auto-magically - \Yii::error($e->__toString()); - } + if($provider instanceof IWebServiceProvider) + { + if($provider->beforeWebMethod($this)) + { + $server->handle(); + $provider->afterWebMethod($this); + } + } + else + $server->handle(); + } + catch(\Exception $e) + { + if($e->getCode()!==self::SOAP_ERROR) // non-PHP error + { + // only log for non-PHP-error case because application's error handler already logs it + // php <5.2 doesn't support string conversion auto-magically + \Yii::error($e->__toString()); + } - $message=$e->getMessage(); - if(YII_DEBUG) - $message.=' ('.$e->getFile().':'.$e->getLine().")\n".$e->getTraceAsString(); + $message=$e->getMessage(); + if(YII_DEBUG) + $message.=' ('.$e->getFile().':'.$e->getLine().")\n".$e->getTraceAsString(); - // We need to end application explicitly because of - // http://bugs.php.net/bug.php?id=49513 - \Yii::$app->state = Application::STATE_AFTER_REQUEST; - \Yii::$app->trigger(Application::EVENT_AFTER_REQUEST); - $reflect = new \ReflectionClass($e); - $server->fault($reflect->getShortName(), $message); - exit(1); - } - } + // We need to end application explicitly because of + // http://bugs.php.net/bug.php?id=49513 + \Yii::$app->state = Application::STATE_AFTER_REQUEST; + \Yii::$app->trigger(Application::EVENT_AFTER_REQUEST); + $reflect = new \ReflectionClass($e); + $server->fault($reflect->getShortName(), $message); + exit(1); + } + } - /** - * @return string the currently requested method name. Empty if no method is being requested. - */ - public function getMethodName() - { - if($this->_method===null) - { - if(isset($HTTP_RAW_POST_DATA)) - $request=$HTTP_RAW_POST_DATA; - else - $request=file_get_contents('php://input'); - if(preg_match('/<.*?:Body[^>]*>\s*<.*?:(\w+)/mi',$request,$matches)) - $this->_method=$matches[1]; - else - $this->_method=''; - } - return $this->_method; - } + /** + * @return string the currently requested method name. Empty if no method is being requested. + */ + public function getMethodName() + { + if($this->_method===null) + { + if(isset($HTTP_RAW_POST_DATA)) + $request=$HTTP_RAW_POST_DATA; + else + $request=file_get_contents('php://input'); + if(preg_match('/<.*?:Body[^>]*>\s*<.*?:(\w+)/mi',$request,$matches)) + $this->_method=$matches[1]; + else + $this->_method=''; + } + return $this->_method; + } - /** - * @return array options for creating SoapServer instance - * @see http://www.php.net/manual/en/soapserver.soapserver.php - */ - protected function getOptions() - { - $options=array(); - if($this->soapVersion==='1.1') - $options['soap_version']=SOAP_1_1; - elseif($this->soapVersion==='1.2') - $options['soap_version']=SOAP_1_2; - if($this->actor!==null) - $options['actor']=$this->actor; - $options['encoding']=$this->encoding; - foreach($this->classMap as $type=>$className) - { - \Yii::autoload($className); - if(is_int($type)) - $type=$className; - $options['classmap'][$type]=$className; - } - return $options; - } + /** + * @return array options for creating SoapServer instance + * @see http://www.php.net/manual/en/soapserver.soapserver.php + */ + protected function getOptions() + { + $options=array(); + if($this->soapVersion==='1.1') + $options['soap_version']=SOAP_1_1; + elseif($this->soapVersion==='1.2') + $options['soap_version']=SOAP_1_2; + if($this->actor!==null) + $options['actor']=$this->actor; + $options['encoding']=$this->encoding; + foreach($this->classMap as $type=>$className) + { + \Yii::autoload($className); + if(is_int($type)) + $type=$className; + $options['classmap'][$type]=$className; + } + return $options; + } } @@ -301,40 +301,40 @@ protected function getOptions() */ class SoapObjectWrapper { - /** - * @var object the service provider - */ - public $object=null; + /** + * @var object the service provider + */ + public $object=null; - /** - * Constructor. - * @param object $object the service provider - */ - public function __construct($object) - { - $this->object=$object; - } + /** + * Constructor. + * @param object $object the service provider + */ + public function __construct($object) + { + $this->object=$object; + } - /** - * PHP __call magic method. - * This method calls the service provider to execute the actual logic. - * @param string $name method name - * @param array $arguments method arguments - * @return mixed method return value - */ - public function __call($name,$arguments) - { - return call_user_func_array(array($this->object,$name),$arguments); - } - - /** - * Returns the fully qualified name of this class. - * @return string the fully qualified name of this class. - */ - public static function className() - { - return get_called_class(); - } + /** + * PHP __call magic method. + * This method calls the service provider to execute the actual logic. + * @param string $name method name + * @param array $arguments method arguments + * @return mixed method return value + */ + public function __call($name,$arguments) + { + return call_user_func_array(array($this->object,$name),$arguments); + } + + /** + * Returns the fully qualified name of this class. + * @return string the fully qualified name of this class. + */ + public static function className() + { + return get_called_class(); + } } /** @@ -346,47 +346,47 @@ public static function className() */ class DocumentSoapObjectWrapper extends Object { - /** - * @var object the service provider - */ - public $object=null; + /** + * @var object the service provider + */ + public $object=null; - /** - * Constructor. - * @param object $object the service provider - */ - public function __construct($object) - { - $this->object=$object; - } + /** + * Constructor. + * @param object $object the service provider + */ + public function __construct($object) + { + $this->object=$object; + } - /** - * PHP __call magic method. - * This method calls the service provider to execute the actual logic. - * @param string $name method name - * @param array $arguments method arguments - * @return mixed method return value - */ - public function __call($name,$arguments) - { - if (is_array($arguments) && isset($arguments[0])) - { - $result = call_user_func_array(array($this->object, $name), (array)$arguments[0]); - } - else - { - $result = call_user_func_array(array($this->object, $name), $arguments); - } - return $result === null ? $result : array($name . 'Result' => $result); - } - - /** - * Returns the fully qualified name of this class. - * @return string the fully qualified name of this class. - */ - public static function className() - { - return get_called_class(); - } + /** + * PHP __call magic method. + * This method calls the service provider to execute the actual logic. + * @param string $name method name + * @param array $arguments method arguments + * @return mixed method return value + */ + public function __call($name,$arguments) + { + if (is_array($arguments) && isset($arguments[0])) + { + $result = call_user_func_array(array($this->object, $name), (array)$arguments[0]); + } + else + { + $result = call_user_func_array(array($this->object, $name), $arguments); + } + return $result === null ? $result : array($name . 'Result' => $result); + } + + /** + * Returns the fully qualified name of this class. + * @return string the fully qualified name of this class. + */ + public static function className() + { + return get_called_class(); + } } diff --git a/WebServiceAction.php b/WebServiceAction.php index 0c0f89b..331dff5 100644 --- a/WebServiceAction.php +++ b/WebServiceAction.php @@ -32,107 +32,107 @@ */ class WebServiceAction extends \yii\base\Action { - /** - * @var mixed the Web service provider object or class name. - * If specified as a class name, it can be a path alias. - * Defaults to null, meaning the current controller is used as the service provider. - * If the provider implements the interface {@link IWebServiceProvider}, - * it will be able to intercept the remote method invocation and perform - * additional tasks (e.g. authentication, logging). - */ - public $provider; - /** - * @var string the URL for the Web service. Defaults to null, meaning - * the URL for this action is used to provide Web services. - * In this case, a GET parameter named {@link serviceVar} will be used to - * deteremine whether the current request is for WSDL or Web service. - */ - public $serviceUrl; - /** - * @var string the URL for WSDL. Defaults to null, meaning - * the URL for this action is used to serve WSDL document. - */ - public $wsdlUrl; - /** - * @var string the name of the GET parameter that differentiates a WSDL request - * from a Web service request. If this GET parameter exists, the request is considered - * as a Web service request; otherwise, it is a WSDL request. Defaults to 'ws'. - */ - public $serviceVar='ws'; - /** - * @var array a list of PHP classes that are declared as complex types in WSDL. - * This should be an array with WSDL types as keys and names of PHP classes as values. - * A PHP class can also be specified as a path alias. - * @see http://www.php.net/manual/en/soapclient.soapclient.php - */ - public $classMap; - /** - * @var array the initial property values for the {@link WebService} object. - * The array keys are property names of {@link WebService} and the array values - * are the corresponding property initial values. - */ - public $serviceOptions=array(); + /** + * @var mixed the Web service provider object or class name. + * If specified as a class name, it can be a path alias. + * Defaults to null, meaning the current controller is used as the service provider. + * If the provider implements the interface {@link IWebServiceProvider}, + * it will be able to intercept the remote method invocation and perform + * additional tasks (e.g. authentication, logging). + */ + public $provider; + /** + * @var string the URL for the Web service. Defaults to null, meaning + * the URL for this action is used to provide Web services. + * In this case, a GET parameter named {@link serviceVar} will be used to + * deteremine whether the current request is for WSDL or Web service. + */ + public $serviceUrl; + /** + * @var string the URL for WSDL. Defaults to null, meaning + * the URL for this action is used to serve WSDL document. + */ + public $wsdlUrl; + /** + * @var string the name of the GET parameter that differentiates a WSDL request + * from a Web service request. If this GET parameter exists, the request is considered + * as a Web service request; otherwise, it is a WSDL request. Defaults to 'ws'. + */ + public $serviceVar='ws'; + /** + * @var array a list of PHP classes that are declared as complex types in WSDL. + * This should be an array with WSDL types as keys and names of PHP classes as values. + * A PHP class can also be specified as a path alias. + * @see http://www.php.net/manual/en/soapclient.soapclient.php + */ + public $classMap; + /** + * @var array the initial property values for the {@link WebService} object. + * The array keys are property names of {@link WebService} and the array values + * are the corresponding property initial values. + */ + public $serviceOptions=array(); - /** - * @var WebService - */ - private $_service; + /** + * @var WebService + */ + private $_service; - public function init() - { - $this->controller->enableCsrfValidation = false; - } - + public function init() + { + $this->controller->enableCsrfValidation = false; + } + - /** - * Runs the action. - * If the GET parameter {@link serviceVar} exists, the action handle the remote method invocation. - * If not, the action will serve WSDL content; - */ - public function run() - { - $hostInfo=\Yii::$app->getRequest()->getHostInfo(); - $controller=$this->controller; - if(($serviceUrl=$this->serviceUrl)===null) - $serviceUrl=$hostInfo.Url::toRoute([$this->getUniqueId(),$this->serviceVar=>1]); - if(($wsdlUrl=$this->wsdlUrl)===null) - $wsdlUrl=$hostInfo.Url::toRoute([$this->getUniqueId()]); - if(($provider=$this->provider)===null) - $provider=$controller; + /** + * Runs the action. + * If the GET parameter {@link serviceVar} exists, the action handle the remote method invocation. + * If not, the action will serve WSDL content; + */ + public function run() + { + $hostInfo=\Yii::$app->getRequest()->getHostInfo(); + $controller=$this->controller; + if(($serviceUrl=$this->serviceUrl)===null) + $serviceUrl=$hostInfo.Url::toRoute([$this->getUniqueId(),$this->serviceVar=>1]); + if(($wsdlUrl=$this->wsdlUrl)===null) + $wsdlUrl=$hostInfo.Url::toRoute([$this->getUniqueId()]); + if(($provider=$this->provider)===null) + $provider=$controller; - $this->_service=$this->createWebService($provider,$wsdlUrl,$serviceUrl); + $this->_service=$this->createWebService($provider,$wsdlUrl,$serviceUrl); - if(is_array($this->classMap)) - $this->_service->classMap=$this->classMap; + if(is_array($this->classMap)) + $this->_service->classMap=$this->classMap; - foreach($this->serviceOptions as $name=>$value) - $this->_service->$name=$value; + foreach($this->serviceOptions as $name=>$value) + $this->_service->$name=$value; - if(isset($_GET[$this->serviceVar])) - $this->_service->run(); - else - return $this->_service->renderWsdl(); - } + if(isset($_GET[$this->serviceVar])) + $this->_service->run(); + else + return $this->_service->renderWsdl(); + } - /** - * Returns the Web service instance currently being used. - * @return WebService the Web service instance - */ - public function getService() - { - return $this->_service; - } + /** + * Returns the Web service instance currently being used. + * @return WebService the Web service instance + */ + public function getService() + { + return $this->_service; + } - /** - * Creates a {@link WebService} instance. - * You may override this method to customize the created instance. - * @param mixed $provider the web service provider class name or object - * @param string $wsdlUrl the URL for WSDL. - * @param string $serviceUrl the URL for the Web service. - * @return WebService the Web service instance - */ - protected function createWebService($provider,$wsdlUrl,$serviceUrl) - { - return new WebService($provider,$wsdlUrl,$serviceUrl); - } + /** + * Creates a {@link WebService} instance. + * You may override this method to customize the created instance. + * @param mixed $provider the web service provider class name or object + * @param string $wsdlUrl the URL for WSDL. + * @param string $serviceUrl the URL for the Web service. + * @return WebService the Web service instance + */ + protected function createWebService($provider,$wsdlUrl,$serviceUrl) + { + return new WebService($provider,$wsdlUrl,$serviceUrl); + } } \ No newline at end of file diff --git a/WsdlGenerator.php b/WsdlGenerator.php index 8938f96..f73e7ed 100644 --- a/WsdlGenerator.php +++ b/WsdlGenerator.php @@ -114,11 +114,11 @@ *
  * / *
  *   * @soap-wsdl 
- *   * @soap-wsdl 	
- *   * @soap-wsdl 	
- *   * @soap-wsdl 		
- *   * @soap-wsdl 		
- *   * @soap-wsdl 	
+ *   * @soap-wsdl     
+ *   * @soap-wsdl     
+ *   * @soap-wsdl         
+ *   * @soap-wsdl         
+ *   * @soap-wsdl     
  *   * @soap-wsdl 
  *   * /
  * class User {
@@ -152,735 +152,735 @@
  */
 class WsdlGenerator extends \yii\base\Component
 {
-	const STYLE_RPC = 'rpc';
-	const STYLE_DOCUMENT = 'document';
-	const USE_ENCODED = 'encoded';
-	const USE_LITERAL = 'literal';
-	/**
-	 * @var string 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.
-	 */
-	public $namespace;
-	/**
-	 * @var string the name of the generated WSDL.
-	 * If not set, it defaults to "urn:{$className}wsdl".
-	 */
-	public $serviceName;
-	/**
-	 * @var array
-	 * soap:body operation style options
-	 */
-	public $operationBodyStyle = array(
-		'use' => self::USE_ENCODED,
-		'encodingStyle' => 'http://schemas.xmlsoap.org/soap/encoding/',
-	);
-	/**
-	 * @var array
-	 * soap:operation style
-	 */
-	public $bindingStyle = self::STYLE_RPC;
-	/**
-	 * @var string
-	 * soap:operation transport
-	 */
-	public $bindingTransport = 'http://schemas.xmlsoap.org/soap/http';
-
-	protected static $typeMap=array(
-		'string'=>'xsd:string',
-		'str'=>'xsd:string',
-		'int'=>'xsd:int',
-		'integer'=>'xsd:integer',
-		'float'=>'xsd:float',
-		'double'=>'xsd:float',
-		'bool'=>'xsd:boolean',
-		'boolean'=>'xsd:boolean',
-		'date'=>'xsd:date',
-		'time'=>'xsd:time',
-		'datetime'=>'xsd:dateTime',
-		'array'=>'soap-enc:Array',
-		'object'=>'xsd:struct',
-		'mixed'=>'xsd:anyType',
-	);
-
-	/**
-	* @var array 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, ..
-	*/
-	protected $operations;
-
-	/**
-	* @var array 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 $types;
-
-	/**
-	* @var array
-	*/
-	protected $elements;
-
-	/**
-	* @var array Map of request and response types for all operations.
-	*/
-	protected $messages;
-
-	/**
-	 * Generates the WSDL for the given class.
-	 * @param string $className class name
-	 * @param string $serviceUrl Web service URL
-	 * @param string $encoding 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();
-		
-		$reflection=new \ReflectionClass($className);
-
-		if($this->serviceName===null)
-			$this->serviceName=$reflection->getShortName();
-
- 		if($this->namespace===null)
- 			$this->namespace='urn:'.str_replace('\\','/',$className).'wsdl';
-		
-		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;
-	}
-
-	/**
-	 * @param ReflectionMethod $method 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)),
-		);
-	}
-
-	/**
-	 * @param string $type PHP variable type
-	 */
-	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
-			\Yii::autoload($type);
-			$class=new \ReflectionClass($type);
-			
-			$type=$class->getShortName();
-
-			$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(str_replace('\\','/',$matches[1])),
-							trim($matches[3]),
-							$attributes['nillable'],
-							$attributes['minOccurs'],
-							$attributes['maxOccurs'],
-							$example
-						); // name => type, doc, nillable, minOccurs, maxOccurs, example
-					}
-				}
-			}
-			return 'tns:'.$type;
-		}
-	}
-
-	/**
-	* Parse attributes nillable, minOccurs, maxOccurs
-	* @param string $comment Extracted PHPDoc comment
-	*/
-	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
-		);
-	}
-
-	/**
-	* Import custom XML source node into WSDL document under specified target node
-	* @param DOMDocument $dom XML WSDL document being generated
-	* @param DOMElement $target XML node, to which will be appended $source node
-	* @param DOMNode $source 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);
-	}
-
-	/**
-	 * @param string $serviceUrl Web service URL
-	 * @param string $encoding encoding of the WSDL. Defaults to 'UTF-8'.
-	 */
-	protected function buildDOM($serviceUrl,$encoding)
-	{
-		$xml="
+    const STYLE_RPC = 'rpc';
+    const STYLE_DOCUMENT = 'document';
+    const USE_ENCODED = 'encoded';
+    const USE_LITERAL = 'literal';
+    /**
+     * @var string 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.
+     */
+    public $namespace;
+    /**
+     * @var string the name of the generated WSDL.
+     * If not set, it defaults to "urn:{$className}wsdl".
+     */
+    public $serviceName;
+    /**
+     * @var array
+     * soap:body operation style options
+     */
+    public $operationBodyStyle = array(
+        'use' => self::USE_ENCODED,
+        'encodingStyle' => 'http://schemas.xmlsoap.org/soap/encoding/',
+    );
+    /**
+     * @var array
+     * soap:operation style
+     */
+    public $bindingStyle = self::STYLE_RPC;
+    /**
+     * @var string
+     * soap:operation transport
+     */
+    public $bindingTransport = 'http://schemas.xmlsoap.org/soap/http';
+
+    protected static $typeMap=array(
+        'string'=>'xsd:string',
+        'str'=>'xsd:string',
+        'int'=>'xsd:int',
+        'integer'=>'xsd:integer',
+        'float'=>'xsd:float',
+        'double'=>'xsd:float',
+        'bool'=>'xsd:boolean',
+        'boolean'=>'xsd:boolean',
+        'date'=>'xsd:date',
+        'time'=>'xsd:time',
+        'datetime'=>'xsd:dateTime',
+        'array'=>'soap-enc:Array',
+        'object'=>'xsd:struct',
+        'mixed'=>'xsd:anyType',
+    );
+
+    /**
+    * @var array 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, ..
+    */
+    protected $operations;
+
+    /**
+    * @var array 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 $types;
+
+    /**
+    * @var array
+    */
+    protected $elements;
+
+    /**
+    * @var array Map of request and response types for all operations.
+    */
+    protected $messages;
+
+    /**
+     * Generates the WSDL for the given class.
+     * @param string $className class name
+     * @param string $serviceUrl Web service URL
+     * @param string $encoding 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();
+        
+        $reflection=new \ReflectionClass($className);
+
+        if($this->serviceName===null)
+            $this->serviceName=$reflection->getShortName();
+
+         if($this->namespace===null)
+             $this->namespace='urn:'.str_replace('\\','/',$className).'wsdl';
+        
+        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;
+    }
+
+    /**
+     * @param ReflectionMethod $method 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)),
+        );
+    }
+
+    /**
+     * @param string $type PHP variable type
+     */
+    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
+            \Yii::autoload($type);
+            $class=new \ReflectionClass($type);
+            
+            $type=$class->getShortName();
+
+            $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(str_replace('\\','/',$matches[1])),
+                            trim($matches[3]),
+                            $attributes['nillable'],
+                            $attributes['minOccurs'],
+                            $attributes['maxOccurs'],
+                            $example
+                        ); // name => type, doc, nillable, minOccurs, maxOccurs, example
+                    }
+                }
+            }
+            return 'tns:'.$type;
+        }
+    }
+
+    /**
+    * Parse attributes nillable, minOccurs, maxOccurs
+    * @param string $comment Extracted PHPDoc comment
+    */
+    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
+        );
+    }
+
+    /**
+    * Import custom XML source node into WSDL document under specified target node
+    * @param DOMDocument $dom XML WSDL document being generated
+    * @param DOMElement $target XML node, to which will be appended $source node
+    * @param DOMNode $source 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);
+    }
+
+    /**
+     * @param string $serviceUrl Web service URL
+     * @param string $encoding encoding of the WSDL. Defaults to 'UTF-8'.
+     */
+    protected function buildDOM($serviceUrl,$encoding)
+    {
+        $xml="
 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/\">";
-
-		$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;
-	}
-
-	/**
-	 * @param DOMDocument $dom 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('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))
-			{
-				$pathInfo = pathinfo(str_replace('\\', '/', $phpType));
-				
-				$complexType->setAttribute('name', $pathInfo['basename']);
-				
-				//$complexType->setAttribute('name',$phpType);
-				if($xmlType['custom_wsdl']!==false)
-				{
-					$custom_dom=new \DOMDocument();
-					$custom_dom->loadXML(''.$xmlType['custom_wsdl'].'');
-					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);
-	}
-
-	/**
-	 * @param DOMDocument $dom 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);
-		}
-	}
-
-	/**
-	 * @param DOMDocument $dom 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']));
-	}
-
-	/**
-	 * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
-	 * @param string $name method name
-	 * @param string $doc doc
-	 */
-	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;
-	}
-
-	/**
-	 * @param DOMDocument $dom 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']));
-	}
-
-	/**
-	 * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
-	 * @param string $name method name
-	 * @param array $headers array like array('input'=>array(MESSAGE,PART),'output=>array(MESSAGE,PART))
-	 */
-	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;
-	}
-
-	/**
-	 * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
-	 * @param string $serviceUrl 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);
-	}
-
-	/**
-	* 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:
-	* 
-	*
-	* @param bool $return If true, generated HTML output will be returned rather than directly sent to output buffer
-	*/
-	public function buildHtmlDocs($return=false)
-	{
-		$html='';
-		$html.='';
-		$html.='';
-		$html.='';
-		$html.='

WSDL documentation for service '.$this->serviceName.'

'; - $html.='

Generated on '.date('d.m.Y H:i:s').'

'; - $html.=''; - $html.='
'; - - 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

Object: {$object}

"; - $html.=''; - $html.=''; - $c=0; - foreach($params as $param=>$prop){ - ++$c; - $html.="\n" - ."\n\t" - ."\n\t" - ."\n\t" - ."\n\t" - ."\n\t" - ."\n\t" - ."\n\t" - ."\n\t" - ."\n"; - } - $html.="\n
#AttributeTypeNillMinMaxDescriptionExample
{$c}{$param}".(str_replace('xsd:','',$prop[0]))."".$prop[2]."".($prop[3]==null ? ' ' : $prop[3])."".($prop[4]==null ? ' ' : $prop[4])."{$prop[1]}".(trim($prop[5])=='' ? ' ' : $prop[5])."

"; - } - } - else - $html.='No complex data type found!'; - - $html.='
'; - - if($return) - return $html; - - echo $html; - \Yii::$app->end(); // end the app to avoid conflict with text/xml header - } + $html.=''; + $html.='

WSDL documentation for service '.$this->serviceName.'

'; + $html.='

Generated on '.date('d.m.Y H:i:s').'

'; + $html.=''; + $html.='
'; + + 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

Object: {$object}

"; + $html.=''; + $html.=''; + $c=0; + foreach($params as $param=>$prop){ + ++$c; + $html.="\n" + ."\n\t" + ."\n\t" + ."\n\t" + ."\n\t" + ."\n\t" + ."\n\t" + ."\n\t" + ."\n\t" + ."\n"; + } + $html.="\n
#AttributeTypeNillMinMaxDescriptionExample
{$c}{$param}".(str_replace('xsd:','',$prop[0]))."".$prop[2]."".($prop[3]==null ? ' ' : $prop[3])."".($prop[4]==null ? ' ' : $prop[4])."{$prop[1]}".(trim($prop[5])=='' ? ' ' : $prop[5])."

"; + } + } + else + $html.='No complex data type found!'; + + $html.='
'; + + if($return) + return $html; + + echo $html; + \Yii::$app->end(); // end the app to avoid conflict with text/xml header + } } diff --git a/composer.json b/composer.json index 95fb9fb..aa00131 100644 --- a/composer.json +++ b/composer.json @@ -8,13 +8,12 @@ "authors": [ { "name": "Andrey Borodulin", - "email": "borodulin@gmail.com", - "homepage": "https://github.com/borodulin/yii2-services" + "email": "borodulin@gmail.com" } ], "extra": { "branch-alias": { - "dev-master": "1.1.1.x-dev" + "dev-master": "1.2.1.x-dev" } }, "require": {