File indexing completed on 2024-12-22 05:37:07

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Soap
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /**
0023  * @see Zend_Soap_Wsdl_Strategy_Interface
0024  */
0025 // require_once "Zend/Soap/Wsdl/Strategy/Interface.php";
0026 
0027 /**
0028  * @see Zend_Soap_Wsdl_Strategy_Abstract
0029  */
0030 // require_once "Zend/Soap/Wsdl/Strategy/Abstract.php";
0031 
0032 /** @see Zend_Xml_Security */
0033 // require_once "Zend/Xml/Security.php";
0034 
0035 /**
0036  * Zend_Soap_Wsdl
0037  *
0038  * @category   Zend
0039  * @package    Zend_Soap
0040  */
0041 class Zend_Soap_Wsdl
0042 {
0043     /**
0044      * @var object DomDocument Instance
0045      */
0046     private $_dom;
0047 
0048     /**
0049      * @var object WSDL Root XML_Tree_Node
0050      */
0051     private $_wsdl;
0052 
0053     /**
0054      * @var string URI where the WSDL will be available
0055      */
0056     private $_uri;
0057 
0058     /**
0059      * @var DOMElement
0060      */
0061     private $_schema = null;
0062 
0063     /**
0064      * Types defined on schema
0065      *
0066      * @var array
0067      */
0068     private $_includedTypes = array();
0069 
0070     /**
0071      * Strategy for detection of complex types
0072      */
0073     protected $_strategy = null;
0074 
0075 
0076     /**
0077      * Constructor
0078      *
0079      * @param string  $name Name of the Web Service being Described
0080      * @param string  $uri URI where the WSDL will be available
0081      * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
0082      */
0083     public function __construct($name, $uri, $strategy = true)
0084     {
0085         if ($uri instanceof Zend_Uri_Http) {
0086             $uri = $uri->getUri();
0087         }
0088         $this->_uri = $uri;
0089 
0090         /**
0091          * @todo change DomDocument object creation from cparsing to construxting using API
0092          * It also should authomatically escape $name and $uri values if necessary
0093          */
0094         $wsdl = "<?xml version='1.0' ?>
0095                 <definitions name='$name' targetNamespace='$uri'
0096                     xmlns='http://schemas.xmlsoap.org/wsdl/'
0097                     xmlns:tns='$uri'
0098                     xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
0099                     xmlns:xsd='http://www.w3.org/2001/XMLSchema'
0100                     xmlns:soap-enc='http://schemas.xmlsoap.org/soap/encoding/'
0101                     xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'></definitions>";
0102         $this->_dom = new DOMDocument();
0103         if (!$this->_dom = Zend_Xml_Security::scan($wsdl, $this->_dom)) {
0104             // require_once 'Zend/Server/Exception.php';
0105             throw new Zend_Server_Exception('Unable to create DomDocument');
0106         } 
0107         $this->_wsdl = $this->_dom->documentElement;
0108 
0109         $this->setComplexTypeStrategy($strategy);
0110     }
0111 
0112     /**
0113      * Set a new uri for this WSDL
0114      *
0115      * @param  string|Zend_Uri_Http $uri
0116      * @return Zend_Server_Wsdl
0117      */
0118     public function setUri($uri)
0119     {
0120         if ($uri instanceof Zend_Uri_Http) {
0121             $uri = $uri->getUri();
0122         }
0123         $oldUri = $this->_uri;
0124         $this->_uri = $uri;
0125 
0126         if($this->_dom !== null) {
0127             // @todo: This is the worst hack ever, but its needed due to design and non BC issues of WSDL generation
0128             $xml = $this->_dom->saveXML();
0129             $xml = str_replace($oldUri, $uri, $xml);
0130             $this->_dom = new DOMDocument();
0131             $this->_dom = Zend_Xml_Security::scan($xml, $this->_dom);
0132         }
0133 
0134         return $this;
0135     }
0136 
0137     /**
0138      * Set a strategy for complex type detection and handling
0139      *
0140      * @todo Boolean is for backwards compability with extractComplexType object var. Remove it in later versions.
0141      * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
0142      * @return Zend_Soap_Wsdl
0143      */
0144     public function setComplexTypeStrategy($strategy)
0145     {
0146         if($strategy === true) {
0147             // require_once "Zend/Soap/Wsdl/Strategy/DefaultComplexType.php";
0148             $strategy = new Zend_Soap_Wsdl_Strategy_DefaultComplexType();
0149         } else if($strategy === false) {
0150             // require_once "Zend/Soap/Wsdl/Strategy/AnyType.php";
0151             $strategy = new Zend_Soap_Wsdl_Strategy_AnyType();
0152         } else if(is_string($strategy)) {
0153             if(class_exists($strategy)) {
0154                 $strategy = new $strategy();
0155             } else {
0156                 // require_once "Zend/Soap/Wsdl/Exception.php";
0157                 throw new Zend_Soap_Wsdl_Exception(
0158                     sprintf("Strategy with name '%s does not exist.", $strategy
0159                 ));
0160             }
0161         }
0162 
0163         if(!($strategy instanceof Zend_Soap_Wsdl_Strategy_Interface)) {
0164             // require_once "Zend/Soap/Wsdl/Exception.php";
0165             throw new Zend_Soap_Wsdl_Exception("Set a strategy that is not of type 'Zend_Soap_Wsdl_Strategy_Interface'");
0166         }
0167         $this->_strategy = $strategy;
0168         return $this;
0169     }
0170 
0171     /**
0172      * Get the current complex type strategy
0173      *
0174      * @return Zend_Soap_Wsdl_Strategy_Interface
0175      */
0176     public function getComplexTypeStrategy()
0177     {
0178         return $this->_strategy;
0179     }
0180 
0181     /**
0182      * Add a {@link http://www.w3.org/TR/wsdl#_messages message} element to the WSDL
0183      *
0184      * @param string $name Name for the {@link http://www.w3.org/TR/wsdl#_messages message}
0185      * @param array $parts An array of {@link http://www.w3.org/TR/wsdl#_message parts}
0186      *                     The array is constructed like: 'name of part' => 'part xml schema data type'
0187      *                     or 'name of part' => array('type' => 'part xml schema type')
0188      *                     or 'name of part' => array('element' => 'part xml element name')
0189      * @return object The new message's XML_Tree_Node for use in {@link function addDocumentation}
0190      */
0191     public function addMessage($name, $parts)
0192     {
0193         $message = $this->_dom->createElement('message');
0194 
0195         $message->setAttribute('name', $name);
0196 
0197         if (sizeof($parts) > 0) {
0198             foreach ($parts as $name => $type) {
0199                 $part = $this->_dom->createElement('part');
0200                 $part->setAttribute('name', $name);
0201                 if (is_array($type)) {
0202                     foreach ($type as $key => $value) {
0203                         $part->setAttribute($key, $value);
0204                     }
0205                 } else {
0206                     $part->setAttribute('type', $type);
0207                 }
0208                 $message->appendChild($part);
0209             }
0210         }
0211 
0212         $this->_wsdl->appendChild($message);
0213 
0214         return $message;
0215     }
0216 
0217     /**
0218      * Add a {@link http://www.w3.org/TR/wsdl#_porttypes portType} element to the WSDL
0219      *
0220      * @param string $name portType element's name
0221      * @return object The new portType's XML_Tree_Node for use in {@link function addPortOperation} and {@link function addDocumentation}
0222      */
0223     public function addPortType($name)
0224     {
0225         $portType = $this->_dom->createElement('portType');
0226         $portType->setAttribute('name', $name);
0227         $this->_wsdl->appendChild($portType);
0228 
0229         return $portType;
0230     }
0231 
0232     /**
0233      * Add an {@link http://www.w3.org/TR/wsdl#_request-response operation} element to a portType element
0234      *
0235      * @param object $portType a portType XML_Tree_Node, from {@link function addPortType}
0236      * @param string $name Operation name
0237      * @param string $input Input Message
0238      * @param string $output Output Message
0239      * @param string $fault Fault Message
0240      * @return object The new operation's XML_Tree_Node for use in {@link function addDocumentation}
0241      */
0242     public function addPortOperation($portType, $name, $input = false, $output = false, $fault = false)
0243     {
0244         $operation = $this->_dom->createElement('operation');
0245         $operation->setAttribute('name', $name);
0246 
0247         if (is_string($input) && (strlen(trim($input)) >= 1)) {
0248             $node = $this->_dom->createElement('input');
0249             $node->setAttribute('message', $input);
0250             $operation->appendChild($node);
0251         }
0252         if (is_string($output) && (strlen(trim($output)) >= 1)) {
0253             $node= $this->_dom->createElement('output');
0254             $node->setAttribute('message', $output);
0255             $operation->appendChild($node);
0256         }
0257         if (is_string($fault) && (strlen(trim($fault)) >= 1)) {
0258             $node = $this->_dom->createElement('fault');
0259             $node->setAttribute('message', $fault);
0260             $operation->appendChild($node);
0261         }
0262 
0263         $portType->appendChild($operation);
0264 
0265         return $operation;
0266     }
0267 
0268     /**
0269      * Add a {@link http://www.w3.org/TR/wsdl#_bindings binding} element to WSDL
0270      *
0271      * @param string $name Name of the Binding
0272      * @param string $type name of the portType to bind
0273      * @return object The new binding's XML_Tree_Node for use with {@link function addBindingOperation} and {@link function addDocumentation}
0274      */
0275     public function addBinding($name, $portType)
0276     {
0277         $binding = $this->_dom->createElement('binding');
0278         $binding->setAttribute('name', $name);
0279         $binding->setAttribute('type', $portType);
0280 
0281         $this->_wsdl->appendChild($binding);
0282 
0283         return $binding;
0284     }
0285 
0286     /**
0287      * Add an operation to a binding element
0288      *
0289      * @param object $binding A binding XML_Tree_Node returned by {@link function addBinding}
0290      * @param array $input An array of attributes for the input element, allowed keys are: 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
0291      * @param array $output An array of attributes for the output element, allowed keys are: 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
0292      * @param array $fault An array of attributes for the fault element, allowed keys are: 'name', 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
0293      * @return object The new Operation's XML_Tree_Node for use with {@link function addSoapOperation} and {@link function addDocumentation}
0294      */
0295     public function addBindingOperation($binding, $name, $input = false, $output = false, $fault = false)
0296     {
0297         $operation = $this->_dom->createElement('operation');
0298         $operation->setAttribute('name', $name);
0299 
0300         if (is_array($input)) {
0301             $node = $this->_dom->createElement('input');
0302             $soap_node = $this->_dom->createElement('soap:body');
0303             foreach ($input as $name => $value) {
0304                 $soap_node->setAttribute($name, $value);
0305             }
0306             $node->appendChild($soap_node);
0307             $operation->appendChild($node);
0308         }
0309 
0310         if (is_array($output)) {
0311             $node = $this->_dom->createElement('output');
0312             $soap_node = $this->_dom->createElement('soap:body');
0313             foreach ($output as $name => $value) {
0314                 $soap_node->setAttribute($name, $value);
0315             }
0316             $node->appendChild($soap_node);
0317             $operation->appendChild($node);
0318         }
0319 
0320         if (is_array($fault)) {
0321             $node = $this->_dom->createElement('fault');
0322             /**
0323              * Note. Do we really need name attribute to be also set at wsdl:fault node???
0324              * W3C standard doesn't mention it (http://www.w3.org/TR/wsdl#_soap:fault)
0325              * But some real world WSDLs use it, so it may be required for compatibility reasons.
0326              */
0327             if (isset($fault['name'])) {
0328                 $node->setAttribute('name', $fault['name']);
0329             }
0330 
0331             $soap_node = $this->_dom->createElement('soap:fault');
0332             foreach ($fault as $name => $value) {
0333                 $soap_node->setAttribute($name, $value);
0334             }
0335             $node->appendChild($soap_node);
0336             $operation->appendChild($node);
0337         }
0338 
0339         $binding->appendChild($operation);
0340 
0341         return $operation;
0342     }
0343 
0344     /**
0345      * Add a {@link http://www.w3.org/TR/wsdl#_soap:binding SOAP binding} element to a Binding element
0346      *
0347      * @param object $binding A binding XML_Tree_Node returned by {@link function addBinding}
0348      * @param string $style binding style, possible values are "rpc" (the default) and "document"
0349      * @param string $transport Transport method (defaults to HTTP)
0350      * @return boolean
0351      */
0352     public function addSoapBinding($binding, $style = 'document', $transport = 'http://schemas.xmlsoap.org/soap/http')
0353     {
0354         $soap_binding = $this->_dom->createElement('soap:binding');
0355         $soap_binding->setAttribute('style', $style);
0356         $soap_binding->setAttribute('transport', $transport);
0357 
0358         $binding->appendChild($soap_binding);
0359 
0360         return $soap_binding;
0361     }
0362 
0363     /**
0364      * Add a {@link http://www.w3.org/TR/wsdl#_soap:operation SOAP operation} to an operation element
0365      *
0366      * @param object $operation An operation XML_Tree_Node returned by {@link function addBindingOperation}
0367      * @param string $soap_action SOAP Action
0368      * @return boolean
0369      */
0370     public function addSoapOperation($binding, $soap_action)
0371     {
0372         if ($soap_action instanceof Zend_Uri_Http) {
0373             $soap_action = $soap_action->getUri();
0374         }
0375         $soap_operation = $this->_dom->createElement('soap:operation');
0376         $soap_operation->setAttribute('soapAction', $soap_action);
0377 
0378         $binding->insertBefore($soap_operation, $binding->firstChild);
0379 
0380         return $soap_operation;
0381     }
0382 
0383     /**
0384      * Add a {@link http://www.w3.org/TR/wsdl#_services service} element to the WSDL
0385      *
0386      * @param string $name Service Name
0387      * @param string $port_name Name of the port for the service
0388      * @param string $binding Binding for the port
0389      * @param string $location SOAP Address for the service
0390      * @return object The new service's XML_Tree_Node for use with {@link function addDocumentation}
0391      */
0392     public function addService($name, $port_name, $binding, $location)
0393     {
0394         if ($location instanceof Zend_Uri_Http) {
0395             $location = $location->getUri();
0396         }
0397         $service = $this->_dom->createElement('service');
0398         $service->setAttribute('name', $name);
0399 
0400         $port = $this->_dom->createElement('port');
0401         $port->setAttribute('name', $port_name);
0402         $port->setAttribute('binding', $binding);
0403 
0404         $soap_address = $this->_dom->createElement('soap:address');
0405         $soap_address->setAttribute('location', $location);
0406 
0407         $port->appendChild($soap_address);
0408         $service->appendChild($port);
0409 
0410         $this->_wsdl->appendChild($service);
0411 
0412         return $service;
0413     }
0414 
0415     /**
0416      * Add a documentation element to any element in the WSDL.
0417      *
0418      * Note that the WSDL {@link http://www.w3.org/TR/wsdl#_documentation specification} uses 'document',
0419      * but the WSDL {@link http://schemas.xmlsoap.org/wsdl/ schema} uses 'documentation' instead.
0420      * The {@link http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html#WSDL_documentation_Element WS-I Basic Profile 1.1} recommends using 'documentation'.
0421      *
0422      * @param object $input_node An XML_Tree_Node returned by another method to add the documentation to
0423      * @param string $documentation Human readable documentation for the node
0424      * @return DOMElement The documentation element
0425      */
0426     public function addDocumentation($input_node, $documentation)
0427     {
0428         if ($input_node === $this) {
0429             $node = $this->_dom->documentElement;
0430         } else {
0431             $node = $input_node;
0432         }
0433 
0434         $doc = $this->_dom->createElement('documentation');
0435         $doc_cdata = $this->_dom->createTextNode(str_replace(array("\r\n", "\r"), "\n", $documentation));
0436         $doc->appendChild($doc_cdata);
0437 
0438         if($node->hasChildNodes()) {
0439             $node->insertBefore($doc, $node->firstChild);
0440         } else {
0441             $node->appendChild($doc);
0442         }
0443 
0444         return $doc;
0445     }
0446 
0447     /**
0448      * Add WSDL Types element
0449      *
0450      * @param object $types A DomDocument|DomNode|DomElement|DomDocumentFragment with all the XML Schema types defined in it
0451      */
0452     public function addTypes($types)
0453     {
0454         if ($types instanceof DomDocument) {
0455             $dom = $this->_dom->importNode($types->documentElement);
0456             $this->_wsdl->appendChild($types->documentElement);
0457         } elseif ($types instanceof DomNode || $types instanceof DomElement || $types instanceof DomDocumentFragment ) {
0458             $dom = $this->_dom->importNode($types);
0459             $this->_wsdl->appendChild($dom);
0460         }
0461     }
0462 
0463     /**
0464      * Add a complex type name that is part of this WSDL and can be used in signatures.
0465      *
0466      * @param string $type
0467      * @return Zend_Soap_Wsdl
0468      */
0469     public function addType($type)
0470     {
0471         if(!in_array($type, $this->_includedTypes)) {
0472             $this->_includedTypes[] = $type;
0473         }
0474         return $this;
0475     }
0476 
0477     /**
0478      * Return an array of all currently included complex types
0479      *
0480      * @return array
0481      */
0482     public function getTypes()
0483     {
0484         return $this->_includedTypes;
0485     }
0486 
0487     /**
0488      * Return the Schema node of the WSDL
0489      *
0490      * @return DOMElement
0491      */
0492     public function getSchema()
0493     {
0494         if($this->_schema == null) {
0495             $this->addSchemaTypeSection();
0496         }
0497 
0498         return $this->_schema;
0499     }
0500 
0501     /**
0502      * Return the WSDL as XML
0503      *
0504      * @return string WSDL as XML
0505      */
0506     public function toXML()
0507     {
0508            return $this->_dom->saveXML();
0509     }
0510 
0511     /**
0512      * Return DOM Document
0513      *
0514      * @return object DomDocum ent
0515      */
0516     public function toDomDocument()
0517     {
0518         return $this->_dom;
0519     }
0520 
0521     /**
0522      * Echo the WSDL as XML
0523      *
0524      * @return boolean
0525      */
0526     public function dump($filename = false)
0527     {
0528         if (!$filename) {
0529             echo $this->toXML();
0530             return true;
0531         } else {
0532             return file_put_contents($filename, $this->toXML());
0533         }
0534     }
0535 
0536     /**
0537      * Returns an XSD Type for the given PHP type
0538      *
0539      * @param string $type PHP Type to get the XSD type for
0540      * @return string
0541      */
0542     public function getType($type)
0543     {
0544         switch (strtolower($type)) {
0545             case 'string':
0546             case 'str':
0547                 return 'xsd:string';
0548             case 'long':
0549                 return 'xsd:long';
0550             case 'int':
0551             case 'integer':
0552                 return 'xsd:int';
0553             case 'float':
0554                 return 'xsd:float';
0555             case 'double':
0556                 return 'xsd:double';
0557             case 'boolean':
0558             case 'bool':
0559                 return 'xsd:boolean';
0560             case 'array':
0561                 return 'soap-enc:Array';
0562             case 'object':
0563                 return 'xsd:struct';
0564             case 'mixed':
0565                 return 'xsd:anyType';
0566             case 'void':
0567                 return '';
0568             default:
0569                 // delegate retrieval of complex type to current strategy
0570                 return $this->addComplexType($type);
0571             }
0572     }
0573 
0574     /**
0575      * This function makes sure a complex types section and schema additions are set.
0576      *
0577      * @return Zend_Soap_Wsdl
0578      */
0579     public function addSchemaTypeSection()
0580     {
0581         if ($this->_schema === null) {
0582             $this->_schema = $this->_dom->createElement('xsd:schema');
0583             $this->_schema->setAttribute('targetNamespace', $this->_uri);
0584             $types = $this->_dom->createElement('types');
0585             $types->appendChild($this->_schema);
0586             $this->_wsdl->appendChild($types);
0587         }
0588         return $this;
0589     }
0590 
0591     /**
0592      * Add a {@link http://www.w3.org/TR/wsdl#_types types} data type definition
0593      *
0594      * @param string $type Name of the class to be specified
0595      * @return string XSD Type for the given PHP type
0596      */
0597     public function addComplexType($type)
0598     {
0599         if (in_array($type, $this->getTypes())) {
0600             return "tns:$type";
0601         }
0602         $this->addSchemaTypeSection();
0603 
0604         $strategy = $this->getComplexTypeStrategy();
0605         $strategy->setContext($this);
0606         // delegates the detection of a complex type to the current strategy
0607         return $strategy->addComplexType($type);
0608     }
0609 
0610     /**
0611      * Parse an xsd:element represented as an array into a DOMElement.
0612      *
0613      * @param array $element an xsd:element represented as an array
0614      * @return DOMElement parsed element
0615      */
0616     private function _parseElement($element)
0617     {
0618         if (!is_array($element)) {
0619             // require_once "Zend/Soap/Wsdl/Exception.php";
0620             throw new Zend_Soap_Wsdl_Exception("The 'element' parameter needs to be an associative array.");
0621         }
0622 
0623         $elementXml = $this->_dom->createElement('xsd:element');
0624         foreach ($element as $key => $value) {
0625             if (in_array($key, array('sequence', 'all', 'choice'))) {
0626                 if (is_array($value)) {
0627                     $complexType = $this->_dom->createElement('xsd:complexType');
0628                     if (count($value) > 0) {
0629                         $container = $this->_dom->createElement('xsd:' . $key);
0630                         foreach ($value as $subelement) {
0631                             $subelementXml = $this->_parseElement($subelement);
0632                             $container->appendChild($subelementXml);
0633                         }
0634                         $complexType->appendChild($container);
0635                     }
0636                     $elementXml->appendChild($complexType);
0637                 }
0638             } else {
0639                 $elementXml->setAttribute($key, $value);
0640             }
0641         }
0642         return $elementXml;
0643     }
0644 
0645     /**
0646      * Add an xsd:element represented as an array to the schema.
0647      *
0648      * Array keys represent attribute names and values their respective value.
0649      * The 'sequence', 'all' and 'choice' keys must have an array of elements as their value,
0650      * to add them to a nested complexType.
0651      *
0652      * Example: array( 'name' => 'MyElement',
0653      *                 'sequence' => array( array('name' => 'myString', 'type' => 'string'),
0654      *                                      array('name' => 'myInteger', 'type' => 'int') ) );
0655      * Resulting XML: <xsd:element name="MyElement"><xsd:complexType><xsd:sequence>
0656      *                  <xsd:element name="myString" type="string"/>
0657      *                  <xsd:element name="myInteger" type="int"/>
0658      *                </xsd:sequence></xsd:complexType></xsd:element>
0659      *
0660      * @param array $element an xsd:element represented as an array
0661      * @return string xsd:element for the given element array
0662      */
0663     public function addElement($element)
0664     {
0665         $schema = $this->getSchema();
0666         $elementXml = $this->_parseElement($element);
0667         $schema->appendChild($elementXml);
0668         return 'tns:' . $element['name'];
0669     }
0670 }