]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/TApplication.php
baculum: Remove unused api endpoints
[bacula/bacula] / gui / baculum / framework / TApplication.php
1 <?php
2 /**
3  * TApplication class file
4  *
5  * @author Qiang Xue <qiang.xue@gmail.com>
6  * @link https://github.com/pradosoft/prado
7  * @copyright Copyright &copy; 2005-2016 The PRADO Group
8  * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
9  * @package System
10  */
11
12 /**
13  * Includes core interfaces essential for TApplication class
14  */
15 require_once(PRADO_DIR.'/interfaces.php');
16
17 /**
18  * Includes core classes essential for TApplication class
19  */
20 Prado::using('System.TApplicationComponent');
21 Prado::using('System.TModule');
22 Prado::using('System.TService');
23 Prado::using('System.Exceptions.TErrorHandler');
24 Prado::using('System.Caching.TCache');
25 Prado::using('System.IO.TTextWriter');
26 Prado::using('System.Collections.TPriorityList');
27 Prado::using('System.Collections.TPriorityMap');
28 Prado::using('System.Collections.TStack');
29 Prado::using('System.Xml.TXmlDocument');
30 Prado::using('System.Security.TAuthorizationRule');
31 Prado::using('System.Security.TSecurityManager');
32 Prado::using('System.Web.THttpUtility');
33 Prado::using('System.Web.Javascripts.TJavaScript');
34 Prado::using('System.Web.THttpRequest');
35 Prado::using('System.Web.THttpResponse');
36 Prado::using('System.Web.THttpSession');
37 Prado::using('System.Web.Services.TPageService');
38 Prado::using('System.Web.TAssetManager');
39 Prado::using('System.I18N.TGlobalization');
40
41 /**
42  * TApplication class.
43  *
44  * TApplication coordinates modules and services, and serves as a configuration
45  * context for all Prado components.
46  *
47  * TApplication uses a configuration file to specify the settings of
48  * the application, the modules, the services, the parameters, and so on.
49  *
50  * TApplication adopts a modular structure. A TApplication instance is a composition
51  * of multiple modules. A module is an instance of class implementing
52  * {@link IModule} interface. Each module accomplishes certain functionalities
53  * that are shared by all Prado components in an application.
54  * There are default modules and user-defined modules. The latter offers extreme
55  * flexibility of extending TApplication in a plug-and-play fashion.
56  * Modules cooperate with each other to serve a user request by following
57  * a sequence of lifecycles predefined in TApplication.
58  *
59  * TApplication has four modes that can be changed by setting {@link setMode Mode}
60  * property (in the application configuration file).
61  * - <b>Off</b> mode will prevent the application from serving user requests.
62  * - <b>Debug</b> mode is mainly used during application development. It ensures
63  *   the cache is always up-to-date if caching is enabled. It also allows
64  *   exceptions are displayed with rich context information if they occur.
65  * - <b>Normal</b> mode is mainly used during production stage. Exception information
66  *   will only be recorded in system error logs. The cache is ensured to be
67  *   up-to-date if it is enabled.
68  * - <b>Performance</b> mode is similar to <b>Normal</b> mode except that it
69  *   does not ensure the cache is up-to-date.
70  *
71  * TApplication dispatches each user request to a particular service which
72  * finishes the actual work for the request with the aid from the application
73  * modules.
74  *
75  * TApplication maintains a lifecycle with the following stages:
76  * - [construct] : construction of the application instance
77  * - [initApplication] : load application configuration and instantiate modules and the requested service
78  * - onBeginRequest : this event happens right after application initialization
79  * - onAuthentication : this event happens when authentication is needed for the current request
80  * - onAuthenticationComplete : this event happens right after the authentication is done for the current request
81  * - onAuthorization : this event happens when authorization is needed for the current request
82  * - onAuthorizationComplete : this event happens right after the authorization is done for the current request
83  * - onLoadState : this event happens when application state needs to be loaded
84  * - onLoadStateComplete : this event happens right after the application state is loaded
85  * - onPreRunService : this event happens right before the requested service is to run
86  * - runService : the requested service runs
87  * - onSaveState : this event happens when application needs to save its state
88  * - onSaveStateComplete : this event happens right after the application saves its state
89  * - onPreFlushOutput : this event happens right before the application flushes output to client side.
90  * - flushOutput : the application flushes output to client side.
91  * - onEndRequest : this is the last stage a request is being completed
92  * - [destruct] : destruction of the application instance
93  * Modules and services can attach their methods to one or several of the above
94  * events and do appropriate processing when the events are raised. By this way,
95  * the application is able to coordinate the activities of modules and services
96  * in the above order. To terminate an application before the whole lifecycle
97  * completes, call {@link completeRequest}.
98  *
99  * Examples:
100  * - Create and run a Prado application:
101  * <code>
102  * $application=new TApplication($configFile);
103  * $application->run();
104  * </code>
105  *
106  * @author Qiang Xue <qiang.xue@gmail.com>
107  * @package System
108  * @since 3.0
109  */
110 class TApplication extends TComponent
111 {
112         /**
113          * possible application mode.
114          * @deprecated deprecated since version 3.0.4 (use TApplicationMode constants instead)
115          */
116         const STATE_OFF='Off';
117         const STATE_DEBUG='Debug';
118         const STATE_NORMAL='Normal';
119         const STATE_PERFORMANCE='Performance';
120
121         /**
122          * Page service ID
123          */
124         const PAGE_SERVICE_ID='page';
125         /**
126          * Application configuration file name
127          */
128         const CONFIG_FILE_XML='application.xml';
129         /**
130          * File extension for external config files
131          */
132         const CONFIG_FILE_EXT_XML='.xml';
133         /**
134          * Configuration file type, application.xml and config.xml
135          */
136         const CONFIG_TYPE_XML = 'xml';
137         /**
138          * Application configuration file name
139          */
140         const CONFIG_FILE_PHP='application.php';
141         /**
142          * File extension for external config files
143          */
144         const CONFIG_FILE_EXT_PHP='.php';
145         /**
146          * Configuration file type, application.php and config.php
147          */
148         const CONFIG_TYPE_PHP = 'php';
149         /**
150          * Runtime directory name
151          */
152         const RUNTIME_PATH='runtime';
153         /**
154          * Config cache file
155          */
156         const CONFIGCACHE_FILE='config.cache';
157         /**
158          * Global data file
159          */
160         const GLOBAL_FILE='global.cache';
161
162         /**
163          * @var array list of events that define application lifecycles
164          */
165         private static $_steps=array(
166                 'onBeginRequest',
167                 'onLoadState',
168                 'onLoadStateComplete',
169                 'onAuthentication',
170                 'onAuthenticationComplete',
171                 'onAuthorization',
172                 'onAuthorizationComplete',
173                 'onPreRunService',
174                 'runService',
175                 'onSaveState',
176                 'onSaveStateComplete',
177                 'onPreFlushOutput',
178                 'flushOutput'
179         );
180
181         /**
182          * @var string application ID
183          */
184         private $_id;
185         /**
186          * @var string unique application ID
187          */
188         private $_uniqueID;
189         /**
190          * @var boolean whether the request is completed
191          */
192         private $_requestCompleted=false;
193         /**
194          * @var integer application state
195          */
196         private $_step;
197         /**
198          * @var array available services and their configurations indexed by service IDs
199          */
200         private $_services;
201         /**
202          * @var IService current service instance
203          */
204         private $_service;
205         /**
206          * @var array list of loaded application modules
207          */
208         private $_modules=array();
209         /**
210          * @var array list of application modules yet to be loaded
211          */
212         private $_lazyModules=array();
213         /**
214          * @var TMap list of application parameters
215          */
216         private $_parameters;
217         /**
218          * @var string configuration file
219          */
220         private $_configFile;
221         /**
222          * @var string configuration file extension
223          */
224         private $_configFileExt;
225         /**
226          * @var string configuration type
227          */
228         private $_configType;
229         /**
230          * @var string application base path
231          */
232         private $_basePath;
233         /**
234          * @var string directory storing application state
235          */
236         private $_runtimePath;
237         /**
238          * @var boolean if any global state is changed during the current request
239          */
240         private $_stateChanged=false;
241         /**
242          * @var array global variables (persistent across sessions, requests)
243          */
244         private $_globals=array();
245         /**
246          * @var string cache file
247          */
248         private $_cacheFile;
249         /**
250          * @var TErrorHandler error handler module
251          */
252         private $_errorHandler;
253         /**
254          * @var THttpRequest request module
255          */
256         private $_request;
257         /**
258          * @var THttpResponse response module
259          */
260         private $_response;
261         /**
262          * @var THttpSession session module, could be null
263          */
264         private $_session;
265         /**
266          * @var ICache cache module, could be null
267          */
268         private $_cache;
269         /**
270          * @var IStatePersister application state persister
271          */
272         private $_statePersister;
273         /**
274          * @var IUser user instance, could be null
275          */
276         private $_user;
277         /**
278          * @var TGlobalization module, could be null
279          */
280         private $_globalization;
281         /**
282          * @var TSecurityManager security manager module
283          */
284         private $_security;
285         /**
286          * @var TAssetManager asset manager module
287          */
288         private $_assetManager;
289         /**
290          * @var TAuthorizationRuleCollection collection of authorization rules
291          */
292         private $_authRules;
293         /**
294          * @var TApplicationMode application mode
295          */
296         private $_mode=TApplicationMode::Debug;
297
298         /**
299          * @var string Customizable page service ID
300          */
301         private $_pageServiceID = self::PAGE_SERVICE_ID;
302
303         /**
304          * Constructor.
305          * Sets application base path and initializes the application singleton.
306          * Application base path refers to the root directory storing application
307          * data and code not directly accessible by Web users.
308          * By default, the base path is assumed to be the <b>protected</b>
309          * directory under the directory containing the current running script.
310          * @param string application base path or configuration file path.
311          *        If the parameter is a file, it is assumed to be the application
312          *        configuration file, and the directory containing the file is treated
313          *        as the application base path.
314          *        If it is a directory, it is assumed to be the application base path,
315          *        and within that directory, a file named <b>application.xml</b>
316          *        will be looked for. If found, the file is considered as the application
317          *        configuration file.
318          * @param boolean whether to cache application configuration. Defaults to true.
319          * @throws TConfigurationException if configuration file cannot be read or the runtime path is invalid.
320          */
321         public function __construct($basePath='protected',$cacheConfig=true, $configType=self::CONFIG_TYPE_XML)
322         {
323                 // register application as a singleton
324                 Prado::setApplication($this);
325                 $this->setConfigurationType($configType);
326                 $this->resolvePaths($basePath);
327
328                 if($cacheConfig)
329                         $this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE;
330
331                 // generates unique ID by hashing the runtime path
332                 $this->_uniqueID=md5($this->_runtimePath);
333                 $this->_parameters=new TMap;
334                 $this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null));
335
336                 Prado::setPathOfAlias('Application',$this->_basePath);
337         }
338
339         /**
340          * Resolves application-relevant paths.
341          * This method is invoked by the application constructor
342          * to determine the application configuration file,
343          * application root path and the runtime path.
344          * @param string the application root path or the application configuration file
345          * @see setBasePath
346          * @see setRuntimePath
347          * @see setConfigurationFile
348          */
349         protected function resolvePaths($basePath)
350         {
351                 // determine configuration path and file
352                 if(empty($basePath) || ($basePath=realpath($basePath))===false)
353                         throw new TConfigurationException('application_basepath_invalid',$basePath);
354                 if(is_dir($basePath) && is_file($basePath.DIRECTORY_SEPARATOR.$this->getConfigurationFileName()))
355                         $configFile=$basePath.DIRECTORY_SEPARATOR.$this->getConfigurationFileName();
356                 else if(is_file($basePath))
357                 {
358                         $configFile=$basePath;
359                         $basePath=dirname($configFile);
360                 }
361                 else
362                         $configFile=null;
363
364                 // determine runtime path
365                 $runtimePath=$basePath.DIRECTORY_SEPARATOR.self::RUNTIME_PATH;
366                 if(is_writable($runtimePath))
367                 {
368                         if($configFile!==null)
369                         {
370                                 $runtimePath.=DIRECTORY_SEPARATOR.basename($configFile).'-'.Prado::getVersion();
371                                 if(!is_dir($runtimePath))
372                                 {
373                                         if(@mkdir($runtimePath)===false)
374                                                 throw new TConfigurationException('application_runtimepath_failed',$runtimePath);
375                                         @chmod($runtimePath, PRADO_CHMOD); //make it deletable
376                                 }
377                                 $this->setConfigurationFile($configFile);
378                         }
379                         $this->setBasePath($basePath);
380                         $this->setRuntimePath($runtimePath);
381                 }
382                 else
383                         throw new TConfigurationException('application_runtimepath_invalid',$runtimePath);
384
385         }
386
387         /**
388          * Executes the lifecycles of the application.
389          * This is the main entry function that leads to the running of the whole
390          * Prado application.
391          */
392         public function run()
393         {
394                 try
395                 {
396                         $this->initApplication();
397                         $n=count(self::$_steps);
398                         $this->_step=0;
399                         $this->_requestCompleted=false;
400                         while($this->_step<$n)
401                         {
402                                 if($this->_mode===self::STATE_OFF)
403                                         throw new THttpException(503,'application_unavailable');
404                                 if($this->_requestCompleted)
405                                         break;
406                                 $method=self::$_steps[$this->_step];
407                                 Prado::trace("Executing $method()",'System.TApplication');
408                                 $this->$method();
409                                 $this->_step++;
410                         }
411                 }
412                 catch(Exception $e)
413                 {
414                         $this->onError($e);
415                 }
416                 $this->onEndRequest();
417         }
418
419         /**
420          * Completes current request processing.
421          * This method can be used to exit the application lifecycles after finishing
422          * the current cycle.
423          */
424         public function completeRequest()
425         {
426                 $this->_requestCompleted=true;
427         }
428
429         /**
430          * @return boolean whether the current request is processed.
431          */
432         public function getRequestCompleted()
433         {
434                 return $this->_requestCompleted;
435         }
436
437         /**
438          * Returns a global value.
439          *
440          * A global value is one that is persistent across users sessions and requests.
441          * @param string the name of the value to be returned
442          * @param mixed the default value. If $key is not found, $defaultValue will be returned
443          * @return mixed the global value corresponding to $key
444          */
445         public function getGlobalState($key,$defaultValue=null)
446         {
447                 return isset($this->_globals[$key])?$this->_globals[$key]:$defaultValue;
448         }
449
450         /**
451          * Sets a global value.
452          *
453          * A global value is one that is persistent across users sessions and requests.
454          * Make sure that the value is serializable and unserializable.
455          * @param string the name of the value to be set
456          * @param mixed the global value to be set
457          * @param mixed the default value. If $key is not found, $defaultValue will be returned
458          * @param boolean wheter to force an immediate GlobalState save. defaults to false
459          */
460         public function setGlobalState($key,$value,$defaultValue=null,$forceSave=false)
461         {
462                 $this->_stateChanged=true;
463                 if($value===$defaultValue)
464                         unset($this->_globals[$key]);
465                 else
466                         $this->_globals[$key]=$value;
467                 if($forceSave)
468                         $this->saveGlobals();
469         }
470
471         /**
472          * Clears a global value.
473          *
474          * The value cleared will no longer be available in this request and the following requests.
475          * @param string the name of the value to be cleared
476          */
477         public function clearGlobalState($key)
478         {
479                 $this->_stateChanged=true;
480                 unset($this->_globals[$key]);
481         }
482
483         /**
484          * Loads global values from persistent storage.
485          * This method is invoked when {@link onLoadState OnLoadState} event is raised.
486          * After this method, values that are stored in previous requests become
487          * available to the current request via {@link getGlobalState}.
488          */
489         protected function loadGlobals()
490         {
491                 $this->_globals=$this->getApplicationStatePersister()->load();
492         }
493
494         /**
495          * Saves global values into persistent storage.
496          * This method is invoked when {@link onSaveState OnSaveState} event is raised.
497          */
498         protected function saveGlobals()
499         {
500                 if($this->_stateChanged)
501                 {
502                         $this->_stateChanged=false;
503                         $this->getApplicationStatePersister()->save($this->_globals);
504                 }
505         }
506
507         /**
508          * @return string application ID
509          */
510         public function getID()
511         {
512                 return $this->_id;
513         }
514
515         /**
516          * @param string application ID
517          */
518         public function setID($value)
519         {
520                 $this->_id=$value;
521         }
522
523         /**
524          * @return string page service ID
525          */
526         public function getPageServiceID()
527         {
528                 return $this->_pageServiceID;
529         }
530
531         /**
532          * @param string page service ID
533          */
534         public function setPageServiceID($value)
535         {
536                 $this->_pageServiceID=$value;
537         }
538
539         /**
540          * @return string an ID that uniquely identifies this Prado application from the others
541          */
542         public function getUniqueID()
543         {
544                 return $this->_uniqueID;
545         }
546
547         /**
548          * @return TApplicationMode application mode. Defaults to TApplicationMode::Debug.
549          */
550         public function getMode()
551         {
552                 return $this->_mode;
553         }
554
555         /**
556          * @param TApplicationMode application mode
557          */
558         public function setMode($value)
559         {
560                 $this->_mode=TPropertyValue::ensureEnum($value,'TApplicationMode');
561         }
562
563         /**
564          * @return string the directory containing the application configuration file (absolute path)
565          */
566         public function getBasePath()
567         {
568                 return $this->_basePath;
569         }
570
571         /**
572          * @param string the directory containing the application configuration file
573          */
574         public function setBasePath($value)
575         {
576                 $this->_basePath=$value;
577         }
578
579         /**
580          * @return string the application configuration file (absolute path)
581          */
582         public function getConfigurationFile()
583         {
584                 return $this->_configFile;
585         }
586
587         /**
588          * @param string the application configuration file (absolute path)
589          */
590         public function setConfigurationFile($value)
591         {
592                 $this->_configFile=$value;
593         }
594
595         /**
596          * @return string the application configuration file (absolute path)
597          */
598         public function getConfigurationType()
599         {
600                 return $this->_configType;
601         }
602
603         /**
604          * @param string the application configuration type. 'xml' and 'php' are valid values
605          */
606         public function setConfigurationType($value)
607         {
608                 $this->_configType = $value;
609         }
610
611         /**
612          * @return string the application configuration type. default is 'xml'
613          */
614         public function getConfigurationFileExt()
615         {
616                 if($this->_configFileExt===null)
617                 {
618                         switch($this->_configType)
619                         {
620                                 case TApplication::CONFIG_TYPE_PHP:
621                                         $this->_configFileExt = TApplication::CONFIG_FILE_EXT_PHP;
622                                         break;
623                                 default:
624                                         $this->_configFileExt = TApplication::CONFIG_FILE_EXT_XML;
625                         }
626                 }
627                 return $this->_configFileExt;
628         }
629
630         /**
631          * @return string the default configuration file name
632          */
633         public function getConfigurationFileName()
634         {
635                 static $fileName;
636                 if($fileName == null)
637                 {
638                         switch($this->_configType)
639                         {
640                                 case TApplication::CONFIG_TYPE_PHP:
641                                         $fileName = TApplication::CONFIG_FILE_PHP;
642                                         break;
643                                 default:
644                                         $fileName = TApplication::CONFIG_FILE_XML;
645                         }
646                 }
647                 return $fileName;
648         }
649
650         /**
651          * @return string the directory storing cache data and application-level persistent data. (absolute path)
652          */
653         public function getRuntimePath()
654         {
655                 return $this->_runtimePath;
656         }
657
658         /**
659          * @param string the directory storing cache data and application-level persistent data. (absolute path)
660          */
661         public function setRuntimePath($value)
662         {
663                 $this->_runtimePath=$value;
664                 if($this->_cacheFile)
665                         $this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE;
666                 // generates unique ID by hashing the runtime path
667                 $this->_uniqueID=md5($this->_runtimePath);
668         }
669
670         /**
671          * @return IService the currently requested service
672          */
673         public function getService()
674         {
675                 return $this->_service;
676         }
677
678         /**
679          * @param IService the currently requested service
680          */
681         public function setService($value)
682         {
683                 $this->_service=$value;
684         }
685
686         /**
687          * Adds a module to application.
688          * Note, this method does not do module initialization.
689          * @param string ID of the module
690          * @param IModule module object or null if the module has not been loaded yet
691          */
692         public function setModule($id,IModule $module=null)
693         {
694                 if(isset($this->_modules[$id]))
695                         throw new TConfigurationException('application_moduleid_duplicated',$id);
696                 else
697                         $this->_modules[$id]=$module;
698         }
699
700         /**
701          * @return IModule the module with the specified ID, null if not found
702          */
703         public function getModule($id)
704         {
705                 if(!array_key_exists($id, $this->_modules))
706                         return null;
707
708                 // force loading of a lazy module
709                 if($this->_modules[$id]===null)
710                 {
711                         $module = $this->internalLoadModule($id, true);
712                         $module[0]->init($module[1]);
713                 }
714
715                 return $this->_modules[$id];
716         }
717
718         /**
719          * Returns a list of application modules indexed by module IDs.
720          * Modules that have not been loaded yet are returned as null objects.
721          * @return array list of loaded application modules, indexed by module IDs
722          */
723         public function getModules()
724         {
725                 return $this->_modules;
726         }
727
728         /**
729          * Returns the list of application parameters.
730          * Since the parameters are returned as a {@link TMap} object, you may use
731          * the returned result to access, add or remove individual parameters.
732          * @return TMap the list of application parameters
733          */
734         public function getParameters()
735         {
736                 return $this->_parameters;
737         }
738
739         /**
740          * @return THttpRequest the request module
741          */
742         public function getRequest()
743         {
744                 if(!$this->_request)
745                 {
746                         $this->_request=new THttpRequest;
747                         $this->_request->init(null);
748                 }
749                 return $this->_request;
750         }
751
752         /**
753          * @param THttpRequest the request module
754          */
755         public function setRequest(THttpRequest $request)
756         {
757                 $this->_request=$request;
758         }
759
760         /**
761          * @return THttpResponse the response module
762          */
763         public function getResponse()
764         {
765                 if(!$this->_response)
766                 {
767                         $this->_response=new THttpResponse;
768                         $this->_response->init(null);
769                 }
770                 return $this->_response;
771         }
772
773         /**
774          * @param THttpRequest the request module
775          */
776         public function setResponse(THttpResponse $response)
777         {
778                 $this->_response=$response;
779         }
780
781         /**
782          * @return THttpSession the session module, null if session module is not installed
783          */
784         public function getSession()
785         {
786                 if(!$this->_session)
787                 {
788                         $this->_session=new THttpSession;
789                         $this->_session->init(null);
790                 }
791                 return $this->_session;
792         }
793
794         /**
795          * @param THttpSession the session module
796          */
797         public function setSession(THttpSession $session)
798         {
799                 $this->_session=$session;
800         }
801
802         /**
803          * @return TErrorHandler the error handler module
804          */
805         public function getErrorHandler()
806         {
807                 if(!$this->_errorHandler)
808                 {
809                         $this->_errorHandler=new TErrorHandler;
810                         $this->_errorHandler->init(null);
811                 }
812                 return $this->_errorHandler;
813         }
814
815         /**
816          * @param TErrorHandler the error handler module
817          */
818         public function setErrorHandler(TErrorHandler $handler)
819         {
820                 $this->_errorHandler=$handler;
821         }
822
823         /**
824          * @return TSecurityManager the security manager module
825          */
826         public function getSecurityManager()
827         {
828                 if(!$this->_security)
829                 {
830                         $this->_security=new TSecurityManager;
831                         $this->_security->init(null);
832                 }
833                 return $this->_security;
834         }
835
836         /**
837          * @param TSecurityManager the security manager module
838          */
839         public function setSecurityManager(TSecurityManager $sm)
840         {
841                 $this->_security=$sm;
842         }
843
844         /**
845          * @return TAssetManager asset manager
846          */
847         public function getAssetManager()
848         {
849                 if(!$this->_assetManager)
850                 {
851                         $this->_assetManager=new TAssetManager;
852                         $this->_assetManager->init(null);
853                 }
854                 return $this->_assetManager;
855         }
856
857         /**
858          * @param TAssetManager asset manager
859          */
860         public function setAssetManager(TAssetManager $value)
861         {
862                 $this->_assetManager=$value;
863         }
864
865         /**
866          * @return IStatePersister application state persister
867          */
868         public function getApplicationStatePersister()
869         {
870                 if(!$this->_statePersister)
871                 {
872                         $this->_statePersister=new TApplicationStatePersister;
873                         $this->_statePersister->init(null);
874                 }
875                 return $this->_statePersister;
876         }
877
878         /**
879          * @param IStatePersister  application state persister
880          */
881         public function setApplicationStatePersister(IStatePersister $persister)
882         {
883                 $this->_statePersister=$persister;
884         }
885
886         /**
887          * @return ICache the cache module, null if cache module is not installed
888          */
889         public function getCache()
890         {
891                 return $this->_cache;
892         }
893
894         /**
895          * @param ICache the cache module
896          */
897         public function setCache(ICache $cache)
898         {
899                 $this->_cache=$cache;
900         }
901
902         /**
903          * @return IUser the application user
904          */
905         public function getUser()
906         {
907                 return $this->_user;
908         }
909
910         /**
911          * @param IUser the application user
912          */
913         public function setUser(IUser $user)
914         {
915                 $this->_user=$user;
916         }
917
918         /**
919          * @param boolean whether to create globalization if it does not exist
920          * @return TGlobalization globalization module
921          */
922         public function getGlobalization($createIfNotExists=true)
923         {
924                 if($this->_globalization===null && $createIfNotExists)
925                 {
926                         $this->_globalization=new TGlobalization;
927                         $this->_globalization->init(null);
928                 }
929                 return $this->_globalization;
930         }
931
932         /**
933          * @param TGlobalization globalization module
934          */
935         public function setGlobalization(TGlobalization $glob)
936         {
937                 $this->_globalization=$glob;
938         }
939
940         /**
941          * @return TAuthorizationRuleCollection list of authorization rules for the current request
942          */
943         public function getAuthorizationRules()
944         {
945                 if($this->_authRules===null)
946                         $this->_authRules=new TAuthorizationRuleCollection;
947                 return $this->_authRules;
948         }
949
950         protected function getApplicationConfigurationClass()
951         {
952                 return 'TApplicationConfiguration';
953         }
954
955         protected function internalLoadModule($id, $force=false)
956         {
957                 list($moduleClass, $initProperties, $configElement)=$this->_lazyModules[$id];
958                 if(isset($initProperties['lazy']) && $initProperties['lazy'] && !$force)
959                 {
960                         Prado::trace("Postponed loading of lazy module $id ({$moduleClass})",'System.TApplication');
961                         $this->setModule($id, null);
962                         return null;
963                 }
964
965                 Prado::trace("Loading module $id ({$moduleClass})",'System.TApplication');
966                 $module=Prado::createComponent($moduleClass);
967                 foreach($initProperties as $name=>$value)
968                 {
969                         if($name==='lazy') continue;
970                         $module->setSubProperty($name,$value);
971                 }
972                 $this->setModule($id,$module);
973                 // keep the key to avoid reuse of the old module id
974                 $this->_lazyModules[$id]=null;
975
976                 return array($module,$configElement);
977         }
978         /**
979          * Applies an application configuration.
980          * @param TApplicationConfiguration the configuration
981          * @param boolean whether the configuration is specified within a service.
982          */
983         public function applyConfiguration($config,$withinService=false)
984         {
985                 if($config->getIsEmpty())
986                         return;
987
988                 // set path aliases and using namespaces
989                 foreach($config->getAliases() as $alias=>$path)
990                         Prado::setPathOfAlias($alias,$path);
991                 foreach($config->getUsings() as $using)
992                         Prado::using($using);
993
994                 // set application properties
995                 if(!$withinService)
996                 {
997                         foreach($config->getProperties() as $name=>$value)
998                                 $this->setSubProperty($name,$value);
999                 }
1000
1001                 if(empty($this->_services))
1002                         $this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null));
1003
1004                 // load parameters
1005                 foreach($config->getParameters() as $id=>$parameter)
1006                 {
1007                         if(is_array($parameter))
1008                         {
1009                                 $component=Prado::createComponent($parameter[0]);
1010                                 foreach($parameter[1] as $name=>$value)
1011                                         $component->setSubProperty($name,$value);
1012                                 $this->_parameters->add($id,$component);
1013                         }
1014                         else
1015                                 $this->_parameters->add($id,$parameter);
1016                 }
1017
1018                 // load and init modules specified in app config
1019                 $modules=array();
1020                 foreach($config->getModules() as $id=>$moduleConfig)
1021                 {
1022                         if(!is_string($id))
1023                                 $id='_module'.count($this->_lazyModules);
1024                         $this->_lazyModules[$id]=$moduleConfig;
1025                         if($module = $this->internalLoadModule($id))
1026                                 $modules[]=$module;
1027                 }
1028                 foreach($modules as $module)
1029                         $module[0]->init($module[1]);
1030
1031                 // load service
1032                 foreach($config->getServices() as $serviceID=>$serviceConfig)
1033                         $this->_services[$serviceID]=$serviceConfig;
1034
1035                 // external configurations
1036                 foreach($config->getExternalConfigurations() as $filePath=>$condition)
1037                 {
1038                         if($condition!==true)
1039                                 $condition=$this->evaluateExpression($condition);
1040                         if($condition)
1041                         {
1042                                 if(($path=Prado::getPathOfNamespace($filePath,$this->getConfigurationFileExt()))===null || !is_file($path))
1043                                         throw new TConfigurationException('application_includefile_invalid',$filePath);
1044                                 $cn=$this->getApplicationConfigurationClass();
1045                                 $c=new $cn;
1046                                 $c->loadFromFile($path);
1047                                 $this->applyConfiguration($c,$withinService);
1048                         }
1049                 }
1050         }
1051
1052         /**
1053          * Loads configuration and initializes application.
1054          * Configuration file will be read and parsed (if a valid cached version exists,
1055          * it will be used instead). Then, modules are created and initialized;
1056          * Afterwards, the requested service is created and initialized.
1057          * @param string configuration file path (absolute or relative to current executing script)
1058          * @param string cache file path, empty if no present or needed
1059          * @throws TConfigurationException if module is redefined of invalid type, or service not defined or of invalid type
1060          */
1061         protected function initApplication()
1062         {
1063                 Prado::trace('Initializing application','System.TApplication');
1064
1065                 if($this->_configFile!==null)
1066                 {
1067                         if($this->_cacheFile===null || @filemtime($this->_cacheFile)<filemtime($this->_configFile))
1068                         {
1069                                 $config=new TApplicationConfiguration;
1070                                 $config->loadFromFile($this->_configFile);
1071                                 if($this->_cacheFile!==null)
1072                                         file_put_contents($this->_cacheFile,serialize($config),LOCK_EX);
1073                         }
1074                         else
1075                                 $config=unserialize(file_get_contents($this->_cacheFile));
1076
1077                         $this->applyConfiguration($config,false);
1078                 }
1079
1080                 if(($serviceID=$this->getRequest()->resolveRequest(array_keys($this->_services)))===null)
1081                         $serviceID=$this->getPageServiceID();
1082
1083                 $this->startService($serviceID);
1084         }
1085
1086         /**
1087          * Starts the specified service.
1088          * The service instance will be created. Its properties will be initialized
1089          * and the configurations will be applied, if any.
1090          * @param string service ID
1091          */
1092         public function startService($serviceID)
1093         {
1094                 if(isset($this->_services[$serviceID]))
1095                 {
1096                         list($serviceClass,$initProperties,$configElement)=$this->_services[$serviceID];
1097                         $service=Prado::createComponent($serviceClass);
1098                         if(!($service instanceof IService))
1099                                 throw new THttpException(500,'application_service_invalid',$serviceClass);
1100                         if(!$service->getEnabled())
1101                                 throw new THttpException(500,'application_service_unavailable',$serviceClass);
1102                         $service->setID($serviceID);
1103                         $this->setService($service);
1104
1105                         foreach($initProperties as $name=>$value)
1106                                 $service->setSubProperty($name,$value);
1107
1108                         if($configElement!==null)
1109                         {
1110                                 $config=new TApplicationConfiguration;
1111                                 if($this->getConfigurationType()==self::CONFIG_TYPE_PHP)
1112                                         $config->loadFromPhp($configElement,$this->getBasePath());
1113                                 else
1114                                         $config->loadFromXml($configElement,$this->getBasePath());
1115                                 $this->applyConfiguration($config,true);
1116                         }
1117
1118                         $service->init($configElement);
1119                 }
1120                 else
1121                         throw new THttpException(500,'application_service_unknown',$serviceID);
1122         }
1123
1124         /**
1125          * Raises OnError event.
1126          * This method is invoked when an exception is raised during the lifecycles
1127          * of the application.
1128          * @param mixed event parameter
1129          */
1130         public function onError($param)
1131         {
1132                 Prado::log($param->getMessage(),TLogger::ERROR,'System.TApplication');
1133                 $this->raiseEvent('OnError',$this,$param);
1134                 $this->getErrorHandler()->handleError($this,$param);
1135         }
1136
1137         /**
1138          * Raises OnBeginRequest event.
1139          * At the time when this method is invoked, application modules are loaded
1140          * and initialized, user request is resolved and the corresponding service
1141          * is loaded and initialized. The application is about to start processing
1142          * the user request.
1143          */
1144         public function onBeginRequest()
1145         {
1146                 $this->raiseEvent('OnBeginRequest',$this,null);
1147         }
1148
1149         /**
1150          * Raises OnAuthentication event.
1151          * This method is invoked when the user request needs to be authenticated.
1152          */
1153         public function onAuthentication()
1154         {
1155                 $this->raiseEvent('OnAuthentication',$this,null);
1156         }
1157
1158         /**
1159          * Raises OnAuthenticationComplete event.
1160          * This method is invoked right after the user request is authenticated.
1161          */
1162         public function onAuthenticationComplete()
1163         {
1164                 $this->raiseEvent('OnAuthenticationComplete',$this,null);
1165         }
1166
1167         /**
1168          * Raises OnAuthorization event.
1169          * This method is invoked when the user request needs to be authorized.
1170          */
1171         public function onAuthorization()
1172         {
1173                 $this->raiseEvent('OnAuthorization',$this,null);
1174         }
1175
1176         /**
1177          * Raises OnAuthorizationComplete event.
1178          * This method is invoked right after the user request is authorized.
1179          */
1180         public function onAuthorizationComplete()
1181         {
1182                 $this->raiseEvent('OnAuthorizationComplete',$this,null);
1183         }
1184
1185         /**
1186          * Raises OnLoadState event.
1187          * This method is invoked when the application needs to load state (probably stored in session).
1188          */
1189         public function onLoadState()
1190         {
1191                 $this->loadGlobals();
1192                 $this->raiseEvent('OnLoadState',$this,null);
1193         }
1194
1195         /**
1196          * Raises OnLoadStateComplete event.
1197          * This method is invoked right after the application state has been loaded.
1198          */
1199         public function onLoadStateComplete()
1200         {
1201                 $this->raiseEvent('OnLoadStateComplete',$this,null);
1202         }
1203
1204         /**
1205          * Raises OnPreRunService event.
1206          * This method is invoked right before the service is to be run.
1207          */
1208         public function onPreRunService()
1209         {
1210                 $this->raiseEvent('OnPreRunService',$this,null);
1211         }
1212
1213         /**
1214          * Runs the requested service.
1215          */
1216         public function runService()
1217         {
1218                 if($this->_service)
1219                         $this->_service->run();
1220         }
1221
1222         /**
1223          * Raises OnSaveState event.
1224          * This method is invoked when the application needs to save state (probably stored in session).
1225          */
1226         public function onSaveState()
1227         {
1228                 $this->raiseEvent('OnSaveState',$this,null);
1229                 $this->saveGlobals();
1230         }
1231
1232         /**
1233          * Raises OnSaveStateComplete event.
1234          * This method is invoked right after the application state has been saved.
1235          */
1236         public function onSaveStateComplete()
1237         {
1238                 $this->raiseEvent('OnSaveStateComplete',$this,null);
1239         }
1240
1241         /**
1242          * Raises OnPreFlushOutput event.
1243          * This method is invoked right before the application flushes output to client.
1244          */
1245         public function onPreFlushOutput()
1246         {
1247                 $this->raiseEvent('OnPreFlushOutput',$this,null);
1248         }
1249
1250         /**
1251          * Flushes output to client side.
1252          * @param boolean whether to continue buffering after flush if buffering was active
1253          */
1254         public function flushOutput($continueBuffering = true)
1255         {
1256                 $this->getResponse()->flush($continueBuffering);
1257         }
1258
1259         /**
1260          * Raises OnEndRequest event.
1261          * This method is invoked when the application completes the processing of the request.
1262          */
1263         public function onEndRequest()
1264         {
1265                 $this->flushOutput(false); // flush all remaining content in the buffer
1266                 $this->saveGlobals();  // save global state
1267                 $this->raiseEvent('OnEndRequest',$this,null);
1268         }
1269 }
1270
1271 /**
1272  * TApplicationMode class.
1273  * TApplicationMode defines the possible mode that an application can be set at by
1274  * setting {@link TApplication::setMode Mode}.
1275  * In particular, the following modes are defined
1276  * - Off: the application is not running. Any request to the application will obtain an error.
1277  * - Debug: the application is running in debug mode.
1278  * - Normal: the application is running in normal production mode.
1279  * - Performance: the application is running in performance mode.
1280  * @author Qiang Xue <qiang.xue@gmail.com>
1281  * @package System
1282  * @since 3.0.4
1283  */
1284 class TApplicationMode extends TEnumerable
1285 {
1286         const Off='Off';
1287         const Debug='Debug';
1288         const Normal='Normal';
1289         const Performance='Performance';
1290 }
1291
1292
1293 /**
1294  * TApplicationConfiguration class.
1295  *
1296  * This class is used internally by TApplication to parse and represent application configuration.
1297  *
1298  * @author Qiang Xue <qiang.xue@gmail.com>
1299  * @author Carl G. Mathisen <carlgmathisen@gmail.com>
1300  * @package System
1301  * @since 3.0
1302  */
1303 class TApplicationConfiguration extends TComponent
1304 {
1305         /**
1306          * @var array list of application initial property values, indexed by property names
1307          */
1308         private $_properties=array();
1309         /**
1310          * @var array list of namespaces to be used
1311          */
1312         private $_usings=array();
1313         /**
1314          * @var array list of path aliases, indexed by alias names
1315          */
1316         private $_aliases=array();
1317         /**
1318          * @var array list of module configurations
1319          */
1320         private $_modules=array();
1321         /**
1322          * @var array list of service configurations
1323          */
1324         private $_services=array();
1325         /**
1326          * @var array list of parameters
1327          */
1328         private $_parameters=array();
1329         /**
1330          * @var array list of included configurations
1331          */
1332         private $_includes=array();
1333         /**
1334          * @var boolean whether this configuration contains actual stuff
1335          */
1336         private $_empty=true;
1337
1338         /**
1339          * Parses the application configuration file.
1340          * @param string configuration file name
1341          * @throws TConfigurationException if there is any parsing error
1342          */
1343         public function loadFromFile($fname)
1344         {
1345                 if(Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
1346                 {
1347                         $fcontent = include $fname;
1348                         $this->loadFromPhp($fcontent,dirname($fname));
1349                 }
1350                 else
1351                 {
1352                         $dom=new TXmlDocument;
1353                         $dom->loadFromFile($fname);
1354                         $this->loadFromXml($dom,dirname($fname));
1355                 }
1356         }
1357
1358         /**
1359          * @return boolean whether this configuration contains actual stuff
1360          */
1361         public function getIsEmpty()
1362         {
1363                 return $this->_empty;
1364         }
1365
1366         /**
1367          * Parses the application configuration given in terms of a PHP array.
1368          * @param array the PHP array
1369          * @param string the context path (for specifying relative paths)
1370          */
1371         public function loadFromPhp($config, $configPath)
1372         {
1373                 // application properties
1374                 if(isset($config['application']))
1375                 {
1376                         foreach($config['application'] as $name=>$value)
1377                         {
1378                                 $this->_properties[$name]=$value;
1379                         }
1380                         $this->_empty = false;
1381                 }
1382
1383                 if(isset($config['paths']) && is_array($config['paths']))
1384                         $this->loadPathsPhp($config['paths'],$configPath);
1385
1386                 if(isset($config['modules']) && is_array($config['modules']))
1387                         $this->loadModulesPhp($config['modules'],$configPath);
1388
1389                 if(isset($config['services']) && is_array($config['services']))
1390                         $this->loadServicesPhp($config['services'],$configPath);
1391
1392                 if(isset($config['parameters']) && is_array($config['parameters']))
1393                         $this->loadParametersPhp($config['parameters'], $configPath);
1394
1395                 if(isset($config['includes']) && is_array($config['includes']))
1396                         $this->loadExternalXml($config['includes'],$configPath);
1397         }
1398
1399         /**
1400          * Parses the application configuration given in terms of a TXmlElement.
1401          * @param TXmlElement the XML element
1402          * @param string the context path (for specifying relative paths)
1403          */
1404         public function loadFromXml($dom,$configPath)
1405         {
1406                 // application properties
1407                 foreach($dom->getAttributes() as $name=>$value)
1408                 {
1409                         $this->_properties[$name]=$value;
1410                         $this->_empty=false;
1411                 }
1412
1413                 foreach($dom->getElements() as $element)
1414                 {
1415                         switch($element->getTagName())
1416                         {
1417                                 case 'paths':
1418                                         $this->loadPathsXml($element,$configPath);
1419                                         break;
1420                                 case 'modules':
1421                                         $this->loadModulesXml($element,$configPath);
1422                                         break;
1423                                 case 'services':
1424                                         $this->loadServicesXml($element,$configPath);
1425                                         break;
1426                                 case 'parameters':
1427                                         $this->loadParametersXml($element,$configPath);
1428                                         break;
1429                                 case 'include':
1430                                         $this->loadExternalXml($element,$configPath);
1431                                         break;
1432                                 default:
1433                                         //throw new TConfigurationException('appconfig_tag_invalid',$element->getTagName());
1434                                         break;
1435                         }
1436                 }
1437         }
1438
1439         /**
1440          * Loads the paths PHP array
1441          * @param array the paths PHP array
1442          * @param string the context path (for specifying relative paths)
1443          */
1444         protected function loadPathsPhp($pathsNode, $configPath)
1445         {
1446                 if(isset($pathsNode['aliases']) && is_array($pathsNode['aliases']))
1447                 {
1448                         foreach($pathsNode['aliases'] as $id=>$path)
1449                         {
1450                                 $path=str_replace('\\','/',$path);
1451                                 if(preg_match('/^\\/|.:\\/|.:\\\\/',$path))     // if absolute path
1452                                         $p=realpath($path);
1453                                 else
1454                                         $p=realpath($configPath.DIRECTORY_SEPARATOR.$path);
1455                                 if($p===false || !is_dir($p))
1456                                         throw new TConfigurationException('appconfig_aliaspath_invalid',$id,$path);
1457                                 if(isset($this->_aliases[$id]))
1458                                         throw new TConfigurationException('appconfig_alias_redefined',$id);
1459                                 $this->_aliases[$id]=$p;
1460                         }
1461                 }
1462
1463                 if(isset($pathsNode['using']) && is_array($pathsNode['using']))
1464                 {
1465                         foreach($pathsNode['using'] as $namespace)
1466                         {
1467                                 $this->_usings[] = $namespace;
1468                         }
1469                 }
1470         }
1471
1472         /**
1473          * Loads the paths XML node.
1474          * @param TXmlElement the paths XML node
1475          * @param string the context path (for specifying relative paths)
1476          */
1477         protected function loadPathsXml($pathsNode,$configPath)
1478         {
1479                 foreach($pathsNode->getElements() as $element)
1480                 {
1481                         switch($element->getTagName())
1482                         {
1483                                 case 'alias':
1484                                 {
1485                                         if(($id=$element->getAttribute('id'))!==null && ($path=$element->getAttribute('path'))!==null)
1486                                         {
1487                                                 $path=str_replace('\\','/',$path);
1488                                                 if(preg_match('/^\\/|.:\\/|.:\\\\/',$path))     // if absolute path
1489                                                         $p=realpath($path);
1490                                                 else
1491                                                         $p=realpath($configPath.DIRECTORY_SEPARATOR.$path);
1492                                                 if($p===false || !is_dir($p))
1493                                                         throw new TConfigurationException('appconfig_aliaspath_invalid',$id,$path);
1494                                                 if(isset($this->_aliases[$id]))
1495                                                         throw new TConfigurationException('appconfig_alias_redefined',$id);
1496                                                 $this->_aliases[$id]=$p;
1497                                         }
1498                                         else
1499                                                 throw new TConfigurationException('appconfig_alias_invalid');
1500                                         $this->_empty=false;
1501                                         break;
1502                                 }
1503                                 case 'using':
1504                                 {
1505                                         if(($namespace=$element->getAttribute('namespace'))!==null)
1506                                                 $this->_usings[]=$namespace;
1507                                         else
1508                                                 throw new TConfigurationException('appconfig_using_invalid');
1509                                         $this->_empty=false;
1510                                         break;
1511                                 }
1512                                 default:
1513                                         throw new TConfigurationException('appconfig_paths_invalid',$element->getTagName());
1514                         }
1515                 }
1516         }
1517
1518         /**
1519          * Loads the modules PHP array.
1520          * @param array the modules PHP array
1521          * @param string the context path (for specifying relative paths)
1522          */
1523         protected function loadModulesPhp($modulesNode, $configPath)
1524         {
1525                 foreach($modulesNode as $id=>$module)
1526                 {
1527                         if(!isset($module['class']))
1528                                 throw new TConfigurationException('appconfig_moduletype_required',$id);
1529                         $type = $module['class'];
1530                         unset($module['class']);
1531                         $properties = array();
1532                         if(isset($module['properties']))
1533                         {
1534                                 $properties = $module['properties'];
1535                                 unset($module['properties']);
1536                         }
1537                         $properties['id'] = $id;
1538                         $this->_modules[$id]=array($type,$properties,$module);
1539                         $this->_empty=false;
1540                 }
1541         }
1542
1543         /**
1544          * Loads the modules XML node.
1545          * @param TXmlElement the modules XML node
1546          * @param string the context path (for specifying relative paths)
1547          */
1548         protected function loadModulesXml($modulesNode,$configPath)
1549         {
1550                 foreach($modulesNode->getElements() as $element)
1551                 {
1552                         if($element->getTagName()==='module')
1553                         {
1554                                 $properties=$element->getAttributes();
1555                                 $id=$properties->itemAt('id');
1556                                 $type=$properties->remove('class');
1557                                 if($type===null)
1558                                         throw new TConfigurationException('appconfig_moduletype_required',$id);
1559                                 $element->setParent(null);
1560                                 if($id===null)
1561                                         $this->_modules[]=array($type,$properties->toArray(),$element);
1562                                 else
1563                                         $this->_modules[$id]=array($type,$properties->toArray(),$element);
1564                                 $this->_empty=false;
1565                         }
1566                         else
1567                                 throw new TConfigurationException('appconfig_modules_invalid',$element->getTagName());
1568                 }
1569         }
1570
1571         /**
1572          * Loads the services PHP array.
1573          * @param array the services PHP array
1574          * @param string the context path (for specifying relative paths)
1575          */
1576         protected function loadServicesPhp($servicesNode,$configPath)
1577         {
1578                 foreach($servicesNode as $id => $service)
1579                 {
1580                         if(!isset($service['class']))
1581                                 throw new TConfigurationException('appconfig_servicetype_required');
1582                         $type = $service['class'];
1583                         $properties = isset($service['properties']) ? $service['properties'] : array();
1584                         unset($service['properties']);
1585                         $properties['id'] = $id;
1586                         $this->_services[$id] = array($type,$properties,$service);
1587                         $this->_empty = false;
1588                 }
1589         }
1590
1591         /**
1592          * Loads the services XML node.
1593          * @param TXmlElement the services XML node
1594          * @param string the context path (for specifying relative paths)
1595          */
1596         protected function loadServicesXml($servicesNode,$configPath)
1597         {
1598                 foreach($servicesNode->getElements() as $element)
1599                 {
1600                         if($element->getTagName()==='service')
1601                         {
1602                                 $properties=$element->getAttributes();
1603                                 if(($id=$properties->itemAt('id'))===null)
1604                                         throw new TConfigurationException('appconfig_serviceid_required');
1605                                 if(($type=$properties->remove('class'))===null)
1606                                         throw new TConfigurationException('appconfig_servicetype_required',$id);
1607                                 $element->setParent(null);
1608                                 $this->_services[$id]=array($type,$properties->toArray(),$element);
1609                                 $this->_empty=false;
1610                         }
1611                         else
1612                                 throw new TConfigurationException('appconfig_services_invalid',$element->getTagName());
1613                 }
1614         }
1615
1616         /**
1617          * Loads the parameters PHP array.
1618          * @param array the parameters PHP array
1619          * @param string the context path (for specifying relative paths)
1620          */
1621         protected function loadParametersPhp($parametersNode,$configPath)
1622         {
1623                 foreach($parametersNode as $id => $parameter)
1624                 {
1625                         if(is_array($parameter))
1626                         {
1627                                 if(isset($parameter['class']))
1628                                 {
1629                                         $type = $parameter['class'];
1630                                         unset($parameter['class']);
1631                                         $properties = isset($service['properties']) ? $service['properties'] : array();
1632                                         $properties['id'] = $id;
1633                                         $this->_parameters[$id] = array($type,$properties);
1634                                 }
1635                         }
1636                         else
1637                         {
1638                                 $this->_parameters[$id] = $parameter;
1639                         }
1640                 }
1641         }
1642
1643         /**
1644          * Loads the parameters XML node.
1645          * @param TXmlElement the parameters XML node
1646          * @param string the context path (for specifying relative paths)
1647          */
1648         protected function loadParametersXml($parametersNode,$configPath)
1649         {
1650                 foreach($parametersNode->getElements() as $element)
1651                 {
1652                         if($element->getTagName()==='parameter')
1653                         {
1654                                 $properties=$element->getAttributes();
1655                                 if(($id=$properties->remove('id'))===null)
1656                                         throw new TConfigurationException('appconfig_parameterid_required');
1657                                 if(($type=$properties->remove('class'))===null)
1658                                 {
1659                                         if(($value=$properties->remove('value'))===null)
1660                                                 $this->_parameters[$id]=$element;
1661                                         else
1662                                                 $this->_parameters[$id]=$value;
1663                                 }
1664                                 else
1665                                         $this->_parameters[$id]=array($type,$properties->toArray());
1666                                 $this->_empty=false;
1667                         }
1668                         else
1669                                 throw new TConfigurationException('appconfig_parameters_invalid',$element->getTagName());
1670                 }
1671         }
1672
1673         /**
1674          * Loads the external PHP array.
1675          * @param array the application PHP array
1676          * @param string the context path (for specifying relative paths)
1677          */
1678         protected function loadExternalPhp($includeNode,$configPath)
1679         {
1680                 foreach($includeNode as $include)
1681                 {
1682                         $when = isset($include['when'])?true:false;
1683                         if(!isset($include['file']))
1684                                 throw new TConfigurationException('appconfig_includefile_required');
1685                         $filePath = $include['file'];
1686                         if(isset($this->_includes[$filePath]))
1687                                 $this->_includes[$filePath]='('.$this->_includes[$filePath].') || ('.$when.')';
1688                         else
1689                                 $$this->_includes[$filePath]=$when;
1690                         $this->_empty=false;
1691                 }
1692         }
1693
1694         /**
1695          * Loads the external XML configurations.
1696          * @param TXmlElement the application DOM element
1697          * @param string the context path (for specifying relative paths)
1698          */
1699         protected function loadExternalXml($includeNode,$configPath)
1700         {
1701                 if(($when=$includeNode->getAttribute('when'))===null)
1702                         $when=true;
1703                 if(($filePath=$includeNode->getAttribute('file'))===null)
1704                         throw new TConfigurationException('appconfig_includefile_required');
1705                 if(isset($this->_includes[$filePath]))
1706                         $this->_includes[$filePath]='('.$this->_includes[$filePath].') || ('.$when.')';
1707                 else
1708                         $this->_includes[$filePath]=$when;
1709                 $this->_empty=false;
1710         }
1711
1712         /**
1713          * Returns list of page initial property values.
1714          * Each array element represents a single property with the key
1715          * being the property name and the value the initial property value.
1716          * @return array list of page initial property values
1717          */
1718         public function getProperties()
1719         {
1720                 return $this->_properties;
1721         }
1722
1723         /**
1724          * Returns list of path alias definitions.
1725          * The definitions are aggregated (top-down) from configuration files along the path
1726          * to the specified page. Each array element represents a single alias definition,
1727          * with the key being the alias name and the value the absolute path.
1728          * @return array list of path alias definitions
1729          */
1730         public function getAliases()
1731         {
1732                 return $this->_aliases;
1733         }
1734
1735         /**
1736          * Returns list of namespaces to be used.
1737          * The namespaces are aggregated (top-down) from configuration files along the path
1738          * to the specified page. Each array element represents a single namespace usage,
1739          * with the value being the namespace to be used.
1740          * @return array list of namespaces to be used
1741          */
1742         public function getUsings()
1743         {
1744                 return $this->_usings;
1745         }
1746
1747         /**
1748          * Returns list of module configurations.
1749          * The module configurations are aggregated (top-down) from configuration files
1750          * along the path to the specified page. Each array element represents
1751          * a single module configuration, with the key being the module ID and
1752          * the value the module configuration. Each module configuration is
1753          * stored in terms of an array with the following content
1754          * ([0]=>module type, [1]=>module properties, [2]=>complete module configuration)
1755          * The module properties are an array of property values indexed by property names.
1756          * The complete module configuration is a TXmlElement object representing
1757          * the raw module configuration which may contain contents enclosed within
1758          * module tags.
1759          * @return array list of module configurations to be used
1760          */
1761         public function getModules()
1762         {
1763                 return $this->_modules;
1764         }
1765
1766         /**
1767          * @return array list of service configurations
1768          */
1769         public function getServices()
1770         {
1771                 return $this->_services;
1772         }
1773
1774         /**
1775          * Returns list of parameter definitions.
1776          * The parameter definitions are aggregated (top-down) from configuration files
1777          * along the path to the specified page. Each array element represents
1778          * a single parameter definition, with the key being the parameter ID and
1779          * the value the parameter definition. A parameter definition can be either
1780          * a string representing a string-typed parameter, or an array.
1781          * The latter defines a component-typed parameter whose format is as follows,
1782          * ([0]=>component type, [1]=>component properties)
1783          * The component properties are an array of property values indexed by property names.
1784          * @return array list of parameter definitions to be used
1785          */
1786         public function getParameters()
1787         {
1788                 return $this->_parameters;
1789         }
1790
1791         /**
1792          * @return array list of external configuration files. Each element is like $filePath=>$condition
1793          */
1794         public function getExternalConfigurations()
1795         {
1796                 return $this->_includes;
1797         }
1798 }
1799
1800 /**
1801  * TApplicationStatePersister class.
1802  * TApplicationStatePersister provides a file-based persistent storage
1803  * for application state. Application state, when serialized, is stored
1804  * in a file named 'global.cache' under the 'runtime' directory of the application.
1805  * Cache will be exploited if it is enabled.
1806  *
1807  * @author Qiang Xue <qiang.xue@gmail.com>
1808  * @package System
1809  * @since 3.0
1810  */
1811 class TApplicationStatePersister extends TModule implements IStatePersister
1812 {
1813         /**
1814          * Name of the value stored in cache
1815          */
1816         const CACHE_NAME='prado:appstate';
1817
1818         /**
1819          * Initializes module.
1820          * @param TXmlElement module configuration (may be null)
1821          */
1822         public function init($config)
1823         {
1824                 $this->getApplication()->setApplicationStatePersister($this);
1825         }
1826
1827         /**
1828          * @return string the file path storing the application state
1829          */
1830         protected function getStateFilePath()
1831         {
1832                 return $this->getApplication()->getRuntimePath().'/global.cache';
1833         }
1834
1835         /**
1836          * Loads application state from persistent storage.
1837          * @return mixed application state
1838          */
1839         public function load()
1840         {
1841                 if(($cache=$this->getApplication()->getCache())!==null && ($value=$cache->get(self::CACHE_NAME))!==false)
1842                         return unserialize($value);
1843                 else
1844                 {
1845                         if(($content=@file_get_contents($this->getStateFilePath()))!==false)
1846                                 return unserialize($content);
1847                         else
1848                                 return null;
1849                 }
1850         }
1851
1852         /**
1853          * Saves application state in persistent storage.
1854          * @param mixed application state
1855          */
1856         public function save($state)
1857         {
1858                 $content=serialize($state);
1859                 $saveFile=true;
1860                 if(($cache=$this->getApplication()->getCache())!==null)
1861                 {
1862                         if($cache->get(self::CACHE_NAME)===$content)
1863                                 $saveFile=false;
1864                         else
1865                                 $cache->set(self::CACHE_NAME,$content);
1866                 }
1867                 if($saveFile)
1868                 {
1869                         $fileName=$this->getStateFilePath();
1870                         file_put_contents($fileName,$content,LOCK_EX);
1871                 }
1872         }
1873
1874 }