]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/Web/Services/TSoapService.php
c928dc06656627f4d7ccc00ac359e687e891e95a
[bacula/bacula] / gui / baculum / framework / Web / Services / TSoapService.php
1 <?php
2 /**
3  * TSoapService and TSoapServer class file
4  *
5  * @author Knut Urdalen <knut.urdalen@gmail.com>
6  * @author Qiang Xue <qiang.xue@gmail.com>
7  * @link http://www.pradosoft.com/
8  * @copyright Copyright &copy; 2005-2014 PradoSoft
9  * @license http://www.pradosoft.com/license/
10  * @package System.Web.Services
11  */
12
13 /**
14  * TSoapService class
15  *
16  * TSoapService processes SOAP requests for a PRADO application.
17  * TSoapService requires PHP SOAP extension to be loaded.
18  *
19  * TSoapService manages a set of SOAP providers. Each SOAP provider
20  * is a class that implements a set of SOAP methods which are exposed
21  * to SOAP clients for remote invocation. TSoapService generates WSDL
22  * automatically for the SOAP providers by default.
23  *
24  * To use TSoapService, configure it in the application specification like following:
25  * <code>
26  *   <services>
27  *     <service id="soap" class="System.Web.Services.TSoapService">
28  *       <soap id="stockquote" provider="MyStockQuote" />
29  *     </service>
30  *   </services>
31  * </code>
32  * PHP configuration style:
33  * <code>
34  *  'services' => array(
35  *    'soap' => array(
36  *     'class' => 'System.Web.Services.TSoapService'
37  *     'properties' => array(
38  *       'provider' => 'MyStockQuote'
39  *         )
40  *    )
41  *  )
42  * </code>
43  *
44  * The WSDL for the provider class "MyStockQuote" is generated based on special
45  * comment tags in the class. In particular, if a class method's comment
46  * contains the keyword "@soapmethod", it is considered to be a SOAP method
47  * and will be exposed to SOAP clients. For example,
48  * <code>
49  *   class MyStockQuote {
50  *      / **
51  *       * @param string $symbol the stock symbol
52  *       * @return float the stock price
53  *       * @soapmethod
54  *       * /
55  *      public function getQuote($symbol) {...}
56  *   }
57  * </code>
58  *
59  * With the above SOAP provider, a typical SOAP client may call the method "getQuote"
60  * remotely like the following:
61  * <code>
62  *   $client=new SoapClient("http://hostname/path/to/index.php?soap=stockquote.wsdl");
63  *   echo $client->getQuote("ibm");
64  * </code>
65  *
66  * Each <soap> element in the application specification actually configures
67  * the properties of a SOAP server which defaults to {@link TSoapServer}.
68  * Therefore, any writable property of {@link TSoapServer} may appear as an attribute
69  * in the <soap> element. For example, the "provider" attribute refers to
70  * the {@link TSoapServer::setProvider Provider} property of {@link TSoapServer}.
71  * The following configuration specifies that the SOAP server is persistent within
72  * the user session (that means a MyStockQuote object will be stored in session)
73  * <code>
74  *   <services>
75  *     <service id="soap" class="System.Web.Services.TSoapService">
76  *       <soap id="stockquote" provider="MyStockQuote" SessionPersistent="true" />
77  *     </service>
78  *   </services>
79  * </code>
80  *
81  * You may also use your own SOAP server class by specifying the "class" attribute of <soap>.
82  *
83  * @author Knut Urdalen <knut.urdalen@gmail.com>
84  * @author Qiang Xue <qiang.xue@gmail.com>
85  * @author Carl G. Mathisen <carlgmathisen@gmail.com>
86  * @package System.Web.Services
87  * @since 3.1
88  */
89 class TSoapService extends TService
90 {
91         const DEFAULT_SOAP_SERVER='TSoapServer';
92         private $_servers=array();
93         private $_configFile=null;
94         private $_wsdlRequest=false;
95         private $_serverID=null;
96
97         /**
98          * Constructor.
99          * Sets default service ID to 'soap'.
100          */
101         public function __construct()
102         {
103                 $this->setID('soap');
104         }
105
106         /**
107          * Initializes this module.
108          * This method is required by the IModule interface.
109          * @param TXmlElement configuration for this module, can be null
110          * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid.
111          */
112         public function init($config)
113         {
114                 if($this->_configFile!==null)
115                 {
116                         if(is_file($this->_configFile))
117                         {
118                                 $dom=new TXmlDocument;
119                                 $dom->loadFromFile($this->_configFile);
120                                 $this->loadConfig($dom);
121                         }
122                         else
123                                 throw new TConfigurationException('soapservice_configfile_invalid',$this->_configFile);
124                 }
125                 $this->loadConfig($config);
126
127                 $this->resolveRequest();
128         }
129
130         /**
131          * Resolves the request parameter.
132          * It identifies the server ID and whether the request is for WSDL.
133          * @throws THttpException if the server ID cannot be found
134          * @see getServerID
135          * @see getIsWsdlRequest
136          */
137         protected function resolveRequest()
138         {
139                 $serverID=$this->getRequest()->getServiceParameter();
140                 if(($pos=strrpos($serverID,'.wsdl'))===strlen($serverID)-5)
141                 {
142                         $serverID=substr($serverID,0,$pos);
143                         $this->_wsdlRequest=true;
144                 }
145                 else
146                         $this->_wsdlRequest=false;
147                 $this->_serverID=$serverID;
148                 if(!isset($this->_servers[$serverID]))
149                         throw new THttpException(400,'soapservice_request_invalid',$serverID);
150         }
151
152         /**
153          * Loads configuration from an XML element
154          * @param mixed configuration node
155          * @throws TConfigurationException if soap server id is not specified or duplicated
156          */
157         private function loadConfig($config)
158         {
159                 if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
160                 {
161                         if(is_array($config))
162                         {
163                                 foreach($config['soap'] as $id => $server)
164                                 {
165                                         $properties = isset($server['properties'])?$server['properties']:array();
166                                         if(isset($this->_servers[$id]))
167                                                 throw new TConfigurationException('soapservice_serverid_duplicated',$id);
168                                         $this->_servers[$id]=$properties;
169                                 }
170                         }
171                 }
172                 else
173                 {
174                         foreach($config->getElementsByTagName('soap') as $serverXML)
175                         {
176                                 $properties=$serverXML->getAttributes();
177                                 if(($id=$properties->remove('id'))===null)
178                                         throw new TConfigurationException('soapservice_serverid_required');
179                                 if(isset($this->_servers[$id]))
180                                         throw new TConfigurationException('soapservice_serverid_duplicated',$id);
181                                 $this->_servers[$id]=$properties;
182                         }
183                 }
184         }
185
186         /**
187          * @return string external configuration file. Defaults to null.
188          */
189         public function getConfigFile()
190         {
191                 return $this->_configFile;
192         }
193
194         /**
195          * @param string external configuration file in namespace format. The file
196          * must be suffixed with '.xml'.
197          * @throws TInvalidDataValueException if the file is invalid.
198          */
199         public function setConfigFile($value)
200         {
201                 if(($this->_configFile=Prado::getPathOfNamespace($value,Prado::getApplication()->getConfigurationFileExt()))===null)
202                         throw new TConfigurationException('soapservice_configfile_invalid',$value);
203         }
204
205         /**
206          * Constructs a URL with specified page path and GET parameters.
207          * @param string soap server ID
208          * @param array list of GET parameters, null if no GET parameters required
209          * @param boolean whether to encode the ampersand in URL, defaults to true.
210          * @param boolean whether to encode the GET parameters (their names and values), defaults to true.
211          * @return string URL for the page and GET parameters
212          */
213         public function constructUrl($serverID,$getParams=null,$encodeAmpersand=true,$encodeGetItems=true)
214         {
215                 return $this->getRequest()->constructUrl($this->getID(),$serverID,$getParams,$encodeAmpersand,$encodeGetItems);
216         }
217
218         /**
219          * @return boolean whether this is a request for WSDL
220          */
221         public function getIsWsdlRequest()
222         {
223                 return $this->_wsdlRequest;
224         }
225
226         /**
227          * @return string the SOAP server ID
228          */
229         public function getServerID()
230         {
231                 return $this->_serverID;
232         }
233
234         /**
235          * Creates the requested SOAP server.
236          * The SOAP server is initialized with the property values specified
237          * in the configuration.
238          * @return TSoapServer the SOAP server instance
239          */
240         protected function createServer()
241         {
242                 $properties=$this->_servers[$this->_serverID];
243                 $serverClass=null;
244                 if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP && isset($config['class']))
245                         $serverClass=$config['class'];
246                 else if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_XML)
247                         $serverClass=$properties->remove('class');
248                 if($serverClass===null)
249                         $serverClass=self::DEFAULT_SOAP_SERVER;
250                 Prado::using($serverClass);
251                 $className=($pos=strrpos($serverClass,'.'))!==false?substr($serverClass,$pos+1):$serverClass;
252                 if($className!==self::DEFAULT_SOAP_SERVER && !is_subclass_of($className,self::DEFAULT_SOAP_SERVER))
253                         throw new TConfigurationException('soapservice_server_invalid',$serverClass);
254                 $server=new $className;
255                 $server->setID($this->_serverID);
256                 foreach($properties as $name=>$value)
257                         $server->setSubproperty($name,$value);
258                 return $server;
259         }
260
261         /**
262          * Runs the service.
263          * If the service parameter ends with '.wsdl', it will serve a WSDL file for
264          * the specified soap server.
265          * Otherwise, it will handle the soap request using the specified server.
266          */
267         public function run()
268         {
269                 Prado::trace("Running SOAP service",'System.Web.Services.TSoapService');
270                 $server=$this->createServer();
271                 $this->getResponse()->setContentType('text/xml');
272                 $this->getResponse()->setCharset($server->getEncoding());
273                 if($this->getIsWsdlRequest())
274                 {
275                         // server WSDL file
276                         Prado::trace("Generating WSDL",'System.Web.Services.TSoapService');
277                         $this->getResponse()->clear();
278                         $this->getResponse()->write($server->getWsdl());
279                 }
280                 else
281                 {
282                         // provide SOAP service
283                         Prado::trace("Handling SOAP request",'System.Web.Services.TSoapService');
284                         $server->run();
285                 }
286         }
287 }
288
289
290 /**
291  * TSoapServer class.
292  *
293  * TSoapServer is a wrapper of the PHP SoapServer class.
294  * It associates a SOAP provider class to the SoapServer object.
295  * It also manages the URI for the SOAP service and WSDL.
296  *
297  * @author Qiang Xue <qiang.xue@gmail.com>
298  * @package System.Web.Services
299  * @since 3.1
300  */
301 class TSoapServer extends TApplicationComponent
302 {
303         const WSDL_CACHE_PREFIX='wsdl.';
304
305         private $_id;
306         private $_provider;
307
308         private $_version='';
309         private $_actor='';
310         private $_encoding='';
311         private $_uri='';
312         private $_classMap;
313         private $_persistent=false;
314         private $_wsdlUri='';
315
316         private $_requestedMethod;
317
318         private $_server;
319
320         /**
321          * @return string the ID of the SOAP server
322          */
323         public function getID()
324         {
325                 return $this->_id;
326         }
327
328         /**
329          * @param string the ID of the SOAP server
330          * @throws TInvalidDataValueException if the ID ends with '.wsdl'.
331          */
332         public function setID($id)
333         {
334                 if(strrpos($this->_id,'.wsdl')===strlen($this->_id)-5)
335                         throw new TInvalidDataValueException('soapserver_id_invalid',$id);
336                 $this->_id=$id;
337         }
338
339         /**
340          * Handles the SOAP request.
341          */
342         public function run()
343         {
344                 if(($provider=$this->getProvider())!==null)
345                 {
346                         Prado::using($provider);
347                         $providerClass=($pos=strrpos($provider,'.'))!==false?substr($provider,$pos+1):$provider;
348                         $this->guessMethodCallRequested($providerClass);
349                         $server=$this->createServer();
350                         $server->setClass($providerClass, $this);
351                         if($this->_persistent)
352                                 $server->setPersistence(SOAP_PERSISTENCE_SESSION);
353                 }
354                 else
355                         $server=$this->createServer();
356                 try
357                 {
358                         $server->handle();
359                 }
360                 catch (Exception $e)
361                 {
362                         if($this->getApplication()->getMode()===TApplicationMode::Debug)
363                                 $this->fault($e->getMessage(), $e->__toString());
364                         else
365                                 $this->fault($e->getMessage());
366                 }
367         }
368
369         /**
370          * Generate a SOAP fault message.
371          * @param string message title
372          * @param mixed message details
373          * @param string message code, defalt is 'SERVER'.
374          * @param string actors
375          * @param string message name
376          */
377         public function fault($title, $details='', $code='SERVER', $actor='', $name='')
378         {
379                 Prado::trace('SOAP-Fault '.$code. ' '.$title.' : '.$details, 'System.Web.Services.TSoapService');
380                 $this->_server->fault($code, $title, $actor, $details, $name);
381         }
382
383         /**
384          * Guess the SOAP method request from the actual SOAP message
385          *
386          * @param string $class current handler class.
387          */
388         protected function guessMethodCallRequested($class)
389         {
390                 $namespace = $class.'wsdl';
391                 $message = file_get_contents("php://input");
392                 $matches= array();
393                 if(preg_match('/xmlns:([^=]+)="urn:'.$namespace.'"/', $message, $matches))
394                 {
395                         if(preg_match('/<'.$matches[1].':([a-zA-Z_]+[a-zA-Z0-9_]+)/', $message, $method))
396                         {
397                                 $this->_requestedMethod = $method[1];
398                         }
399                 }
400         }
401
402         /**
403          * Soap method guessed from the SOAP message received.
404          * @return string soap method request, null if not found.
405          */
406         public function getRequestedMethod()
407         {
408                 return $this->_requestedMethod;
409         }
410
411         /**
412          * Creates the SoapServer instance.
413          * @return SoapServer
414          */
415         protected function createServer()
416         {
417                 if($this->_server===null)
418                 {
419                         if($this->getApplication()->getMode()===TApplicationMode::Debug)
420                                 ini_set("soap.wsdl_cache_enabled",0);
421                         $this->_server = new SoapServer($this->getWsdlUri(),$this->getOptions());
422                 }
423                 return $this->_server;
424         }
425
426         /**
427          * @return array options for creating SoapServer instance
428          */
429         protected function getOptions()
430         {
431                 $options=array();
432                 if($this->_version==='1.1')
433                         $options['soap_version']=SOAP_1_1;
434                 else if($this->_version==='1.2')
435                         $options['soap_version']=SOAP_1_2;
436                 if(!empty($this->_actor))
437                         $options['actor']=$this->_actor;
438                 if(!empty($this->_encoding))
439                         $options['encoding']=$this->_encoding;
440                 if(!empty($this->_uri))
441                         $options['uri']=$this->_uri;
442                 if(is_string($this->_classMap))
443                 {
444                         foreach(preg_split('/\s*,\s*/', $this->_classMap) as $className)
445                                 $options['classmap'][$className]=$className; //complex type uses the class name in the wsdl
446                 }
447                 return $options;
448         }
449
450         /**
451          * Returns the WSDL content of the SOAP server.
452          * If {@link getWsdlUri WsdlUri} is set, its content will be returned.
453          * If not, the {@link setProvider Provider} class will be investigated
454          * and the WSDL will be automatically genearted.
455          * @return string the WSDL content of the SOAP server
456          */
457         public function getWsdl()
458         {
459                 if($this->_wsdlUri==='')
460                 {
461                         $provider=$this->getProvider();
462                         $providerClass=($pos=strrpos($provider,'.'))!==false?substr($provider,$pos+1):$provider;
463                         Prado::using($provider);
464                         if($this->getApplication()->getMode()===TApplicationMode::Performance && ($cache=$this->getApplication()->getCache())!==null)
465                         {
466                                 $wsdl=$cache->get(self::WSDL_CACHE_PREFIX.$providerClass);
467                                 if(is_string($wsdl))
468                                         return $wsdl;
469                                 Prado::using('System.3rdParty.WsdlGen.WsdlGenerator');
470                                 $wsdl=WsdlGenerator::generate($providerClass, $this->getUri(), $this->getEncoding());
471                                 $cache->set(self::WSDL_CACHE_PREFIX.$providerClass,$wsdl);
472                                 return $wsdl;
473                         }
474                         else
475                         {
476                                 Prado::using('System.3rdParty.WsdlGen.WsdlGenerator');
477                                 return WsdlGenerator::generate($providerClass, $this->getUri(), $this->getEncoding());
478                         }
479                 }
480                 else
481                         return file_get_contents($this->_wsdlUri);
482         }
483
484         /**
485          * @return string the URI for WSDL
486          */
487         public function getWsdlUri()
488         {
489                 if($this->_wsdlUri==='')
490                         return $this->getRequest()->getBaseUrl().$this->getService()->constructUrl($this->getID().'.wsdl',false);
491                 else
492                         return $this->_wsdlUri;
493         }
494
495         /**
496          * @param string the URI for WSDL
497          */
498         public function setWsdlUri($value)
499         {
500                 $this->_wsdlUri=$value;
501         }
502
503         /**
504          * @return string the URI for the SOAP service
505          */
506         public function getUri()
507         {
508                 if($this->_uri==='')
509                         return $this->getRequest()->getBaseUrl().$this->getService()->constructUrl($this->getID(),false);
510                 else
511                         return $this->_uri;
512         }
513
514         /**
515          * @param string the URI for the SOAP service
516          */
517         public function setUri($uri)
518         {
519                 $this->_uri=$uri;
520         }
521
522         /**
523          * @return string the SOAP provider class (in namespace format)
524          */
525         public function getProvider()
526         {
527                 return $this->_provider;
528         }
529
530         /**
531          * @param string the SOAP provider class (in namespace format)
532          */
533         public function setProvider($provider)
534         {
535                 $this->_provider=$provider;
536         }
537
538         /**
539          * @return string SOAP version, defaults to empty (meaning not set).
540          */
541         public function getVersion()
542         {
543                 return $this->_version;
544         }
545
546         /**
547          * @param string SOAP version, either '1.1' or '1.2'
548          * @throws TInvalidDataValueException if neither '1.1' nor '1.2'
549          */
550         public function setVersion($value)
551         {
552                 if($value==='1.1' || $value==='1.2' || $value==='')
553                         $this->_version=$value;
554                 else
555                         throw new TInvalidDataValueException('soapserver_version_invalid',$value);
556         }
557
558         /**
559          * @return string actor of the SOAP service
560          */
561         public function getActor()
562         {
563                 return $this->_actor;
564         }
565
566         /**
567          * @param string actor of the SOAP service
568          */
569         public function setActor($value)
570         {
571                 $this->_actor=$value;
572         }
573
574         /**
575          * @return string encoding of the SOAP service
576          */
577         public function getEncoding()
578         {
579                 return $this->_encoding;
580         }
581
582         /**
583          * @param string encoding of the SOAP service
584          */
585         public function setEncoding($value)
586         {
587                 $this->_encoding=$value;
588         }
589
590         /**
591          * @return boolean whether the SOAP service is persistent within session. Defaults to false.
592          */
593         public function getSessionPersistent()
594         {
595                 return $this->_persistent;
596         }
597
598         /**
599          * @param boolean whether the SOAP service is persistent within session.
600          */
601         public function setSessionPersistent($value)
602         {
603                 $this->_persistent=TPropertyValue::ensureBoolean($value);
604         }
605
606         /**
607          * @return string comma delimit list of complex type classes.
608          */
609         public function getClassMaps()
610         {
611                 return $this->_classMap;
612         }
613
614         /**
615          * @return string comma delimit list of class names
616          */
617         public function setClassMaps($classes)
618         {
619                 $this->_classMap = $classes;
620         }
621 }
622