5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the BSD License.
8 * Copyright(c) 2005 by Marcus Nyeholt. All rights reserved.
10 * To contact the author write to {@link mailto:tanus@users.sourceforge.net Marcus Nyeholt}
11 * This file is part of the PRADO framework from {@link http://www.xisc.com}
13 * @author Marcus Nyeholt <tanus@users.sourceforge.net>
14 * @package System.Web.Services.SOAP
17 require_once(dirname(__FILE__).'/Wsdl.php');
18 require_once(dirname(__FILE__).'/WsdlMessage.php');
19 require_once(dirname(__FILE__).'/WsdlOperation.php');
22 * Generator for the wsdl.
23 * Special thanks to Cristian Losada for implementing the Complex Types section of the WSDL.
24 * @author Marcus Nyeholt <tanus@users.sourceforge.net>
25 * @author Cristian Losada <cristian@teaxul.com>
34 private static $instance;
37 * The name of this service (the classname)
40 private $serviceName = '';
43 * The complex types to use in the wsdl
46 private $types = array();
49 * The operations available in this wsdl
58 private $wsdlDocument;
61 * The actual wsdl string
67 * The singleton instance for the generator
69 public static function getInstance()
71 if (is_null(self::$instance)) {
72 self::$instance = new WsdlGenerator();
74 return self::$instance;
78 * Get the Wsdl generated
79 * @return string The Wsdl for this wsdl
81 public function getWsdl()
87 * Generates WSDL for a passed in class, and saves it in the current object. The
88 * WSDL can then be retrieved by calling
89 * @param string $className The name of the class to generate for
90 * @param string $serviceUri The URI of the service that handles this WSDL
91 * @param string $encoding character encoding.
94 public function generateWsdl($className, $serviceUri='',$encoding='')
96 $this->wsdlDocument = new Wsdl($className, $serviceUri, $encoding);
98 $classReflect = new ReflectionClass($className);
99 $methods = $classReflect->getMethods();
101 foreach ($methods as $method) {
102 // Only process public methods
103 if ($method->isPublic()) {
104 $this->processMethod($method);
108 foreach($this->types as $type => $elements) {
109 $this->wsdlDocument->addComplexType($type, $elements);
112 $this->wsdl = $this->wsdlDocument->getWsdl();
116 * Static method that generates and outputs the generated wsdl
117 * @param string $className The name of the class to export
118 * @param string $serviceUri The URI of the service that handles this WSDL
119 * @param string $encoding character encoding.
121 public static function generate($className, $serviceUri='', $encoding='')
123 $generator = WsdlGenerator::getInstance();
124 $generator->generateWsdl($className, $serviceUri,$encoding);
125 //header('Content-type: text/xml');
126 return $generator->getWsdl();
132 * Process a method found in the passed in class.
133 * @param ReflectionMethod $method The method to process
135 protected function processMethod(ReflectionMethod $method)
137 $comment = $method->getDocComment();
138 if (strpos($comment, '@soapmethod') === false) {
141 $comment = preg_replace("/(^[\\s]*\\/\\*\\*)
145 |(^[\\t]*)/ixm", "", $comment);
147 $comment = str_replace("\r", "", $comment);
148 $comment = preg_replace("/([\\t])+/", "\t", $comment);
149 $commentLines = explode("\n", $comment);
157 foreach ($commentLines as $line) {
158 if ($line == '') continue;
159 if ($line{0} == '@') {
161 if (preg_match('/^@param\s+([\w\[\]()]+)\s+\$([\w()]+)\s*(.*)/i', $line, $match)) {
163 $param['type'] = $this->convertType($match[1]);
164 $param['name'] = $match[2];
165 $param['desc'] = $match[3];
168 else if (preg_match('/^@return\s+([\w\[\]()]+)\s*(.*)/i', $line, $match)) {
170 $return['type'] = $this->convertType($match[1]);
171 $return['desc'] = $match[2];
172 $return['name'] = 'return';
177 $methodDoc .= trim($line);
179 else if (!$gotParams) {
180 $params[count($params)-1]['desc'] .= trim($line);
183 if ($line == '*/') continue;
184 $return['desc'] .= trim($line);
189 $methodName = $method->getName();
190 $operation = new WsdlOperation($methodName, $methodDoc);
192 $operation->setInputMessage(new WsdlMessage($methodName.'Request', $params));
193 $operation->setOutputMessage(new WsdlMessage($methodName.'Response', array($return)));
195 $this->wsdlDocument->addOperation($operation);
200 * Converts from a PHP type into a WSDL type. This is borrowed from
201 * Cerebral Cortex (let me know and I'll remove asap).
203 * TODO: date and dateTime
204 * @param string $type The php type to convert
205 * @return string The XSD type.
207 private function convertType($type)
224 return 'xsd:boolean';
233 return 'xsd:dateTime';
236 return 'soap-enc:Array';
242 return 'xsd:anyType';
247 if(strpos($type, '[]')) // if it is an array
249 $className = substr($type, 0, strlen($type) - 2);
250 $type = $className . 'Array';
251 $this->types[$type] = '';
252 $this->convertType($className);
256 if(!isset($this->types[$type]))
257 $this->extractClassProperties($type);
259 return 'tns:' . $type;
264 * Extract the type and the name of all properties of the $className class and saves it in the $types array
265 * This method extract properties from PHPDoc formatted comments for variables. Unfortunately the reflectionproperty
266 * class doesn't have a getDocComment method to extract comments about it, so we have to extract the information
267 * about the variables manually. Thanks heaps to Cristian Losada for implementing this.
268 * @param string $className The name of the class
270 private function extractClassProperties($className)
273 * modified by Qiang Xue, Jan. 2, 2007
274 * Using Reflection's DocComment to obtain property definitions
275 * DocComment is available since PHP 5.1
277 $reflection = new ReflectionClass($className);
278 $properties = $reflection->getProperties();
279 foreach($properties as $property)
281 $comment = $property->getDocComment();
282 if(strpos($comment, '@soapproperty') !== false)
284 if(preg_match('/@var\s+([\w\.]+(\[\s*\])?)\s*?\$(.*)$/mi',$comment,$matches))
286 // support nillable, minOccurs, maxOccurs attributes
287 $nillable=$minOccurs=$maxOccurs=false;
288 if(preg_match('/{(.+)}/',$matches[3],$attr))
290 $matches[3]=str_replace($attr[0],'',$matches[3]);
291 if(preg_match_all('/((\w+)\s*=\s*(\w+))/mi',$attr[1],$attr))
293 foreach($attr[2] as $id=>$prop)
295 if(strcasecmp($prop,'nillable')===0)
296 $nillable=$attr[3][$id] ? 'true' : 'false';
297 elseif(strcasecmp($prop,'minOccurs')===0)
298 $minOccurs=(int)$attr[3][$id];
299 elseif(strcasecmp($prop,'maxOccurs')===0)
300 $maxOccurs=(int)$attr[3][$id];
306 $param['type'] = $this->convertType($matches[1]);
307 $param['name'] = trim($matches[3]);
308 $param['nil'] = $nillable;
309 $param['minOc'] = $minOccurs;
310 $param['maxOc'] = $maxOccurs;
311 $this->types[$className][] = $param;