WSDLStruct.class.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. <?php
  2. /**
  3. * Class that can generate a WSDL document from PHP code
  4. *
  5. * This class generates a WSDL document for the given
  6. * methods when the the methods and parameters are documented
  7. * enough. When there is not enough documentation available (ie
  8. * unclear what the type of a variable or return type is) a
  9. * WSDLException is thrown.
  10. *
  11. * It should extend domdocument, but this would give problems with
  12. * arrays, see bug #28817
  13. *
  14. * Patch by Shawn Cook (Shawn@itbytez.com), wich makes it default to create empty message tags
  15. * when a method has no parameters, instead of creating no message tag at all.
  16. * This behaviour can be modified using the CREATE_EMPTY_INPUTS constant
  17. *
  18. * @author David Kingma
  19. * @version 1.5
  20. */
  21. class WSDLStruct {
  22. /** @var boolean */
  23. public $_debug = false;
  24. /** @var int binding type: SOAP_RPC | SOAP_DOCUMENT */
  25. public $binding_style;
  26. /** @var int use: SOAP_LITERAL | SOAP_ENCODED */
  27. public $use;
  28. /************************** Private properties ***************************/
  29. /** @var SOAPService[] */
  30. private $services = Array();
  31. /** @var domElement[] */
  32. private $serviceTags = Array();
  33. /** @var domElement[] */
  34. private $operationTags = Array();
  35. /** @var domElement[] references to the portType tags. servicename as key */
  36. private $portTypeTags = Array();
  37. /** @var domElement[] references to the binding tags. servicename as key */
  38. private $bindingTags = Array();
  39. /** @var domElement[] references to the binding operation tags. servicename as first key, operationname as second */
  40. private $bindingOperationTags = Array();
  41. /** @var domDocument */
  42. private $doc;
  43. /** @var domelement */
  44. private $definitions;
  45. /** @var domelement Refference tot the types tag*/
  46. private $typesTag;
  47. /** @var domelement Refference to the xsd:schema tag*/
  48. private $xsdSchema;
  49. /** @var IPXMLSchema */
  50. private $xmlSchema;
  51. //namespaces used
  52. const NS_WSDL = "http://schemas.xmlsoap.org/wsdl/";
  53. const NS_SOAP = "http://schemas.xmlsoap.org/wsdl/soap/";
  54. const NS_ENC = "http://schemas.xmlsoap.org/soap/encoding/";
  55. const NS_XSD = "http://www.w3.org/2001/XMLSchema";
  56. const CREATE_EMPTY_INPUTS = true;
  57. /*
  58. * @param string Target namespace
  59. * @param string URL for the webservice
  60. * @return void
  61. */
  62. public function __construct($tns, $url, $type = SOAP_RPC, $use = SOAP_ENCODED){
  63. if($type != SOAP_RPC && $type != SOAP_DOCUMENT) throw new Exception("Webservice type parameter should be either SOAP_RPC or SOAP_DOCUMENT");
  64. if($use != SOAP_ENCODED && $use != SOAP_LITERAL) throw new Exception("Webservice use parameter should be either SOAP_ENCODED or SOAP_LITERAL");
  65. $this->use = $use;
  66. $this->binding_style=$type;
  67. $this->tns = $tns;
  68. $this->url = $url;
  69. $this->doc = new domDocument();
  70. $this->definitions = $this->addElement("wsdl:definitions",$this->doc);
  71. $this->typesTag = $this->addElement("wsdl:types", $this->definitions);
  72. $this->xsdSchema = $this->addElement("xsd:schema", $this->typesTag);
  73. $this->xsdSchema->setAttribute("targetNamespace", $this->tns);
  74. $this->xmlSchema = new IPXMLSchema($this->xsdSchema);
  75. }
  76. /**
  77. * Adds the class to the services for this WSDL
  78. *
  79. * @param IPReflectionClass The service
  80. * @return void
  81. */
  82. public function setService(IPReflectionClass $class){
  83. $this->services[$class->classname] = $class;
  84. $this->services[$class->classname]->getMethods(false, false);
  85. }
  86. /**
  87. * @return string The WSDL document for this structure
  88. */
  89. public function generateDocument(){
  90. $this->addToDebug("Generating document");
  91. //add all definitions
  92. $definitions=$this->definitions;
  93. $definitions->setAttribute("xmlns", self::NS_WSDL);
  94. $definitions->setAttribute("xmlns:soap", self::NS_SOAP);
  95. $definitions->setAttribute("xmlns:SOAP-ENC", self::NS_ENC);
  96. $definitions->setAttribute("xmlns:wsdl", self::NS_WSDL);
  97. $definitions->setAttribute("xmlns:xsd", self::NS_XSD);
  98. $definitions->setAttribute("xmlns:tns", $this->tns);
  99. $definitions->setAttribute("targetNamespace", $this->tns);
  100. //add all the services
  101. foreach((array)$this->services as $serviceName => $service){
  102. //add the portType
  103. $portType = $this->addPortType($serviceName);
  104. //add binding
  105. $binding = $this->addBinding($serviceName);
  106. //loop the operations
  107. foreach((array)$service->methods as $operation){
  108. $operationName = $operation->name;
  109. $operationTag = $this->addOperation($operationName, $serviceName);
  110. //input
  111. //only when to operation needs arguments
  112. $parameters = $operation->getParameters();
  113. if(count($parameters)>0 || self::CREATE_EMPTY_INPUTS){
  114. $messageName = $operationName."Request";
  115. $input=$this->addElement("wsdl:input", $operationTag);
  116. $input->setAttribute("message", "tns:".$messageName);
  117. $para=Array();
  118. foreach((array)$parameters as $parameterName => $parameter){
  119. $para[$parameterName] = $parameter->type;
  120. }
  121. $this->addMessage($messageName, $para);
  122. $this->addInput($this->bindingOperationTags[$serviceName][$operationName]);
  123. }
  124. //output
  125. //only when the operation returns something
  126. if(!$operation->return || trim($operation->return) == "") throw new WSDLException('No return type for '.$operationName);
  127. if(strtolower(trim($operation->return))!='void'){
  128. $messageName = $operationName."Response";
  129. $output = $this->addElement("wsdl:output", $operationTag);
  130. $output->setAttribute("message", "tns:".$messageName);
  131. $this->addOutput($this->bindingOperationTags[$serviceName][$operationName]);
  132. $this->addMessage($messageName,Array($operation->name."Return" => $operation->return));
  133. }
  134. }
  135. // SH. now add the portType and binding
  136. $this->definitions->AppendChild($portType);
  137. $this->definitions->AppendChild($binding);
  138. //add the service
  139. $this->addService($serviceName);
  140. }
  141. return $this->doc->saveXML();
  142. }
  143. /**
  144. * Adds a new operation to the given service
  145. * @param string operation name
  146. * @param string service name
  147. * @return domElement
  148. */
  149. private function addOperation($operationName, $serviceName){
  150. $this->addToDebug("Adding Operation: '$operationName : $serviceName'");
  151. $operationTag = $this->addElement("wsdl:operation",$this->portTypeTags[$serviceName]);
  152. $operationTag->setAttribute("name",$operationName);
  153. //create operation tag for binding
  154. $bindingOperationTag = $this->addElement("wsdl:operation",$this->bindingTags[$serviceName]);
  155. $bindingOperationTag->setAttribute("name",$operationName);
  156. //soap operation tag
  157. $soapOperationTag = $this->addElement("soap:operation",$bindingOperationTag);
  158. $soapOperationTag->setAttribute("soapAction",$this->url."&method=".$operationName);
  159. $soapOperationTag->setAttribute("style",($this->binding_style == SOAP_RPC)? "rpc" : "document");
  160. //save references
  161. $this->operationTags[$serviceName][$operationName] = $operationTag;
  162. $this->bindingOperationTags[$serviceName][$operationName] = $bindingOperationTag;
  163. //and return
  164. return $operationTag;
  165. }
  166. /**
  167. * adds a new service tag to the WSDL file
  168. * @param string the service name
  169. * @return domElement
  170. */
  171. private function addService($serviceName){
  172. $this->addToDebug("Adding service: '$serviceName'");
  173. //create service
  174. $serviceTag=$this->addElement("wsdl:service",$this->definitions);
  175. $serviceTag->setAttribute("name",$serviceName);
  176. //port tag
  177. $portTag=$this->addElement("wsdl:port", $serviceTag);
  178. $portTag->setAttribute("name", $serviceName."Port");
  179. $portTag->setAttribute("binding", "tns:".$serviceName."Binding");
  180. //address tag
  181. $addressTag = $this->addElement("soap:address", $portTag);
  182. $addressTag->setAttribute("location", $this->url);
  183. //keep a reference
  184. $this->serviceTags[$serviceName] = $serviceTag;
  185. //and return
  186. return $serviceTag;
  187. }
  188. /**
  189. * Adds a new portType to the WSDL structure
  190. * @param string the service name for which we create a portType
  191. * @return domElement
  192. */
  193. private function addPortType($serviceName){
  194. $this->addToDebug("Adding portType: '$serviceName'");
  195. // SH don't add to main doc just yet
  196. // $portTypeTag=$this->addElement("wsdl:portType", $this->definitions);
  197. $portTypeTag = $this->addElement("wsdl:portType");
  198. $portTypeTag->setAttribute("name", $serviceName."PortType");
  199. //keep a reference
  200. $this->portTypeTags[$serviceName]=$portTypeTag;
  201. //and return
  202. return $portTypeTag;
  203. }
  204. /**
  205. * Adds a new binding to the WSDL structure
  206. * @param string serviceName to bind
  207. * @return domElement
  208. */
  209. private function addBinding($serviceName){
  210. $this->addToDebug("Adding binding: '$serviceName'");
  211. // SH. don't add to main doc just yet
  212. // $bindingTag=$this->addElement("binding");
  213. $bindingTag=$this->addElement("binding",$this->definitions);
  214. $bindingTag->setAttribute("name", $serviceName."Binding");
  215. $bindingTag->setAttribute("type", "tns:".$serviceName."PortType");
  216. //soap binding tag
  217. $soapBindingTag = $this->addElement("soap:binding", $bindingTag);
  218. $soapBindingTag->setAttribute("style", ($this->binding_style == SOAP_RPC)? "rpc" : "document");
  219. $soapBindingTag->setAttribute("transport", "http://schemas.xmlsoap.org/soap/http");
  220. //keep a reference
  221. $this->bindingTags[$serviceName] = $bindingTag;
  222. //and return
  223. return $bindingTag;
  224. }
  225. /**
  226. * Adds a message tag to the WSDL document
  227. * @param string Message name
  228. * @param Array[string=>string] Array with variables & types
  229. */
  230. private function addMessage($name, $parts){
  231. $this->addToDebug("Adding message: '$name'");
  232. $msg = $this->addElement("message", $this->definitions);
  233. $msg->setAttribute("name", $name);
  234. foreach((array)$parts as $partName => $partType){
  235. $this->addToDebug("Adding Message part: '$partName => $partType'");
  236. $part=$this->addElement("part", $msg);
  237. $part->setAttribute("name", $partName);
  238. //check if it is a valid XML Schema datatype
  239. if($t = IPXMLSchema::checkSchemaType(strtolower($partType)))
  240. $part->setAttribute("type", "xsd:".$t);
  241. else{
  242. //If it is an array, change the type name
  243. $partName = (substr($partType,-2) == "[]")?substr($partType,0,strpos($partType,"["))."Array":$partType;
  244. $part->setAttribute("type", "tns:".$partName);
  245. $this->xmlSchema->addComplexType($partType, $partName);
  246. }
  247. }
  248. }
  249. /**
  250. * Adds an input element to the given parent (an operation tag)
  251. * @param domNode The Parent domNode
  252. * @param boolean Kind of tag. true=input tag, false=output tag
  253. * @return domNode The input/output node
  254. */
  255. private function addInput($parent, $input=true){
  256. $name = $input ? "wsdl:input" : "wsdl:output";
  257. $tag=$this->addElement($name, $parent);
  258. $soapOperation=$this->addElement("soap:body", $tag);
  259. $soapOperation->setAttribute("use", ($this->use == SOAP_ENCODED)? "encoded" : "literal");
  260. $soapOperation->setAttribute("namespace", $this->tns);
  261. if($this->use == SOAP_ENCODED)
  262. $soapOperation->setAttribute("encodingStyle", self::NS_ENC);
  263. }
  264. /**
  265. * Adds an output element to the given parent (an operation tag)
  266. * @param domNode The Parent domNode
  267. * @return domNode The output node
  268. */
  269. private function addOutput($parent){
  270. return $this->addInput($parent,false);
  271. }
  272. /************************* Supporting functions ****************************/
  273. private function addToDebug($msg){
  274. if($this->_debug) echo '-'.$msg." <br>\n";
  275. }
  276. /**
  277. * Adds an child element to the parent
  278. * @param string The name element
  279. * @param domNode
  280. * @return domNode
  281. */
  282. private function addElement($name, $parent=false, $ns=false){
  283. if($ns)
  284. $el=$this->doc->createElementNS($ns,$name);
  285. else
  286. $el=$this->doc->createElement($name);
  287. if($parent)
  288. $parent->appendChild($el);
  289. return $el;
  290. }
  291. }
  292. ?>