PageRenderTime 25ms CodeModel.GetById 5ms app.highlight 15ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Soap/Wsdl.php

https://bitbucket.org/hamidrezas/melobit
PHP | 667 lines | 373 code | 67 blank | 227 comment | 48 complexity | ff37ce179f70d750dceee5e7c1fb767a MD5 | raw file
Possible License(s): AGPL-1.0
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Soap
 17 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 18 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 19 * @version    $Id: Wsdl.php 24601 2012-01-10 21:16:28Z ralph $
 20 */
 21
 22/**
 23 * @see Zend_Soap_Wsdl_Strategy_Interface
 24 */
 25require_once "Zend/Soap/Wsdl/Strategy/Interface.php";
 26
 27/**
 28 * @see Zend_Soap_Wsdl_Strategy_Abstract
 29 */
 30require_once "Zend/Soap/Wsdl/Strategy/Abstract.php";
 31
 32/**
 33 * Zend_Soap_Wsdl
 34 *
 35 * @category   Zend
 36 * @package    Zend_Soap
 37 */
 38class Zend_Soap_Wsdl
 39{
 40    /**
 41     * @var object DomDocument Instance
 42     */
 43    private $_dom;
 44
 45    /**
 46     * @var object WSDL Root XML_Tree_Node
 47     */
 48    private $_wsdl;
 49
 50    /**
 51     * @var string URI where the WSDL will be available
 52     */
 53    private $_uri;
 54
 55    /**
 56     * @var DOMElement
 57     */
 58    private $_schema = null;
 59
 60    /**
 61     * Types defined on schema
 62     *
 63     * @var array
 64     */
 65    private $_includedTypes = array();
 66
 67    /**
 68     * Strategy for detection of complex types
 69     */
 70    protected $_strategy = null;
 71
 72
 73    /**
 74     * Constructor
 75     *
 76     * @param string  $name Name of the Web Service being Described
 77     * @param string  $uri URI where the WSDL will be available
 78     * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
 79     */
 80    public function __construct($name, $uri, $strategy = true)
 81    {
 82        if ($uri instanceof Zend_Uri_Http) {
 83            $uri = $uri->getUri();
 84        }
 85        $this->_uri = $uri;
 86
 87        /**
 88         * @todo change DomDocument object creation from cparsing to construxting using API
 89         * It also should authomatically escape $name and $uri values if necessary
 90         */
 91        $wsdl = "<?xml version='1.0' ?>
 92                <definitions name='$name' targetNamespace='$uri'
 93                    xmlns='http://schemas.xmlsoap.org/wsdl/'
 94                    xmlns:tns='$uri'
 95                    xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
 96                    xmlns:xsd='http://www.w3.org/2001/XMLSchema'
 97                    xmlns:soap-enc='http://schemas.xmlsoap.org/soap/encoding/'
 98                    xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'></definitions>";
 99        $this->_dom = new DOMDocument();
100        if (!$this->_dom->loadXML($wsdl)) {
101            require_once 'Zend/Server/Exception.php';
102            throw new Zend_Server_Exception('Unable to create DomDocument');
103        } else {
104            $this->_wsdl = $this->_dom->documentElement;
105        }
106
107        $this->setComplexTypeStrategy($strategy);
108    }
109
110    /**
111     * Set a new uri for this WSDL
112     *
113     * @param  string|Zend_Uri_Http $uri
114     * @return Zend_Server_Wsdl
115     */
116    public function setUri($uri)
117    {
118        if ($uri instanceof Zend_Uri_Http) {
119            $uri = $uri->getUri();
120        }
121        $oldUri = $this->_uri;
122        $this->_uri = $uri;
123
124        if($this->_dom !== null) {
125            // @todo: This is the worst hack ever, but its needed due to design and non BC issues of WSDL generation
126            $xml = $this->_dom->saveXML();
127            $xml = str_replace($oldUri, $uri, $xml);
128            $this->_dom = new DOMDocument();
129            $this->_dom->loadXML($xml);
130        }
131
132        return $this;
133    }
134
135    /**
136     * Set a strategy for complex type detection and handling
137     *
138     * @todo Boolean is for backwards compability with extractComplexType object var. Remove it in later versions.
139     * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
140     * @return Zend_Soap_Wsdl
141     */
142    public function setComplexTypeStrategy($strategy)
143    {
144        if($strategy === true) {
145            require_once "Zend/Soap/Wsdl/Strategy/DefaultComplexType.php";
146            $strategy = new Zend_Soap_Wsdl_Strategy_DefaultComplexType();
147        } else if($strategy === false) {
148            require_once "Zend/Soap/Wsdl/Strategy/AnyType.php";
149            $strategy = new Zend_Soap_Wsdl_Strategy_AnyType();
150        } else if(is_string($strategy)) {
151            if(class_exists($strategy)) {
152                $strategy = new $strategy();
153            } else {
154                require_once "Zend/Soap/Wsdl/Exception.php";
155                throw new Zend_Soap_Wsdl_Exception(
156                    sprintf("Strategy with name '%s does not exist.", $strategy
157                ));
158            }
159        }
160
161        if(!($strategy instanceof Zend_Soap_Wsdl_Strategy_Interface)) {
162            require_once "Zend/Soap/Wsdl/Exception.php";
163            throw new Zend_Soap_Wsdl_Exception("Set a strategy that is not of type 'Zend_Soap_Wsdl_Strategy_Interface'");
164        }
165        $this->_strategy = $strategy;
166        return $this;
167    }
168
169    /**
170     * Get the current complex type strategy
171     *
172     * @return Zend_Soap_Wsdl_Strategy_Interface
173     */
174    public function getComplexTypeStrategy()
175    {
176        return $this->_strategy;
177    }
178
179    /**
180     * Add a {@link http://www.w3.org/TR/wsdl#_messages message} element to the WSDL
181     *
182     * @param string $name Name for the {@link http://www.w3.org/TR/wsdl#_messages message}
183     * @param array $parts An array of {@link http://www.w3.org/TR/wsdl#_message parts}
184     *                     The array is constructed like: 'name of part' => 'part xml schema data type'
185     *                     or 'name of part' => array('type' => 'part xml schema type')
186     *                     or 'name of part' => array('element' => 'part xml element name')
187     * @return object The new message's XML_Tree_Node for use in {@link function addDocumentation}
188     */
189    public function addMessage($name, $parts)
190    {
191        $message = $this->_dom->createElement('message');
192
193        $message->setAttribute('name', $name);
194
195        if (sizeof($parts) > 0) {
196            foreach ($parts as $name => $type) {
197                $part = $this->_dom->createElement('part');
198                $part->setAttribute('name', $name);
199                if (is_array($type)) {
200                    foreach ($type as $key => $value) {
201                        $part->setAttribute($key, $value);
202                    }
203                } else {
204                    $part->setAttribute('type', $type);
205                }
206                $message->appendChild($part);
207            }
208        }
209
210        $this->_wsdl->appendChild($message);
211
212        return $message;
213    }
214
215    /**
216     * Add a {@link http://www.w3.org/TR/wsdl#_porttypes portType} element to the WSDL
217     *
218     * @param string $name portType element's name
219     * @return object The new portType's XML_Tree_Node for use in {@link function addPortOperation} and {@link function addDocumentation}
220     */
221    public function addPortType($name)
222    {
223        $portType = $this->_dom->createElement('portType');
224        $portType->setAttribute('name', $name);
225        $this->_wsdl->appendChild($portType);
226
227        return $portType;
228    }
229
230    /**
231     * Add an {@link http://www.w3.org/TR/wsdl#_request-response operation} element to a portType element
232     *
233     * @param object $portType a portType XML_Tree_Node, from {@link function addPortType}
234     * @param string $name Operation name
235     * @param string $input Input Message
236     * @param string $output Output Message
237     * @param string $fault Fault Message
238     * @return object The new operation's XML_Tree_Node for use in {@link function addDocumentation}
239     */
240    public function addPortOperation($portType, $name, $input = false, $output = false, $fault = false)
241    {
242        $operation = $this->_dom->createElement('operation');
243        $operation->setAttribute('name', $name);
244
245        if (is_string($input) && (strlen(trim($input)) >= 1)) {
246            $node = $this->_dom->createElement('input');
247            $node->setAttribute('message', $input);
248            $operation->appendChild($node);
249        }
250        if (is_string($output) && (strlen(trim($output)) >= 1)) {
251            $node= $this->_dom->createElement('output');
252            $node->setAttribute('message', $output);
253            $operation->appendChild($node);
254        }
255        if (is_string($fault) && (strlen(trim($fault)) >= 1)) {
256            $node = $this->_dom->createElement('fault');
257            $node->setAttribute('message', $fault);
258            $operation->appendChild($node);
259        }
260
261        $portType->appendChild($operation);
262
263        return $operation;
264    }
265
266    /**
267     * Add a {@link http://www.w3.org/TR/wsdl#_bindings binding} element to WSDL
268     *
269     * @param string $name Name of the Binding
270     * @param string $type name of the portType to bind
271     * @return object The new binding's XML_Tree_Node for use with {@link function addBindingOperation} and {@link function addDocumentation}
272     */
273    public function addBinding($name, $portType)
274    {
275        $binding = $this->_dom->createElement('binding');
276        $binding->setAttribute('name', $name);
277        $binding->setAttribute('type', $portType);
278
279        $this->_wsdl->appendChild($binding);
280
281        return $binding;
282    }
283
284    /**
285     * Add an operation to a binding element
286     *
287     * @param object $binding A binding XML_Tree_Node returned by {@link function addBinding}
288     * @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}
289     * @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}
290     * @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}
291     * @return object The new Operation's XML_Tree_Node for use with {@link function addSoapOperation} and {@link function addDocumentation}
292     */
293    public function addBindingOperation($binding, $name, $input = false, $output = false, $fault = false)
294    {
295        $operation = $this->_dom->createElement('operation');
296        $operation->setAttribute('name', $name);
297
298        if (is_array($input)) {
299            $node = $this->_dom->createElement('input');
300            $soap_node = $this->_dom->createElement('soap:body');
301            foreach ($input as $name => $value) {
302                $soap_node->setAttribute($name, $value);
303            }
304            $node->appendChild($soap_node);
305            $operation->appendChild($node);
306        }
307
308        if (is_array($output)) {
309            $node = $this->_dom->createElement('output');
310            $soap_node = $this->_dom->createElement('soap:body');
311            foreach ($output as $name => $value) {
312                $soap_node->setAttribute($name, $value);
313            }
314            $node->appendChild($soap_node);
315            $operation->appendChild($node);
316        }
317
318        if (is_array($fault)) {
319            $node = $this->_dom->createElement('fault');
320            /**
321             * Note. Do we really need name attribute to be also set at wsdl:fault node???
322             * W3C standard doesn't mention it (http://www.w3.org/TR/wsdl#_soap:fault)
323             * But some real world WSDLs use it, so it may be required for compatibility reasons.
324             */
325            if (isset($fault['name'])) {
326                $node->setAttribute('name', $fault['name']);
327            }
328
329            $soap_node = $this->_dom->createElement('soap:fault');
330            foreach ($fault as $name => $value) {
331                $soap_node->setAttribute($name, $value);
332            }
333            $node->appendChild($soap_node);
334            $operation->appendChild($node);
335        }
336
337        $binding->appendChild($operation);
338
339        return $operation;
340    }
341
342    /**
343     * Add a {@link http://www.w3.org/TR/wsdl#_soap:binding SOAP binding} element to a Binding element
344     *
345     * @param object $binding A binding XML_Tree_Node returned by {@link function addBinding}
346     * @param string $style binding style, possible values are "rpc" (the default) and "document"
347     * @param string $transport Transport method (defaults to HTTP)
348     * @return boolean
349     */
350    public function addSoapBinding($binding, $style = 'document', $transport = 'http://schemas.xmlsoap.org/soap/http')
351    {
352        $soap_binding = $this->_dom->createElement('soap:binding');
353        $soap_binding->setAttribute('style', $style);
354        $soap_binding->setAttribute('transport', $transport);
355
356        $binding->appendChild($soap_binding);
357
358        return $soap_binding;
359    }
360
361    /**
362     * Add a {@link http://www.w3.org/TR/wsdl#_soap:operation SOAP operation} to an operation element
363     *
364     * @param object $operation An operation XML_Tree_Node returned by {@link function addBindingOperation}
365     * @param string $soap_action SOAP Action
366     * @return boolean
367     */
368    public function addSoapOperation($binding, $soap_action)
369    {
370        if ($soap_action instanceof Zend_Uri_Http) {
371            $soap_action = $soap_action->getUri();
372        }
373        $soap_operation = $this->_dom->createElement('soap:operation');
374        $soap_operation->setAttribute('soapAction', $soap_action);
375
376        $binding->insertBefore($soap_operation, $binding->firstChild);
377
378        return $soap_operation;
379    }
380
381    /**
382     * Add a {@link http://www.w3.org/TR/wsdl#_services service} element to the WSDL
383     *
384     * @param string $name Service Name
385     * @param string $port_name Name of the port for the service
386     * @param string $binding Binding for the port
387     * @param string $location SOAP Address for the service
388     * @return object The new service's XML_Tree_Node for use with {@link function addDocumentation}
389     */
390    public function addService($name, $port_name, $binding, $location)
391    {
392        if ($location instanceof Zend_Uri_Http) {
393            $location = $location->getUri();
394        }
395        $service = $this->_dom->createElement('service');
396        $service->setAttribute('name', $name);
397
398        $port = $this->_dom->createElement('port');
399        $port->setAttribute('name', $port_name);
400        $port->setAttribute('binding', $binding);
401
402        $soap_address = $this->_dom->createElement('soap:address');
403        $soap_address->setAttribute('location', $location);
404
405        $port->appendChild($soap_address);
406        $service->appendChild($port);
407
408        $this->_wsdl->appendChild($service);
409
410        return $service;
411    }
412
413    /**
414     * Add a documentation element to any element in the WSDL.
415     *
416     * Note that the WSDL {@link http://www.w3.org/TR/wsdl#_documentation specification} uses 'document',
417     * but the WSDL {@link http://schemas.xmlsoap.org/wsdl/ schema} uses 'documentation' instead.
418     * 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'.
419     *
420     * @param object $input_node An XML_Tree_Node returned by another method to add the documentation to
421     * @param string $documentation Human readable documentation for the node
422     * @return DOMElement The documentation element
423     */
424    public function addDocumentation($input_node, $documentation)
425    {
426        if ($input_node === $this) {
427            $node = $this->_dom->documentElement;
428        } else {
429            $node = $input_node;
430        }
431
432        $doc = $this->_dom->createElement('documentation');
433        $doc_cdata = $this->_dom->createTextNode(str_replace(array("\r\n", "\r"), "\n", $documentation));
434        $doc->appendChild($doc_cdata);
435
436        if($node->hasChildNodes()) {
437            $node->insertBefore($doc, $node->firstChild);
438        } else {
439            $node->appendChild($doc);
440        }
441
442        return $doc;
443    }
444
445    /**
446     * Add WSDL Types element
447     *
448     * @param object $types A DomDocument|DomNode|DomElement|DomDocumentFragment with all the XML Schema types defined in it
449     */
450    public function addTypes($types)
451    {
452        if ($types instanceof DomDocument) {
453            $dom = $this->_dom->importNode($types->documentElement);
454            $this->_wsdl->appendChild($types->documentElement);
455        } elseif ($types instanceof DomNode || $types instanceof DomElement || $types instanceof DomDocumentFragment ) {
456            $dom = $this->_dom->importNode($types);
457            $this->_wsdl->appendChild($dom);
458        }
459    }
460
461    /**
462     * Add a complex type name that is part of this WSDL and can be used in signatures.
463     *
464     * @param string $type
465     * @return Zend_Soap_Wsdl
466     */
467    public function addType($type)
468    {
469        if(!in_array($type, $this->_includedTypes)) {
470            $this->_includedTypes[] = $type;
471        }
472        return $this;
473    }
474
475    /**
476     * Return an array of all currently included complex types
477     *
478     * @return array
479     */
480    public function getTypes()
481    {
482        return $this->_includedTypes;
483    }
484
485    /**
486     * Return the Schema node of the WSDL
487     *
488     * @return DOMElement
489     */
490    public function getSchema()
491    {
492        if($this->_schema == null) {
493            $this->addSchemaTypeSection();
494        }
495
496        return $this->_schema;
497    }
498
499    /**
500     * Return the WSDL as XML
501     *
502     * @return string WSDL as XML
503     */
504    public function toXML()
505    {
506           return $this->_dom->saveXML();
507    }
508
509    /**
510     * Return DOM Document
511     *
512     * @return object DomDocum ent
513     */
514    public function toDomDocument()
515    {
516        return $this->_dom;
517    }
518
519    /**
520     * Echo the WSDL as XML
521     *
522     * @return boolean
523     */
524    public function dump($filename = false)
525    {
526        if (!$filename) {
527            echo $this->toXML();
528            return true;
529        } else {
530            return file_put_contents($filename, $this->toXML());
531        }
532    }
533
534    /**
535     * Returns an XSD Type for the given PHP type
536     *
537     * @param string $type PHP Type to get the XSD type for
538     * @return string
539     */
540    public function getType($type)
541    {
542        switch (strtolower($type)) {
543            case 'string':
544            case 'str':
545                return 'xsd:string';
546            case 'long':
547                return 'xsd:long';
548            case 'int':
549            case 'integer':
550                return 'xsd:int';
551            case 'float':
552            case 'double':
553                return 'xsd:float';
554            case 'boolean':
555            case 'bool':
556                return 'xsd:boolean';
557            case 'array':
558                return 'soap-enc:Array';
559            case 'object':
560                return 'xsd:struct';
561            case 'mixed':
562                return 'xsd:anyType';
563            case 'void':
564                return '';
565            default:
566                // delegate retrieval of complex type to current strategy
567                return $this->addComplexType($type);
568            }
569    }
570
571    /**
572     * This function makes sure a complex types section and schema additions are set.
573     *
574     * @return Zend_Soap_Wsdl
575     */
576    public function addSchemaTypeSection()
577    {
578        if ($this->_schema === null) {
579            $this->_schema = $this->_dom->createElement('xsd:schema');
580            $this->_schema->setAttribute('targetNamespace', $this->_uri);
581            $types = $this->_dom->createElement('types');
582            $types->appendChild($this->_schema);
583            $this->_wsdl->appendChild($types);
584        }
585        return $this;
586    }
587
588    /**
589     * Add a {@link http://www.w3.org/TR/wsdl#_types types} data type definition
590     *
591     * @param string $type Name of the class to be specified
592     * @return string XSD Type for the given PHP type
593     */
594    public function addComplexType($type)
595    {
596        if (in_array($type, $this->getTypes())) {
597            return "tns:$type";
598        }
599        $this->addSchemaTypeSection();
600
601        $strategy = $this->getComplexTypeStrategy();
602        $strategy->setContext($this);
603        // delegates the detection of a complex type to the current strategy
604        return $strategy->addComplexType($type);
605    }
606
607    /**
608     * Parse an xsd:element represented as an array into a DOMElement.
609     *
610     * @param array $element an xsd:element represented as an array
611     * @return DOMElement parsed element
612     */
613    private function _parseElement($element)
614    {
615        if (!is_array($element)) {
616            require_once "Zend/Soap/Wsdl/Exception.php";
617            throw new Zend_Soap_Wsdl_Exception("The 'element' parameter needs to be an associative array.");
618        }
619
620        $elementXml = $this->_dom->createElement('xsd:element');
621        foreach ($element as $key => $value) {
622            if (in_array($key, array('sequence', 'all', 'choice'))) {
623                if (is_array($value)) {
624                    $complexType = $this->_dom->createElement('xsd:complexType');
625                    if (count($value) > 0) {
626                        $container = $this->_dom->createElement('xsd:' . $key);
627                        foreach ($value as $subelement) {
628                            $subelementXml = $this->_parseElement($subelement);
629                            $container->appendChild($subelementXml);
630                        }
631                        $complexType->appendChild($container);
632                    }
633                    $elementXml->appendChild($complexType);
634                }
635            } else {
636                $elementXml->setAttribute($key, $value);
637            }
638        }
639        return $elementXml;
640    }
641
642    /**
643     * Add an xsd:element represented as an array to the schema.
644     *
645     * Array keys represent attribute names and values their respective value.
646     * The 'sequence', 'all' and 'choice' keys must have an array of elements as their value,
647     * to add them to a nested complexType.
648     *
649     * Example: array( 'name' => 'MyElement',
650     *                 'sequence' => array( array('name' => 'myString', 'type' => 'string'),
651     *                                      array('name' => 'myInteger', 'type' => 'int') ) );
652     * Resulting XML: <xsd:element name="MyElement"><xsd:complexType><xsd:sequence>
653     *                  <xsd:element name="myString" type="string"/>
654     *                  <xsd:element name="myInteger" type="int"/>
655     *                </xsd:sequence></xsd:complexType></xsd:element>
656     *
657     * @param array $element an xsd:element represented as an array
658     * @return string xsd:element for the given element array
659     */
660    public function addElement($element)
661    {
662        $schema = $this->getSchema();
663        $elementXml = $this->_parseElement($element);
664        $schema->appendChild($elementXml);
665        return 'tns:' . $element['name'];
666    }
667}