123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- <?php
- /**
- * Class that can generate a WSDL document from PHP code
- *
- * This class generates a WSDL document for the given
- * methods when the the methods and parameters are documented
- * enough. When there is not enough documentation available (ie
- * unclear what the type of a variable or return type is) a
- * WSDLException is thrown.
- *
- * It should extend domdocument, but this would give problems with
- * arrays, see bug #28817
- *
- * Patch by Shawn Cook (Shawn@itbytez.com), wich makes it default to create empty message tags
- * when a method has no parameters, instead of creating no message tag at all.
- * This behaviour can be modified using the CREATE_EMPTY_INPUTS constant
- *
- * @author David Kingma
- * @version 1.5
- */
- class WSDLStruct {
- /** @var boolean */
- public $_debug = false;
- /** @var int binding type: SOAP_RPC | SOAP_DOCUMENT */
- public $binding_style;
- /** @var int use: SOAP_LITERAL | SOAP_ENCODED */
- public $use;
- /************************** Private properties ***************************/
-
- /** @var SOAPService[] */
- private $services = Array();
- /** @var domElement[] */
- private $serviceTags = Array();
- /** @var domElement[] */
- private $operationTags = Array();
- /** @var domElement[] references to the portType tags. servicename as key */
- private $portTypeTags = Array();
-
- /** @var domElement[] references to the binding tags. servicename as key */
- private $bindingTags = Array();
-
- /** @var domElement[] references to the binding operation tags. servicename as first key, operationname as second */
- private $bindingOperationTags = Array();
-
- /** @var domDocument */
- private $doc;
-
- /** @var domelement */
- private $definitions;
- /** @var domelement Refference tot the types tag*/
- private $typesTag;
-
- /** @var domelement Refference to the xsd:schema tag*/
- private $xsdSchema;
- /** @var IPXMLSchema */
- private $xmlSchema;
-
- //namespaces used
- const NS_WSDL = "http://schemas.xmlsoap.org/wsdl/";
- const NS_SOAP = "http://schemas.xmlsoap.org/wsdl/soap/";
- const NS_ENC = "http://schemas.xmlsoap.org/soap/encoding/";
- const NS_XSD = "http://www.w3.org/2001/XMLSchema";
-
- const CREATE_EMPTY_INPUTS = true;
-
- /*
- * @param string Target namespace
- * @param string URL for the webservice
- * @return void
- */
- public function __construct($tns, $url, $type = SOAP_RPC, $use = SOAP_ENCODED){
- if($type != SOAP_RPC && $type != SOAP_DOCUMENT) throw new Exception("Webservice type parameter should be either SOAP_RPC or SOAP_DOCUMENT");
- if($use != SOAP_ENCODED && $use != SOAP_LITERAL) throw new Exception("Webservice use parameter should be either SOAP_ENCODED or SOAP_LITERAL");
- $this->use = $use;
- $this->binding_style=$type;
- $this->tns = $tns;
- $this->url = $url;
- $this->doc = new domDocument();
- $this->definitions = $this->addElement("wsdl:definitions",$this->doc);
- $this->typesTag = $this->addElement("wsdl:types", $this->definitions);
- $this->xsdSchema = $this->addElement("xsd:schema", $this->typesTag);
- $this->xsdSchema->setAttribute("targetNamespace", $this->tns);
- $this->xmlSchema = new IPXMLSchema($this->xsdSchema);
- }
- /**
- * Adds the class to the services for this WSDL
- *
- * @param IPReflectionClass The service
- * @return void
- */
- public function setService(IPReflectionClass $class){
- $this->services[$class->classname] = $class;
- $this->services[$class->classname]->getMethods(false, false);
- }
- /**
- * @return string The WSDL document for this structure
- */
- public function generateDocument(){
- $this->addToDebug("Generating document");
-
- //add all definitions
- $definitions=$this->definitions;
- $definitions->setAttribute("xmlns", self::NS_WSDL);
- $definitions->setAttribute("xmlns:soap", self::NS_SOAP);
- $definitions->setAttribute("xmlns:SOAP-ENC", self::NS_ENC);
- $definitions->setAttribute("xmlns:wsdl", self::NS_WSDL);
- $definitions->setAttribute("xmlns:xsd", self::NS_XSD);
- $definitions->setAttribute("xmlns:tns", $this->tns);
- $definitions->setAttribute("targetNamespace", $this->tns);
- //add all the services
- foreach((array)$this->services as $serviceName => $service){
- //add the portType
- $portType = $this->addPortType($serviceName);
- //add binding
- $binding = $this->addBinding($serviceName);
- //loop the operations
- foreach((array)$service->methods as $operation){
- $operationName = $operation->name;
- $operationTag = $this->addOperation($operationName, $serviceName);
- //input
- //only when to operation needs arguments
- $parameters = $operation->getParameters();
- if(count($parameters)>0 || self::CREATE_EMPTY_INPUTS){
- $messageName = $operationName."Request";
- $input=$this->addElement("wsdl:input", $operationTag);
- $input->setAttribute("message", "tns:".$messageName);
- $para=Array();
- foreach((array)$parameters as $parameterName => $parameter){
- $para[$parameterName] = $parameter->type;
- }
- $this->addMessage($messageName, $para);
- $this->addInput($this->bindingOperationTags[$serviceName][$operationName]);
- }
- //output
- //only when the operation returns something
- if(!$operation->return || trim($operation->return) == "") throw new WSDLException('No return type for '.$operationName);
- if(strtolower(trim($operation->return))!='void'){
- $messageName = $operationName."Response";
- $output = $this->addElement("wsdl:output", $operationTag);
- $output->setAttribute("message", "tns:".$messageName);
- $this->addOutput($this->bindingOperationTags[$serviceName][$operationName]);
- $this->addMessage($messageName,Array($operation->name."Return" => $operation->return));
- }
- }
- // SH. now add the portType and binding
- $this->definitions->AppendChild($portType);
- $this->definitions->AppendChild($binding);
-
- //add the service
- $this->addService($serviceName);
-
- }
- return $this->doc->saveXML();
- }
-
- /**
- * Adds a new operation to the given service
- * @param string operation name
- * @param string service name
- * @return domElement
- */
- private function addOperation($operationName, $serviceName){
- $this->addToDebug("Adding Operation: '$operationName : $serviceName'");
- $operationTag = $this->addElement("wsdl:operation",$this->portTypeTags[$serviceName]);
- $operationTag->setAttribute("name",$operationName);
- //create operation tag for binding
- $bindingOperationTag = $this->addElement("wsdl:operation",$this->bindingTags[$serviceName]);
- $bindingOperationTag->setAttribute("name",$operationName);
-
- //soap operation tag
- $soapOperationTag = $this->addElement("soap:operation",$bindingOperationTag);
- $soapOperationTag->setAttribute("soapAction",$this->url."&method=".$operationName);
- $soapOperationTag->setAttribute("style",($this->binding_style == SOAP_RPC)? "rpc" : "document");
-
- //save references
- $this->operationTags[$serviceName][$operationName] = $operationTag;
- $this->bindingOperationTags[$serviceName][$operationName] = $bindingOperationTag;
-
- //and return
- return $operationTag;
- }
-
- /**
- * adds a new service tag to the WSDL file
- * @param string the service name
- * @return domElement
- */
- private function addService($serviceName){
- $this->addToDebug("Adding service: '$serviceName'");
- //create service
- $serviceTag=$this->addElement("wsdl:service",$this->definitions);
- $serviceTag->setAttribute("name",$serviceName);
- //port tag
- $portTag=$this->addElement("wsdl:port", $serviceTag);
- $portTag->setAttribute("name", $serviceName."Port");
- $portTag->setAttribute("binding", "tns:".$serviceName."Binding");
- //address tag
- $addressTag = $this->addElement("soap:address", $portTag);
- $addressTag->setAttribute("location", $this->url);
-
- //keep a reference
- $this->serviceTags[$serviceName] = $serviceTag;
- //and return
- return $serviceTag;
- }
-
- /**
- * Adds a new portType to the WSDL structure
- * @param string the service name for which we create a portType
- * @return domElement
- */
- private function addPortType($serviceName){
- $this->addToDebug("Adding portType: '$serviceName'");
- // SH don't add to main doc just yet
- // $portTypeTag=$this->addElement("wsdl:portType", $this->definitions);
- $portTypeTag = $this->addElement("wsdl:portType");
- $portTypeTag->setAttribute("name", $serviceName."PortType");
- //keep a reference
- $this->portTypeTags[$serviceName]=$portTypeTag;
- //and return
- return $portTypeTag;
- }
-
- /**
- * Adds a new binding to the WSDL structure
- * @param string serviceName to bind
- * @return domElement
- */
- private function addBinding($serviceName){
- $this->addToDebug("Adding binding: '$serviceName'");
- // SH. don't add to main doc just yet
- // $bindingTag=$this->addElement("binding");
- $bindingTag=$this->addElement("binding",$this->definitions);
- $bindingTag->setAttribute("name", $serviceName."Binding");
- $bindingTag->setAttribute("type", "tns:".$serviceName."PortType");
-
- //soap binding tag
- $soapBindingTag = $this->addElement("soap:binding", $bindingTag);
- $soapBindingTag->setAttribute("style", ($this->binding_style == SOAP_RPC)? "rpc" : "document");
- $soapBindingTag->setAttribute("transport", "http://schemas.xmlsoap.org/soap/http");
-
- //keep a reference
- $this->bindingTags[$serviceName] = $bindingTag;
- //and return
- return $bindingTag;
- }
-
- /**
- * Adds a message tag to the WSDL document
- * @param string Message name
- * @param Array[string=>string] Array with variables & types
- */
- private function addMessage($name, $parts){
- $this->addToDebug("Adding message: '$name'");
- $msg = $this->addElement("message", $this->definitions);
- $msg->setAttribute("name", $name);
- foreach((array)$parts as $partName => $partType){
- $this->addToDebug("Adding Message part: '$partName => $partType'");
- $part=$this->addElement("part", $msg);
- $part->setAttribute("name", $partName);
- //check if it is a valid XML Schema datatype
- if($t = IPXMLSchema::checkSchemaType(strtolower($partType)))
- $part->setAttribute("type", "xsd:".$t);
- else{
- //If it is an array, change the type name
- $partName = (substr($partType,-2) == "[]")?substr($partType,0,strpos($partType,"["))."Array":$partType;
- $part->setAttribute("type", "tns:".$partName);
- $this->xmlSchema->addComplexType($partType, $partName);
- }
- }
- }
- /**
- * Adds an input element to the given parent (an operation tag)
- * @param domNode The Parent domNode
- * @param boolean Kind of tag. true=input tag, false=output tag
- * @return domNode The input/output node
- */
- private function addInput($parent, $input=true){
- $name = $input ? "wsdl:input" : "wsdl:output";
- $tag=$this->addElement($name, $parent);
- $soapOperation=$this->addElement("soap:body", $tag);
- $soapOperation->setAttribute("use", ($this->use == SOAP_ENCODED)? "encoded" : "literal");
- $soapOperation->setAttribute("namespace", $this->tns);
- if($this->use == SOAP_ENCODED)
- $soapOperation->setAttribute("encodingStyle", self::NS_ENC);
- }
- /**
- * Adds an output element to the given parent (an operation tag)
- * @param domNode The Parent domNode
- * @return domNode The output node
- */
- private function addOutput($parent){
- return $this->addInput($parent,false);
- }
- /************************* Supporting functions ****************************/
-
- private function addToDebug($msg){
- if($this->_debug) echo '-'.$msg." <br>\n";
- }
- /**
- * Adds an child element to the parent
- * @param string The name element
- * @param domNode
- * @return domNode
- */
- private function addElement($name, $parent=false, $ns=false){
- if($ns)
- $el=$this->doc->createElementNS($ns,$name);
- else
- $el=$this->doc->createElement($name);
- if($parent)
- $parent->appendChild($el);
- return $el;
- }
- }
- ?>
|