File indexing completed on 2024-05-12 06:03: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  * @subpackage AutoDiscover
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Server_Interface
0025  */
0026 // require_once 'Zend/Server/Interface.php';
0027 /**
0028  * @see Zend_Soap_Wsdl
0029  */
0030 // require_once 'Zend/Soap/Wsdl.php';
0031 /**
0032  * @see Zend_Server_Reflection
0033  */
0034 // require_once 'Zend/Server/Reflection.php';
0035 /**
0036  * @see Zend_Server_Abstract
0037  */
0038 // require_once 'Zend/Server/Abstract.php';
0039 /**
0040  * @see Zend_Uri
0041  */
0042 // require_once 'Zend/Uri.php';
0043 
0044 /**
0045  * Zend_Soap_AutoDiscover
0046  *
0047  * @category   Zend
0048  * @package    Zend_Soap
0049  * @subpackage AutoDiscover
0050  */
0051 class Zend_Soap_AutoDiscover implements Zend_Server_Interface
0052 {
0053     /**
0054      * @var Zend_Soap_Wsdl
0055      */
0056     protected $_wsdl = null;
0057 
0058     /**
0059      * @var Zend_Server_Reflection
0060      */
0061     protected $_reflection = null;
0062 
0063     /**
0064      * @var array
0065      */
0066     protected $_functions = array();
0067 
0068     /**
0069      * @var boolean
0070      */
0071     protected $_strategy;
0072 
0073     /**
0074      * Url where the WSDL file will be available at.
0075      *
0076      * @var WSDL Uri
0077      */
0078     protected $_uri;
0079 
0080     /**
0081      * soap:body operation style options
0082      *
0083      * @var array
0084      */
0085     protected $_operationBodyStyle = array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/");
0086 
0087     /**
0088      * soap:operation style
0089      *
0090      * @var array
0091      */
0092     protected $_bindingStyle = array('style' => 'rpc', 'transport' => 'http://schemas.xmlsoap.org/soap/http');
0093 
0094     /**
0095      * Name of the class to handle the WSDL creation.
0096      *
0097      * @var string
0098      */
0099     protected $_wsdlClass = 'Zend_Soap_Wsdl';
0100 
0101     /**
0102      * Constructor
0103      *
0104      * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
0105      * @param string|Zend_Uri $uri
0106      * @param string $wsdlClass
0107      */
0108     public function __construct($strategy = true, $uri=null, $wsdlClass=null)
0109     {
0110         $this->_reflection = new Zend_Server_Reflection();
0111         $this->setComplexTypeStrategy($strategy);
0112 
0113         if($uri !== null) {
0114             $this->setUri($uri);
0115         }
0116 
0117         if($wsdlClass !== null) {
0118             $this->setWsdlClass($wsdlClass);
0119         }
0120     }
0121 
0122     /**
0123      * Set the location at which the WSDL file will be availabe.
0124      *
0125      * @see Zend_Soap_Exception
0126      * @param  Zend_Uri|string $uri
0127      * @return Zend_Soap_AutoDiscover
0128      * @throws Zend_Soap_AutoDiscover_Exception
0129      */
0130     public function setUri($uri)
0131     {
0132         if (!is_string($uri) && !($uri instanceof Zend_Uri)) {
0133             // require_once "Zend/Soap/AutoDiscover/Exception.php";
0134             throw new Zend_Soap_AutoDiscover_Exception("No uri given to Zend_Soap_AutoDiscover::setUri as string or Zend_Uri instance.");
0135         }
0136         $this->_uri = $uri;
0137 
0138         // change uri in WSDL file also if existant
0139         if ($this->_wsdl instanceof Zend_Soap_Wsdl) {
0140             $this->_wsdl->setUri($uri);
0141         }
0142 
0143         return $this;
0144     }
0145 
0146     /**
0147      * Return the current Uri that the SOAP WSDL Service will be located at.
0148      *
0149      * @return Zend_Uri
0150      */
0151     public function getUri()
0152     {
0153         if($this->_uri !== null) {
0154             $uri = $this->_uri;
0155         } else {
0156             $schema     = $this->getSchema();
0157             $host       = $this->getHostName();
0158             $scriptName = $this->getRequestUriWithoutParameters();
0159             $uri = Zend_Uri::factory($schema . '://' . $host . $scriptName);
0160             $this->setUri($uri);
0161         }
0162         return $uri;
0163     }
0164 
0165     /**
0166      * Set the name of the WSDL handling class.
0167      *
0168      * @see Zend_Soap_Exception
0169      * @see Zend_Soap_Exception
0170      * @param  string $wsdlClass
0171      * @return Zend_Soap_AutoDiscover
0172      * @throws Zend_Soap_AutoDiscover_Exception
0173      */
0174     public function setWsdlClass($wsdlClass)
0175     {
0176         if (!is_string($wsdlClass) && !is_subclass_of($wsdlClass, 'Zend_Soap_Wsdl')) {
0177             // require_once "Zend/Soap/AutoDiscover/Exception.php";
0178             throw new Zend_Soap_AutoDiscover_Exception("No Zend_Soap_Wsdl subclass given to Zend_Soap_AutoDiscover::setWsdlClass as string.");
0179         }
0180         $this->_wsdlClass = $wsdlClass;
0181 
0182         return $this;
0183     }
0184 
0185     /**
0186      * Return the name of the WSDL handling class.
0187      *
0188      * @return string
0189      */
0190     public function getWsdlClass()
0191     {
0192         return $this->_wsdlClass;
0193     }
0194 
0195     /**
0196      * Set options for all the binding operations soap:body elements.
0197      *
0198      * By default the options are set to 'use' => 'encoded' and
0199      * 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/".
0200      *
0201      * @see    Zend_Soap_AutoDiscover_Exception
0202      * @param  array $operationStyle
0203      * @return Zend_Soap_AutoDiscover
0204      * @throws Zend_Soap_AutoDiscover_Exception
0205      */
0206     public function setOperationBodyStyle(array $operationStyle=array())
0207     {
0208         if(!isset($operationStyle['use'])) {
0209             // require_once "Zend/Soap/AutoDiscover/Exception.php";
0210             throw new Zend_Soap_AutoDiscover_Exception("Key 'use' is required in Operation soap:body style.");
0211         }
0212         $this->_operationBodyStyle = $operationStyle;
0213         return $this;
0214     }
0215 
0216     /**
0217      * Set Binding soap:binding style.
0218      *
0219      * By default 'style' is 'rpc' and 'transport' is 'http://schemas.xmlsoap.org/soap/http'.
0220      *
0221      * @param  array $bindingStyle
0222      * @return Zend_Soap_AutoDiscover
0223      */
0224     public function setBindingStyle(array $bindingStyle=array())
0225     {
0226         if(isset($bindingStyle['style'])) {
0227             $this->_bindingStyle['style'] = $bindingStyle['style'];
0228         }
0229         if(isset($bindingStyle['transport'])) {
0230             $this->_bindingStyle['transport'] = $bindingStyle['transport'];
0231         }
0232         return $this;
0233     }
0234 
0235     /**
0236      * Detect and returns the current HTTP/HTTPS Schema
0237      *
0238      * @return string
0239      */
0240     protected function getSchema()
0241     {
0242         $schema = "http";
0243         if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
0244             $schema = 'https';
0245         }
0246         return $schema;
0247     }
0248 
0249     /**
0250      * Detect and return the current hostname
0251      *
0252      * @return string
0253      */
0254     protected function getHostName()
0255     {
0256         if(isset($_SERVER['HTTP_HOST'])) {
0257             $host = $_SERVER['HTTP_HOST'];
0258         } else {
0259             $host = $_SERVER['SERVER_NAME'];
0260         }
0261         return $host;
0262     }
0263 
0264     /**
0265      * Detect and return the current script name without parameters
0266      *
0267      * @return string
0268      */
0269     protected function getRequestUriWithoutParameters()
0270     {
0271         if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) { // IIS with Microsoft Rewrite Module
0272             $requestUri = $_SERVER['HTTP_X_ORIGINAL_URL'];
0273         } elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // check this first so IIS will catch
0274             $requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
0275         } elseif (isset($_SERVER['REQUEST_URI'])) {
0276             $requestUri = $_SERVER['REQUEST_URI'];
0277         } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0, PHP as CGI
0278             $requestUri = $_SERVER['ORIG_PATH_INFO'];
0279         } else {
0280             $requestUri = $_SERVER['SCRIPT_NAME'];
0281         }
0282         if( ($pos = strpos($requestUri, "?")) !== false) {
0283             $requestUri = substr($requestUri, 0, $pos);
0284         }
0285 
0286         return $requestUri;
0287     }
0288 
0289     /**
0290      * Set the strategy that handles functions and classes that are added AFTER this call.
0291      *
0292      * @param  boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
0293      * @return Zend_Soap_AutoDiscover
0294      */
0295     public function setComplexTypeStrategy($strategy)
0296     {
0297         $this->_strategy = $strategy;
0298         if($this->_wsdl instanceof  Zend_Soap_Wsdl) {
0299             $this->_wsdl->setComplexTypeStrategy($strategy);
0300         }
0301 
0302         return $this;
0303     }
0304 
0305     /**
0306      * Set the Class the SOAP server will use
0307      *
0308      * @param string $class Class Name
0309      * @param string $namespace Class Namspace - Not Used
0310      * @param array $argv Arguments to instantiate the class - Not Used
0311      * @return Zend_Soap_AutoDiscover
0312      */
0313     public function setClass($class, $namespace = '', $argv = null)
0314     {
0315         $uri = $this->getUri();
0316 
0317         $wsdl = new $this->_wsdlClass($class, $uri, $this->_strategy);
0318 
0319         // The wsdl:types element must precede all other elements (WS-I Basic Profile 1.1 R2023)
0320         $wsdl->addSchemaTypeSection();
0321 
0322         $port = $wsdl->addPortType($class . 'Port');
0323         $binding = $wsdl->addBinding($class . 'Binding', 'tns:' .$class. 'Port');
0324 
0325         $wsdl->addSoapBinding($binding, $this->_bindingStyle['style'], $this->_bindingStyle['transport']);
0326         $wsdl->addService($class . 'Service', $class . 'Port', 'tns:' . $class . 'Binding', $uri);
0327         foreach ($this->_reflection->reflectClass($class)->getMethods() as $method) {
0328             $this->_addFunctionToWsdl($method, $wsdl, $port, $binding);
0329         }
0330         $this->_wsdl = $wsdl;
0331 
0332         return $this;
0333     }
0334 
0335     /**
0336      * Add a Single or Multiple Functions to the WSDL
0337      *
0338      * @param string $function Function Name
0339      * @param string $namespace Function namespace - Not Used
0340      * @return Zend_Soap_AutoDiscover
0341      */
0342     public function addFunction($function, $namespace = '')
0343     {
0344         static $port;
0345         static $operation;
0346         static $binding;
0347 
0348         if (!is_array($function)) {
0349             $function = (array) $function;
0350         }
0351 
0352         $uri = $this->getUri();
0353 
0354         if (!($this->_wsdl instanceof Zend_Soap_Wsdl)) {
0355             $parts = explode('.', basename($_SERVER['SCRIPT_NAME']));
0356             $name = $parts[0];
0357             $wsdl = new Zend_Soap_Wsdl($name, $uri, $this->_strategy);
0358 
0359             // The wsdl:types element must precede all other elements (WS-I Basic Profile 1.1 R2023)
0360             $wsdl->addSchemaTypeSection();
0361 
0362             $port = $wsdl->addPortType($name . 'Port');
0363             $binding = $wsdl->addBinding($name . 'Binding', 'tns:' .$name. 'Port');
0364 
0365             $wsdl->addSoapBinding($binding, $this->_bindingStyle['style'], $this->_bindingStyle['transport']);
0366             $wsdl->addService($name . 'Service', $name . 'Port', 'tns:' . $name . 'Binding', $uri);
0367         } else {
0368             $wsdl = $this->_wsdl;
0369         }
0370 
0371         foreach ($function as $func) {
0372             $method = $this->_reflection->reflectFunction($func);
0373             $this->_addFunctionToWsdl($method, $wsdl, $port, $binding);
0374         }
0375         $this->_wsdl = $wsdl;
0376 
0377         return $this;
0378     }
0379 
0380     /**
0381      * Add a function to the WSDL document.
0382      *
0383      * @param Zend_Server_Reflection_Function_Abstract $function function to add
0384      * @param Zend_Soap_Wsdl                           $wsdl     WSDL document
0385      * @param object                                   $port     wsdl:portType
0386      * @param object                                   $binding  wsdl:binding
0387      * @return void
0388      */
0389     protected function _addFunctionToWsdl($function, $wsdl, $port, $binding)
0390     {
0391         $uri = $this->getUri();
0392 
0393         // We only support one prototype: the one with the maximum number of arguments
0394         $prototype = null;
0395         $maxNumArgumentsOfPrototype = -1;
0396         foreach ($function->getPrototypes() as $tmpPrototype) {
0397             $numParams = count($tmpPrototype->getParameters());
0398             if ($numParams > $maxNumArgumentsOfPrototype) {
0399                 $maxNumArgumentsOfPrototype = $numParams;
0400                 $prototype = $tmpPrototype;
0401             }
0402         }
0403         if ($prototype === null) {
0404             // require_once "Zend/Soap/AutoDiscover/Exception.php";
0405             throw new Zend_Soap_AutoDiscover_Exception("No prototypes could be found for the '" . $function->getName() . "' function");
0406         }
0407 
0408         // Add the input message (parameters)
0409         $args = array();
0410         if ($this->_bindingStyle['style'] == 'document') {
0411             // Document style: wrap all parameters in a sequence element
0412             $sequence = array();
0413             foreach ($prototype->getParameters() as $param) {
0414                 $sequenceElement = array(
0415                     'name' => $param->getName(),
0416                     'type' => $wsdl->getType($param->getType())
0417                 );
0418                 if ($param->isOptional()) {
0419                     $sequenceElement['nillable'] = 'true';
0420                 }
0421                 $sequence[] = $sequenceElement;
0422             }
0423             $element = array(
0424                 'name' => $function->getName(),
0425                 'sequence' => $sequence
0426             );
0427             // Add the wrapper element part, which must be named 'parameters'
0428             $args['parameters'] = array('element' => $wsdl->addElement($element));
0429         } else {
0430             // RPC style: add each parameter as a typed part
0431             foreach ($prototype->getParameters() as $param) {
0432                 $args[$param->getName()] = array('type' => $wsdl->getType($param->getType()));
0433             }
0434         }
0435         $wsdl->addMessage($function->getName() . 'In', $args);
0436 
0437         $isOneWayMessage = false;
0438         if($prototype->getReturnType() == "void") {
0439             $isOneWayMessage = true;
0440         }
0441 
0442         if($isOneWayMessage == false) {
0443             // Add the output message (return value)
0444             $args = array();
0445             if ($this->_bindingStyle['style'] == 'document') {
0446                 // Document style: wrap the return value in a sequence element
0447                 $sequence = array();
0448                 if ($prototype->getReturnType() != "void") {
0449                     $sequence[] = array(
0450                         'name' => $function->getName() . 'Result',
0451                         'type' => $wsdl->getType($prototype->getReturnType())
0452                     );
0453                 }
0454                 $element = array(
0455                     'name' => $function->getName() . 'Response',
0456                     'sequence' => $sequence
0457                 );
0458                 // Add the wrapper element part, which must be named 'parameters'
0459                 $args['parameters'] = array('element' => $wsdl->addElement($element));
0460             } else if ($prototype->getReturnType() != "void") {
0461                 // RPC style: add the return value as a typed part
0462                 $args['return'] = array('type' => $wsdl->getType($prototype->getReturnType()));
0463             }
0464             $wsdl->addMessage($function->getName() . 'Out', $args);
0465         }
0466 
0467         // Add the portType operation
0468         if($isOneWayMessage == false) {
0469             $portOperation = $wsdl->addPortOperation($port, $function->getName(), 'tns:' . $function->getName() . 'In', 'tns:' . $function->getName() . 'Out');
0470         } else {
0471             $portOperation = $wsdl->addPortOperation($port, $function->getName(), 'tns:' . $function->getName() . 'In', false);
0472         }
0473         $desc = $function->getDescription();
0474         if (strlen($desc) > 0) {
0475             $wsdl->addDocumentation($portOperation, $desc);
0476         }
0477 
0478         // When using the RPC style, make sure the operation style includes a 'namespace' attribute (WS-I Basic Profile 1.1 R2717)
0479         if ($this->_bindingStyle['style'] == 'rpc' && !isset($this->_operationBodyStyle['namespace'])) {
0480             $this->_operationBodyStyle['namespace'] = ''.$uri;
0481         }
0482 
0483         // Add the binding operation
0484         if($isOneWayMessage == false) {
0485             $operation = $wsdl->addBindingOperation($binding, $function->getName(),  $this->_operationBodyStyle, $this->_operationBodyStyle);
0486         } else {
0487             $operation = $wsdl->addBindingOperation($binding, $function->getName(),  $this->_operationBodyStyle);
0488         }
0489         $wsdl->addSoapOperation($operation, $uri . '#' .$function->getName());
0490 
0491         // Add the function name to the list
0492         $this->_functions[] = $function->getName();
0493     }
0494 
0495     /**
0496      * Action to take when an error occurs
0497      *
0498      * @param string $fault
0499      * @param string|int $code
0500      * @throws Zend_Soap_AutoDiscover_Exception
0501      */
0502     public function fault($fault = null, $code = null)
0503     {
0504         // require_once "Zend/Soap/AutoDiscover/Exception.php";
0505         throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover.");
0506     }
0507 
0508     /**
0509      * Handle the Request
0510      *
0511      * @param string $request A non-standard request - Not Used
0512      */
0513     public function handle($request = false)
0514     {
0515         if (!headers_sent()) {
0516             header('Content-Type: text/xml');
0517         }
0518         $this->_wsdl->dump();
0519     }
0520 
0521     /**
0522      * Proxy to WSDL dump function
0523      *
0524      * @param string $filename
0525      * @return boolean
0526      * @throws Zend_Soap_AutoDiscover_Exception
0527      */
0528     public function dump($filename)
0529     {
0530         if($this->_wsdl !== null) {
0531             return $this->_wsdl->dump($filename);
0532         } else {
0533             /**
0534              * @see Zend_Soap_AutoDiscover_Exception
0535              */
0536             // require_once "Zend/Soap/AutoDiscover/Exception.php";
0537             throw new Zend_Soap_AutoDiscover_Exception("Cannot dump autodiscovered contents, WSDL file has not been generated yet.");
0538         }
0539     }
0540 
0541     /**
0542      * Proxy to WSDL toXml() function
0543      *
0544      * @return string
0545      * @throws Zend_Soap_AutoDiscover_Exception
0546      */
0547     public function toXml()
0548     {
0549         if($this->_wsdl !== null) {
0550             return $this->_wsdl->toXml();
0551         } else {
0552             /**
0553              * @see Zend_Soap_AutoDiscover_Exception
0554              */
0555             // require_once "Zend/Soap/AutoDiscover/Exception.php";
0556             throw new Zend_Soap_AutoDiscover_Exception("Cannot return autodiscovered contents, WSDL file has not been generated yet.");
0557         }
0558     }
0559 
0560     /**
0561      * Return an array of functions in the WSDL
0562      *
0563      * @return array
0564      */
0565     public function getFunctions()
0566     {
0567         return $this->_functions;
0568     }
0569 
0570     /**
0571      * Load Functions
0572      *
0573      * @param unknown_type $definition
0574      * @throws Zend_Soap_AutoDiscover_Exception
0575      */
0576     public function loadFunctions($definition)
0577     {
0578         // require_once "Zend/Soap/AutoDiscover/Exception.php";
0579         throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover.");
0580     }
0581 
0582     /**
0583      * Set Persistance
0584      *
0585      * @param int $mode
0586      * @throws Zend_Soap_AutoDiscover_Exception
0587      */
0588     public function setPersistence($mode)
0589     {
0590         // require_once "Zend/Soap/AutoDiscover/Exception.php";
0591         throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover.");
0592     }
0593 
0594     /**
0595      * Returns an XSD Type for the given PHP type
0596      *
0597      * @param string $type PHP Type to get the XSD type for
0598      * @return string
0599      */
0600     public function getType($type)
0601     {
0602         if (!($this->_wsdl instanceof Zend_Soap_Wsdl)) {
0603             /** @todo Exception throwing may be more correct */
0604 
0605             // WSDL is not defined yet, so we can't recognize type in context of current service
0606             return '';
0607         } else {
0608             return $this->_wsdl->getType($type);
0609         }
0610     }
0611 }