PageRenderTime 44ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/a10/lib/yii-1.1.10/web/services/CWsdlGenerator.php

http://chenjin.googlecode.com/
PHP | 419 lines | 254 code | 35 blank | 130 comment | 26 complexity | ac9b9dd7b15f0d8a5654877a7b06a1b0 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * CWsdlGenerator class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CWsdlGenerator generates the WSDL for a given service class.
  12. *
  13. * The WSDL generation is based on the doc comments found in the service class file.
  14. * In particular, it recognizes the '@soap' tag in the comment and extracts
  15. * API method and type definitions.
  16. *
  17. * In a service class, a remote invokable method must be a public method with a doc
  18. * comment block containing the '@soap' tag. In the doc comment, the type and name
  19. * of every input parameter and the type of the return value should be declared using
  20. * the standard phpdoc format.
  21. *
  22. * CWsdlGenerator recognizes the following primitive types (case-sensitive) in
  23. * the parameter and return type declarations:
  24. * <ul>
  25. * <li>str/string: maps to xsd:string;</li>
  26. * <li>int/integer: maps to xsd:int;</li>
  27. * <li>float/double: maps to xsd:float;</li>
  28. * <li>bool/boolean: maps to xsd:boolean;</li>
  29. * <li>date: maps to xsd:date;</li>
  30. * <li>time: maps to xsd:time;</li>
  31. * <li>datetime: maps to xsd:dateTime;</li>
  32. * <li>array: maps to xsd:string;</li>
  33. * <li>object: maps to xsd:struct;</li>
  34. * <li>mixed: maps to xsd:anyType.</li>
  35. * </ul>
  36. *
  37. * If a type is not a primitive type, it is considered as a class type, and
  38. * CWsdlGenerator will look for its property declarations. Only public properties
  39. * are considered, and they each must be associated with a doc comment block containg
  40. * the '@soap' tag. The doc comment block should declare the type of the property.
  41. *
  42. * CWsdlGenerator recognizes the array type with the following format:
  43. * <pre>
  44. * typeName[]: maps to tns:typeNameArray
  45. * </pre>
  46. *
  47. * The following is an example declaring a remote invokable method:
  48. * <pre>
  49. * / **
  50. * * A foo method.
  51. * * @param string name of something
  52. * * @param string value of something
  53. * * @return string[] some array
  54. * * @soap
  55. * * /
  56. * public function foo($name,$value) {...}
  57. * </pre>
  58. *
  59. * And the following is an example declaring a class with remote accessible properties:
  60. * <pre>
  61. * class Foo {
  62. * / **
  63. * * @var string name of foo
  64. * * @soap
  65. * * /
  66. * public $name;
  67. * / **
  68. * * @var Member[] members of foo
  69. * * @soap
  70. * * /
  71. * public $members;
  72. * }
  73. * </pre>
  74. * In the above, the 'members' property is an array of 'Member' objects. Since 'Member' is not
  75. * a primitive type, CWsdlGenerator will look further to find the definition of 'Member'.
  76. *
  77. * @author Qiang Xue <qiang.xue@gmail.com>
  78. * @version $Id: CWsdlGenerator.php 242 2012-03-29 15:18:01Z mole1230 $
  79. * @package system.web.services
  80. * @since 1.0
  81. */
  82. class CWsdlGenerator extends CComponent
  83. {
  84. /**
  85. * @var string the namespace to be used in the generated WSDL.
  86. * If not set, it defaults to the name of the class that WSDL is generated upon.
  87. */
  88. public $namespace;
  89. /**
  90. * @var string the name of the generated WSDL.
  91. * If not set, it defaults to "urn:{$className}wsdl".
  92. */
  93. public $serviceName;
  94. private $_operations;
  95. private $_types;
  96. private $_messages;
  97. /**
  98. * Generates the WSDL for the given class.
  99. * @param string $className class name
  100. * @param string $serviceUrl Web service URL
  101. * @param string $encoding encoding of the WSDL. Defaults to 'UTF-8'.
  102. * @return string the generated WSDL
  103. */
  104. public function generateWsdl($className, $serviceUrl, $encoding='UTF-8')
  105. {
  106. $this->_operations=array();
  107. $this->_types=array();
  108. $this->_messages=array();
  109. if($this->serviceName===null)
  110. $this->serviceName=$className;
  111. if($this->namespace===null)
  112. $this->namespace="urn:{$className}wsdl";
  113. $reflection=new ReflectionClass($className);
  114. foreach($reflection->getMethods() as $method)
  115. {
  116. if($method->isPublic())
  117. $this->processMethod($method);
  118. }
  119. return $this->buildDOM($serviceUrl,$encoding)->saveXML();
  120. }
  121. /*
  122. * @param ReflectionMethod $method method
  123. */
  124. private function processMethod($method)
  125. {
  126. $comment=$method->getDocComment();
  127. if(strpos($comment,'@soap')===false)
  128. return;
  129. $methodName=$method->getName();
  130. $comment=preg_replace('/^\s*\**(\s*?$|\s*)/m','',$comment);
  131. $params=$method->getParameters();
  132. $message=array();
  133. $n=preg_match_all('/^@param\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches);
  134. if($n>count($params))
  135. $n=count($params);
  136. for($i=0;$i<$n;++$i)
  137. $message[$params[$i]->getName()]=array($this->processType($matches[1][$i]), trim($matches[3][$i])); // name => type, doc
  138. $this->_messages[$methodName.'Request']=$message;
  139. if(preg_match('/^@return\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches))
  140. $return=array($this->processType($matches[1]),trim($matches[2])); // type, doc
  141. else
  142. $return=null;
  143. $this->_messages[$methodName.'Response']=array('return'=>$return);
  144. if(preg_match('/^\/\*+\s*([^@]*?)\n@/s',$comment,$matches))
  145. $doc=trim($matches[1]);
  146. else
  147. $doc='';
  148. $this->_operations[$methodName]=$doc;
  149. }
  150. /*
  151. * @param string $type PHP variable type
  152. */
  153. private function processType($type)
  154. {
  155. static $typeMap=array(
  156. 'string'=>'xsd:string',
  157. 'str'=>'xsd:string',
  158. 'int'=>'xsd:int',
  159. 'integer'=>'xsd:integer',
  160. 'float'=>'xsd:float',
  161. 'double'=>'xsd:float',
  162. 'bool'=>'xsd:boolean',
  163. 'boolean'=>'xsd:boolean',
  164. 'date'=>'xsd:date',
  165. 'time'=>'xsd:time',
  166. 'datetime'=>'xsd:dateTime',
  167. 'array'=>'soap-enc:Array',
  168. 'object'=>'xsd:struct',
  169. 'mixed'=>'xsd:anyType',
  170. );
  171. if(isset($typeMap[$type]))
  172. return $typeMap[$type];
  173. else if(isset($this->_types[$type]))
  174. return is_array($this->_types[$type]) ? 'tns:'.$type : $this->_types[$type];
  175. else if(($pos=strpos($type,'[]'))!==false) // if it is an array
  176. {
  177. $type=substr($type,0,$pos);
  178. if(isset($typeMap[$type]))
  179. $this->_types[$type.'[]']='xsd:'.$type.'Array';
  180. else
  181. {
  182. $this->_types[$type.'[]']='tns:'.$type.'Array';
  183. $this->processType($type);
  184. }
  185. return $this->_types[$type.'[]'];
  186. }
  187. else // class type
  188. {
  189. $type=Yii::import($type,true);
  190. $this->_types[$type]=array();
  191. $class=new ReflectionClass($type);
  192. foreach($class->getProperties() as $property)
  193. {
  194. $comment=$property->getDocComment();
  195. if($property->isPublic() && strpos($comment,'@soap')!==false)
  196. {
  197. if(preg_match('/@var\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/mi',$comment,$matches))
  198. $this->_types[$type][$property->getName()]=array($this->processType($matches[1]),trim($matches[3])); // name => type, doc
  199. }
  200. }
  201. return 'tns:'.$type;
  202. }
  203. }
  204. /*
  205. * @param string $serviceUrl Web service URL
  206. * @param string $encoding encoding of the WSDL. Defaults to 'UTF-8'.
  207. */
  208. private function buildDOM($serviceUrl,$encoding)
  209. {
  210. $xml="<?xml version=\"1.0\" encoding=\"$encoding\"?>
  211. <definitions name=\"{$this->serviceName}\" targetNamespace=\"{$this->namespace}\"
  212. xmlns=\"http://schemas.xmlsoap.org/wsdl/\"
  213. xmlns:tns=\"{$this->namespace}\"
  214. xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\"
  215. xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
  216. xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\"
  217. xmlns:soap-enc=\"http://schemas.xmlsoap.org/soap/encoding/\"></definitions>";
  218. $dom=new DOMDocument();
  219. $dom->loadXml($xml);
  220. $this->addTypes($dom);
  221. $this->addMessages($dom);
  222. $this->addPortTypes($dom);
  223. $this->addBindings($dom);
  224. $this->addService($dom,$serviceUrl);
  225. return $dom;
  226. }
  227. /*
  228. * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
  229. */
  230. private function addTypes($dom)
  231. {
  232. if($this->_types===array())
  233. return;
  234. $types=$dom->createElement('wsdl:types');
  235. $schema=$dom->createElement('xsd:schema');
  236. $schema->setAttribute('targetNamespace',$this->namespace);
  237. foreach($this->_types as $phpType=>$xmlType)
  238. {
  239. if(is_string($xmlType) && strrpos($xmlType,'Array')!==strlen($xmlType)-5)
  240. continue; // simple type
  241. $complexType=$dom->createElement('xsd:complexType');
  242. if(is_string($xmlType))
  243. {
  244. if(($pos=strpos($xmlType,'tns:'))!==false)
  245. $complexType->setAttribute('name',substr($xmlType,4));
  246. else
  247. $complexType->setAttribute('name',$xmlType);
  248. $complexContent=$dom->createElement('xsd:complexContent');
  249. $restriction=$dom->createElement('xsd:restriction');
  250. $restriction->setAttribute('base','soap-enc:Array');
  251. $attribute=$dom->createElement('xsd:attribute');
  252. $attribute->setAttribute('ref','soap-enc:arrayType');
  253. $attribute->setAttribute('wsdl:arrayType',substr($xmlType,0,strlen($xmlType)-5).'[]');
  254. $restriction->appendChild($attribute);
  255. $complexContent->appendChild($restriction);
  256. $complexType->appendChild($complexContent);
  257. }
  258. else if(is_array($xmlType))
  259. {
  260. $complexType->setAttribute('name',$phpType);
  261. $all=$dom->createElement('xsd:all');
  262. foreach($xmlType as $name=>$type)
  263. {
  264. $element=$dom->createElement('xsd:element');
  265. $element->setAttribute('name',$name);
  266. $element->setAttribute('type',$type[0]);
  267. $all->appendChild($element);
  268. }
  269. $complexType->appendChild($all);
  270. }
  271. $schema->appendChild($complexType);
  272. $types->appendChild($schema);
  273. }
  274. $dom->documentElement->appendChild($types);
  275. }
  276. /*
  277. * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
  278. */
  279. private function addMessages($dom)
  280. {
  281. foreach($this->_messages as $name=>$message)
  282. {
  283. $element=$dom->createElement('wsdl:message');
  284. $element->setAttribute('name',$name);
  285. foreach($this->_messages[$name] as $partName=>$part)
  286. {
  287. if(is_array($part))
  288. {
  289. $partElement=$dom->createElement('wsdl:part');
  290. $partElement->setAttribute('name',$partName);
  291. $partElement->setAttribute('type',$part[0]);
  292. $element->appendChild($partElement);
  293. }
  294. }
  295. $dom->documentElement->appendChild($element);
  296. }
  297. }
  298. /*
  299. * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
  300. */
  301. private function addPortTypes($dom)
  302. {
  303. $portType=$dom->createElement('wsdl:portType');
  304. $portType->setAttribute('name',$this->serviceName.'PortType');
  305. $dom->documentElement->appendChild($portType);
  306. foreach($this->_operations as $name=>$doc)
  307. $portType->appendChild($this->createPortElement($dom,$name,$doc));
  308. }
  309. /*
  310. * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
  311. * @param string $name method name
  312. * @param string $doc doc
  313. */
  314. private function createPortElement($dom,$name,$doc)
  315. {
  316. $operation=$dom->createElement('wsdl:operation');
  317. $operation->setAttribute('name',$name);
  318. $input = $dom->createElement('wsdl:input');
  319. $input->setAttribute('message', 'tns:'.$name.'Request');
  320. $output = $dom->createElement('wsdl:output');
  321. $output->setAttribute('message', 'tns:'.$name.'Response');
  322. $operation->appendChild($dom->createElement('wsdl:documentation',$doc));
  323. $operation->appendChild($input);
  324. $operation->appendChild($output);
  325. return $operation;
  326. }
  327. /*
  328. * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
  329. */
  330. private function addBindings($dom)
  331. {
  332. $binding=$dom->createElement('wsdl:binding');
  333. $binding->setAttribute('name',$this->serviceName.'Binding');
  334. $binding->setAttribute('type','tns:'.$this->serviceName.'PortType');
  335. $soapBinding=$dom->createElement('soap:binding');
  336. $soapBinding->setAttribute('style','rpc');
  337. $soapBinding->setAttribute('transport','http://schemas.xmlsoap.org/soap/http');
  338. $binding->appendChild($soapBinding);
  339. $dom->documentElement->appendChild($binding);
  340. foreach($this->_operations as $name=>$doc)
  341. $binding->appendChild($this->createOperationElement($dom,$name));
  342. }
  343. /*
  344. * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
  345. * @param string $name method name
  346. */
  347. private function createOperationElement($dom,$name)
  348. {
  349. $operation=$dom->createElement('wsdl:operation');
  350. $operation->setAttribute('name', $name);
  351. $soapOperation = $dom->createElement('soap:operation');
  352. $soapOperation->setAttribute('soapAction', $this->namespace.'#'.$name);
  353. $soapOperation->setAttribute('style','rpc');
  354. $input = $dom->createElement('wsdl:input');
  355. $output = $dom->createElement('wsdl:output');
  356. $soapBody = $dom->createElement('soap:body');
  357. $soapBody->setAttribute('use', 'encoded');
  358. $soapBody->setAttribute('namespace', $this->namespace);
  359. $soapBody->setAttribute('encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/');
  360. $input->appendChild($soapBody);
  361. $output->appendChild(clone $soapBody);
  362. $operation->appendChild($soapOperation);
  363. $operation->appendChild($input);
  364. $operation->appendChild($output);
  365. return $operation;
  366. }
  367. /*
  368. * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree
  369. * @param string $serviceUrl Web service URL
  370. */
  371. private function addService($dom,$serviceUrl)
  372. {
  373. $service=$dom->createElement('wsdl:service');
  374. $service->setAttribute('name', $this->serviceName.'Service');
  375. $port=$dom->createElement('wsdl:port');
  376. $port->setAttribute('name', $this->serviceName.'Port');
  377. $port->setAttribute('binding', 'tns:'.$this->serviceName.'Binding');
  378. $soapAddress=$dom->createElement('soap:address');
  379. $soapAddress->setAttribute('location',$serviceUrl);
  380. $port->appendChild($soapAddress);
  381. $service->appendChild($port);
  382. $dom->documentElement->appendChild($service);
  383. }
  384. }