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 * @version $Id: WsdlGenerator.php 3314 2013-08-20 10:00:47Z ctrlaltca $
15 * @package System.Web.Services.SOAP
18 require_once(dirname(__FILE__).'/Wsdl.php');
19 require_once(dirname(__FILE__).'/WsdlMessage.php');
20 require_once(dirname(__FILE__).'/WsdlOperation.php');
23 * Generator for the wsdl.
24 * Special thanks to Cristian Losada for implementing the Complex Types section of the WSDL.
25 * @author Marcus Nyeholt <tanus@users.sourceforge.net>
26 * @author Cristian Losada <cristian@teaxul.com>
35 private static $instance;
38 * The name of this service (the classname)
41 private $serviceName = '';
44 * The complex types to use in the wsdl
47 private $types = array();
50 * The operations available in this wsdl
59 private $wsdlDocument;
62 * The actual wsdl string
68 * The singleton instance for the generator
70 public static function getInstance()
72 if (is_null(self::$instance)) {
73 self::$instance = new WsdlGenerator();
75 return self::$instance;
79 * Get the Wsdl generated
80 * @return string The Wsdl for this wsdl
82 public function getWsdl()
88 * Generates WSDL for a passed in class, and saves it in the current object. The
89 * WSDL can then be retrieved by calling
90 * @param string $className The name of the class to generate for
91 * @param string $serviceUri The URI of the service that handles this WSDL
92 * @param string $encoding character encoding.
95 public function generateWsdl($className, $serviceUri='',$encoding='')
97 $this->wsdlDocument = new Wsdl($className, $serviceUri, $encoding);
99 $classReflect = new ReflectionClass($className);
100 $methods = $classReflect->getMethods();
102 foreach ($methods as $method) {
103 // Only process public methods
104 if ($method->isPublic()) {
105 $this->processMethod($method);
109 foreach($this->types as $type => $elements) {
110 $this->wsdlDocument->addComplexType($type, $elements);
113 $this->wsdl = $this->wsdlDocument->getWsdl();
117 * Static method that generates and outputs the generated wsdl
118 * @param string $className The name of the class to export
119 * @param string $serviceUri The URI of the service that handles this WSDL
120 * @param string $encoding character encoding.
122 public static function generate($className, $serviceUri='', $encoding='')
124 $generator = WsdlGenerator::getInstance();
125 $generator->generateWsdl($className, $serviceUri,$encoding);
126 //header('Content-type: text/xml');
127 return $generator->getWsdl();
133 * Process a method found in the passed in class.
134 * @param ReflectionMethod $method The method to process
136 protected function processMethod(ReflectionMethod $method)
138 $comment = $method->getDocComment();
139 if (strpos($comment, '@soapmethod') === false) {
142 $comment = preg_replace("/(^[\\s]*\\/\\*\\*)
146 |(^[\\t]*)/ixm", "", $comment);
148 $comment = str_replace("\r", "", $comment);
149 $comment = preg_replace("/([\\t])+/", "\t", $comment);
150 $commentLines = explode("\n", $comment);
158 foreach ($commentLines as $line) {
159 if ($line == '') continue;
160 if ($line{0} == '@') {
162 if (preg_match('/^@param\s+([\w\[\]()]+)\s+\$([\w()]+)\s*(.*)/i', $line, $match)) {
164 $param['type'] = $this->convertType($match[1]);
165 $param['name'] = $match[2];
166 $param['desc'] = $match[3];
169 else if (preg_match('/^@return\s+([\w\[\]()]+)\s*(.*)/i', $line, $match)) {
171 $return['type'] = $this->convertType($match[1]);
172 $return['desc'] = $match[2];
173 $return['name'] = 'return';
178 $methodDoc .= trim($line);
180 else if (!$gotParams) {
181 $params[count($params)-1]['desc'] .= trim($line);
184 if ($line == '*/') continue;
185 $return['desc'] .= trim($line);
190 $methodName = $method->getName();
191 $operation = new WsdlOperation($methodName, $methodDoc);
193 $operation->setInputMessage(new WsdlMessage($methodName.'Request', $params));
194 $operation->setOutputMessage(new WsdlMessage($methodName.'Response', array($return)));
196 $this->wsdlDocument->addOperation($operation);
201 * Converts from a PHP type into a WSDL type. This is borrowed from
202 * Cerebral Cortex (let me know and I'll remove asap).
204 * TODO: date and dateTime
205 * @param string $type The php type to convert
206 * @return string The XSD type.
208 private function convertType($type)
225 return 'xsd:boolean';
234 return 'xsd:dateTime';
237 return 'soap-enc:Array';
243 return 'xsd:anyType';
248 if(strpos($type, '[]')) // if it is an array
250 $className = substr($type, 0, strlen($type) - 2);
251 $type = $className . 'Array';
252 $this->types[$type] = '';
253 $this->convertType($className);
257 if(!isset($this->types[$type]))
258 $this->extractClassProperties($type);
260 return 'tns:' . $type;
265 * Extract the type and the name of all properties of the $className class and saves it in the $types array
266 * This method extract properties from PHPDoc formatted comments for variables. Unfortunately the reflectionproperty
267 * class doesn't have a getDocComment method to extract comments about it, so we have to extract the information
268 * about the variables manually. Thanks heaps to Cristian Losada for implementing this.
269 * @param string $className The name of the class
271 private function extractClassProperties($className)
274 * modified by Qiang Xue, Jan. 2, 2007
275 * Using Reflection's DocComment to obtain property definitions
276 * DocComment is available since PHP 5.1
278 $reflection = new ReflectionClass($className);
279 $properties = $reflection->getProperties();
280 foreach($properties as $property)
282 $comment = $property->getDocComment();
283 if(strpos($comment, '@soapproperty') !== false)
285 if(preg_match('/@var\s+([\w\.]+(\[\s*\])?)\s*?\$(.*)$/mi',$comment,$matches))
287 // support nillable, minOccurs, maxOccurs attributes
288 $nillable=$minOccurs=$maxOccurs=false;
289 if(preg_match('/{(.+)}/',$matches[3],$attr))
291 $matches[3]=str_replace($attr[0],'',$matches[3]);
292 if(preg_match_all('/((\w+)\s*=\s*(\w+))/mi',$attr[1],$attr))
294 foreach($attr[2] as $id=>$prop)
296 if(strcasecmp($prop,'nillable')===0)
297 $nillable=$attr[3][$id] ? 'true' : 'false';
298 elseif(strcasecmp($prop,'minOccurs')===0)
299 $minOccurs=(int)$attr[3][$id];
300 elseif(strcasecmp($prop,'maxOccurs')===0)
301 $maxOccurs=(int)$attr[3][$id];
307 $param['type'] = $this->convertType($matches[1]);
308 $param['name'] = trim($matches[3]);
309 $param['nil'] = $nillable;
310 $param['minOc'] = $minOccurs;
311 $param['maxOc'] = $maxOccurs;
312 $this->types[$className][] = $param;