3 * TApplication class file
5 * @author Qiang Xue <qiang.xue@gmail.com>
6 * @link https://github.com/pradosoft/prado
7 * @copyright Copyright © 2005-2016 The PRADO Group
8 * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
13 * Includes core interfaces essential for TApplication class
15 require_once(PRADO_DIR.'/interfaces.php');
18 * Includes core classes essential for TApplication class
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');
44 * TApplication coordinates modules and services, and serves as a configuration
45 * context for all Prado components.
47 * TApplication uses a configuration file to specify the settings of
48 * the application, the modules, the services, the parameters, and so on.
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.
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.
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
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}.
100 * - Create and run a Prado application:
102 * $application=new TApplication($configFile);
103 * $application->run();
106 * @author Qiang Xue <qiang.xue@gmail.com>
110 class TApplication extends TComponent
113 * possible application mode.
114 * @deprecated deprecated since version 3.0.4 (use TApplicationMode constants instead)
116 const STATE_OFF='Off';
117 const STATE_DEBUG='Debug';
118 const STATE_NORMAL='Normal';
119 const STATE_PERFORMANCE='Performance';
124 const PAGE_SERVICE_ID='page';
126 * Application configuration file name
128 const CONFIG_FILE_XML='application.xml';
130 * File extension for external config files
132 const CONFIG_FILE_EXT_XML='.xml';
134 * Configuration file type, application.xml and config.xml
136 const CONFIG_TYPE_XML = 'xml';
138 * Application configuration file name
140 const CONFIG_FILE_PHP='application.php';
142 * File extension for external config files
144 const CONFIG_FILE_EXT_PHP='.php';
146 * Configuration file type, application.php and config.php
148 const CONFIG_TYPE_PHP = 'php';
150 * Runtime directory name
152 const RUNTIME_PATH='runtime';
156 const CONFIGCACHE_FILE='config.cache';
160 const GLOBAL_FILE='global.cache';
163 * @var array list of events that define application lifecycles
165 private static $_steps=array(
168 'onLoadStateComplete',
170 'onAuthenticationComplete',
172 'onAuthorizationComplete',
176 'onSaveStateComplete',
182 * @var string application ID
186 * @var string unique application ID
190 * @var boolean whether the request is completed
192 private $_requestCompleted=false;
194 * @var integer application state
198 * @var array available services and their configurations indexed by service IDs
202 * @var IService current service instance
206 * @var array list of loaded application modules
208 private $_modules=array();
210 * @var array list of application modules yet to be loaded
212 private $_lazyModules=array();
214 * @var TMap list of application parameters
216 private $_parameters;
218 * @var string configuration file
220 private $_configFile;
222 * @var string configuration file extension
224 private $_configFileExt;
226 * @var string configuration type
228 private $_configType;
230 * @var string application base path
234 * @var string directory storing application state
236 private $_runtimePath;
238 * @var boolean if any global state is changed during the current request
240 private $_stateChanged=false;
242 * @var array global variables (persistent across sessions, requests)
244 private $_globals=array();
246 * @var string cache file
250 * @var TErrorHandler error handler module
252 private $_errorHandler;
254 * @var THttpRequest request module
258 * @var THttpResponse response module
262 * @var THttpSession session module, could be null
266 * @var ICache cache module, could be null
270 * @var IStatePersister application state persister
272 private $_statePersister;
274 * @var IUser user instance, could be null
278 * @var TGlobalization module, could be null
280 private $_globalization;
282 * @var TSecurityManager security manager module
286 * @var TAssetManager asset manager module
288 private $_assetManager;
290 * @var TAuthorizationRuleCollection collection of authorization rules
294 * @var TApplicationMode application mode
296 private $_mode=TApplicationMode::Debug;
299 * @var string Customizable page service ID
301 private $_pageServiceID = self::PAGE_SERVICE_ID;
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.
321 public function __construct($basePath='protected',$cacheConfig=true, $configType=self::CONFIG_TYPE_XML)
323 // register application as a singleton
324 Prado::setApplication($this);
325 $this->setConfigurationType($configType);
326 $this->resolvePaths($basePath);
329 $this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE;
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));
336 Prado::setPathOfAlias('Application',$this->_basePath);
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
346 * @see setRuntimePath
347 * @see setConfigurationFile
349 protected function resolvePaths($basePath)
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))
358 $configFile=$basePath;
359 $basePath=dirname($configFile);
364 // determine runtime path
365 $runtimePath=$basePath.DIRECTORY_SEPARATOR.self::RUNTIME_PATH;
366 if(is_writable($runtimePath))
368 if($configFile!==null)
370 $runtimePath.=DIRECTORY_SEPARATOR.basename($configFile).'-'.Prado::getVersion();
371 if(!is_dir($runtimePath))
373 if(@mkdir($runtimePath)===false)
374 throw new TConfigurationException('application_runtimepath_failed',$runtimePath);
375 @chmod($runtimePath, PRADO_CHMOD); //make it deletable
377 $this->setConfigurationFile($configFile);
379 $this->setBasePath($basePath);
380 $this->setRuntimePath($runtimePath);
383 throw new TConfigurationException('application_runtimepath_invalid',$runtimePath);
388 * Executes the lifecycles of the application.
389 * This is the main entry function that leads to the running of the whole
392 public function run()
396 $this->initApplication();
397 $n=count(self::$_steps);
399 $this->_requestCompleted=false;
400 while($this->_step<$n)
402 if($this->_mode===self::STATE_OFF)
403 throw new THttpException(503,'application_unavailable');
404 if($this->_requestCompleted)
406 $method=self::$_steps[$this->_step];
407 Prado::trace("Executing $method()",'System.TApplication');
416 $this->onEndRequest();
420 * Completes current request processing.
421 * This method can be used to exit the application lifecycles after finishing
424 public function completeRequest()
426 $this->_requestCompleted=true;
430 * @return boolean whether the current request is processed.
432 public function getRequestCompleted()
434 return $this->_requestCompleted;
438 * Returns a global value.
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
445 public function getGlobalState($key,$defaultValue=null)
447 return isset($this->_globals[$key])?$this->_globals[$key]:$defaultValue;
451 * Sets a global value.
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
460 public function setGlobalState($key,$value,$defaultValue=null,$forceSave=false)
462 $this->_stateChanged=true;
463 if($value===$defaultValue)
464 unset($this->_globals[$key]);
466 $this->_globals[$key]=$value;
468 $this->saveGlobals();
472 * Clears a global value.
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
477 public function clearGlobalState($key)
479 $this->_stateChanged=true;
480 unset($this->_globals[$key]);
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}.
489 protected function loadGlobals()
491 $this->_globals=$this->getApplicationStatePersister()->load();
495 * Saves global values into persistent storage.
496 * This method is invoked when {@link onSaveState OnSaveState} event is raised.
498 protected function saveGlobals()
500 if($this->_stateChanged)
502 $this->_stateChanged=false;
503 $this->getApplicationStatePersister()->save($this->_globals);
508 * @return string application ID
510 public function getID()
516 * @param string application ID
518 public function setID($value)
524 * @return string page service ID
526 public function getPageServiceID()
528 return $this->_pageServiceID;
532 * @param string page service ID
534 public function setPageServiceID($value)
536 $this->_pageServiceID=$value;
540 * @return string an ID that uniquely identifies this Prado application from the others
542 public function getUniqueID()
544 return $this->_uniqueID;
548 * @return TApplicationMode application mode. Defaults to TApplicationMode::Debug.
550 public function getMode()
556 * @param TApplicationMode application mode
558 public function setMode($value)
560 $this->_mode=TPropertyValue::ensureEnum($value,'TApplicationMode');
564 * @return string the directory containing the application configuration file (absolute path)
566 public function getBasePath()
568 return $this->_basePath;
572 * @param string the directory containing the application configuration file
574 public function setBasePath($value)
576 $this->_basePath=$value;
580 * @return string the application configuration file (absolute path)
582 public function getConfigurationFile()
584 return $this->_configFile;
588 * @param string the application configuration file (absolute path)
590 public function setConfigurationFile($value)
592 $this->_configFile=$value;
596 * @return string the application configuration file (absolute path)
598 public function getConfigurationType()
600 return $this->_configType;
604 * @param string the application configuration type. 'xml' and 'php' are valid values
606 public function setConfigurationType($value)
608 $this->_configType = $value;
612 * @return string the application configuration type. default is 'xml'
614 public function getConfigurationFileExt()
616 if($this->_configFileExt===null)
618 switch($this->_configType)
620 case TApplication::CONFIG_TYPE_PHP:
621 $this->_configFileExt = TApplication::CONFIG_FILE_EXT_PHP;
624 $this->_configFileExt = TApplication::CONFIG_FILE_EXT_XML;
627 return $this->_configFileExt;
631 * @return string the default configuration file name
633 public function getConfigurationFileName()
636 if($fileName == null)
638 switch($this->_configType)
640 case TApplication::CONFIG_TYPE_PHP:
641 $fileName = TApplication::CONFIG_FILE_PHP;
644 $fileName = TApplication::CONFIG_FILE_XML;
651 * @return string the directory storing cache data and application-level persistent data. (absolute path)
653 public function getRuntimePath()
655 return $this->_runtimePath;
659 * @param string the directory storing cache data and application-level persistent data. (absolute path)
661 public function setRuntimePath($value)
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);
671 * @return IService the currently requested service
673 public function getService()
675 return $this->_service;
679 * @param IService the currently requested service
681 public function setService($value)
683 $this->_service=$value;
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
692 public function setModule($id,IModule $module=null)
694 if(isset($this->_modules[$id]))
695 throw new TConfigurationException('application_moduleid_duplicated',$id);
697 $this->_modules[$id]=$module;
701 * @return IModule the module with the specified ID, null if not found
703 public function getModule($id)
705 if(!array_key_exists($id, $this->_modules))
708 // force loading of a lazy module
709 if($this->_modules[$id]===null)
711 $module = $this->internalLoadModule($id, true);
712 $module[0]->init($module[1]);
715 return $this->_modules[$id];
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
723 public function getModules()
725 return $this->_modules;
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
734 public function getParameters()
736 return $this->_parameters;
740 * @return THttpRequest the request module
742 public function getRequest()
746 $this->_request=new THttpRequest;
747 $this->_request->init(null);
749 return $this->_request;
753 * @param THttpRequest the request module
755 public function setRequest(THttpRequest $request)
757 $this->_request=$request;
761 * @return THttpResponse the response module
763 public function getResponse()
765 if(!$this->_response)
767 $this->_response=new THttpResponse;
768 $this->_response->init(null);
770 return $this->_response;
774 * @param THttpRequest the request module
776 public function setResponse(THttpResponse $response)
778 $this->_response=$response;
782 * @return THttpSession the session module, null if session module is not installed
784 public function getSession()
788 $this->_session=new THttpSession;
789 $this->_session->init(null);
791 return $this->_session;
795 * @param THttpSession the session module
797 public function setSession(THttpSession $session)
799 $this->_session=$session;
803 * @return TErrorHandler the error handler module
805 public function getErrorHandler()
807 if(!$this->_errorHandler)
809 $this->_errorHandler=new TErrorHandler;
810 $this->_errorHandler->init(null);
812 return $this->_errorHandler;
816 * @param TErrorHandler the error handler module
818 public function setErrorHandler(TErrorHandler $handler)
820 $this->_errorHandler=$handler;
824 * @return TSecurityManager the security manager module
826 public function getSecurityManager()
828 if(!$this->_security)
830 $this->_security=new TSecurityManager;
831 $this->_security->init(null);
833 return $this->_security;
837 * @param TSecurityManager the security manager module
839 public function setSecurityManager(TSecurityManager $sm)
841 $this->_security=$sm;
845 * @return TAssetManager asset manager
847 public function getAssetManager()
849 if(!$this->_assetManager)
851 $this->_assetManager=new TAssetManager;
852 $this->_assetManager->init(null);
854 return $this->_assetManager;
858 * @param TAssetManager asset manager
860 public function setAssetManager(TAssetManager $value)
862 $this->_assetManager=$value;
866 * @return IStatePersister application state persister
868 public function getApplicationStatePersister()
870 if(!$this->_statePersister)
872 $this->_statePersister=new TApplicationStatePersister;
873 $this->_statePersister->init(null);
875 return $this->_statePersister;
879 * @param IStatePersister application state persister
881 public function setApplicationStatePersister(IStatePersister $persister)
883 $this->_statePersister=$persister;
887 * @return ICache the cache module, null if cache module is not installed
889 public function getCache()
891 return $this->_cache;
895 * @param ICache the cache module
897 public function setCache(ICache $cache)
899 $this->_cache=$cache;
903 * @return IUser the application user
905 public function getUser()
911 * @param IUser the application user
913 public function setUser(IUser $user)
919 * @param boolean whether to create globalization if it does not exist
920 * @return TGlobalization globalization module
922 public function getGlobalization($createIfNotExists=true)
924 if($this->_globalization===null && $createIfNotExists)
926 $this->_globalization=new TGlobalization;
927 $this->_globalization->init(null);
929 return $this->_globalization;
933 * @param TGlobalization globalization module
935 public function setGlobalization(TGlobalization $glob)
937 $this->_globalization=$glob;
941 * @return TAuthorizationRuleCollection list of authorization rules for the current request
943 public function getAuthorizationRules()
945 if($this->_authRules===null)
946 $this->_authRules=new TAuthorizationRuleCollection;
947 return $this->_authRules;
950 protected function getApplicationConfigurationClass()
952 return 'TApplicationConfiguration';
955 protected function internalLoadModule($id, $force=false)
957 list($moduleClass, $initProperties, $configElement)=$this->_lazyModules[$id];
958 if(isset($initProperties['lazy']) && $initProperties['lazy'] && !$force)
960 Prado::trace("Postponed loading of lazy module $id ({$moduleClass})",'System.TApplication');
961 $this->setModule($id, null);
965 Prado::trace("Loading module $id ({$moduleClass})",'System.TApplication');
966 $module=Prado::createComponent($moduleClass);
967 foreach($initProperties as $name=>$value)
969 if($name==='lazy') continue;
970 $module->setSubProperty($name,$value);
972 $this->setModule($id,$module);
973 // keep the key to avoid reuse of the old module id
974 $this->_lazyModules[$id]=null;
976 return array($module,$configElement);
979 * Applies an application configuration.
980 * @param TApplicationConfiguration the configuration
981 * @param boolean whether the configuration is specified within a service.
983 public function applyConfiguration($config,$withinService=false)
985 if($config->getIsEmpty())
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);
994 // set application properties
997 foreach($config->getProperties() as $name=>$value)
998 $this->setSubProperty($name,$value);
1001 if(empty($this->_services))
1002 $this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null));
1005 foreach($config->getParameters() as $id=>$parameter)
1007 if(is_array($parameter))
1009 $component=Prado::createComponent($parameter[0]);
1010 foreach($parameter[1] as $name=>$value)
1011 $component->setSubProperty($name,$value);
1012 $this->_parameters->add($id,$component);
1015 $this->_parameters->add($id,$parameter);
1018 // load and init modules specified in app config
1020 foreach($config->getModules() as $id=>$moduleConfig)
1023 $id='_module'.count($this->_lazyModules);
1024 $this->_lazyModules[$id]=$moduleConfig;
1025 if($module = $this->internalLoadModule($id))
1028 foreach($modules as $module)
1029 $module[0]->init($module[1]);
1032 foreach($config->getServices() as $serviceID=>$serviceConfig)
1033 $this->_services[$serviceID]=$serviceConfig;
1035 // external configurations
1036 foreach($config->getExternalConfigurations() as $filePath=>$condition)
1038 if($condition!==true)
1039 $condition=$this->evaluateExpression($condition);
1042 if(($path=Prado::getPathOfNamespace($filePath,$this->getConfigurationFileExt()))===null || !is_file($path))
1043 throw new TConfigurationException('application_includefile_invalid',$filePath);
1044 $cn=$this->getApplicationConfigurationClass();
1046 $c->loadFromFile($path);
1047 $this->applyConfiguration($c,$withinService);
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
1061 protected function initApplication()
1063 Prado::trace('Initializing application','System.TApplication');
1065 if($this->_configFile!==null)
1067 if($this->_cacheFile===null || @filemtime($this->_cacheFile)<filemtime($this->_configFile))
1069 $config=new TApplicationConfiguration;
1070 $config->loadFromFile($this->_configFile);
1071 if($this->_cacheFile!==null)
1072 file_put_contents($this->_cacheFile,serialize($config),LOCK_EX);
1075 $config=unserialize(file_get_contents($this->_cacheFile));
1077 $this->applyConfiguration($config,false);
1080 if(($serviceID=$this->getRequest()->resolveRequest(array_keys($this->_services)))===null)
1081 $serviceID=$this->getPageServiceID();
1083 $this->startService($serviceID);
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
1092 public function startService($serviceID)
1094 if(isset($this->_services[$serviceID]))
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);
1105 foreach($initProperties as $name=>$value)
1106 $service->setSubProperty($name,$value);
1108 if($configElement!==null)
1110 $config=new TApplicationConfiguration;
1111 if($this->getConfigurationType()==self::CONFIG_TYPE_PHP)
1112 $config->loadFromPhp($configElement,$this->getBasePath());
1114 $config->loadFromXml($configElement,$this->getBasePath());
1115 $this->applyConfiguration($config,true);
1118 $service->init($configElement);
1121 throw new THttpException(500,'application_service_unknown',$serviceID);
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
1130 public function onError($param)
1132 Prado::log($param->getMessage(),TLogger::ERROR,'System.TApplication');
1133 $this->raiseEvent('OnError',$this,$param);
1134 $this->getErrorHandler()->handleError($this,$param);
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
1144 public function onBeginRequest()
1146 $this->raiseEvent('OnBeginRequest',$this,null);
1150 * Raises OnAuthentication event.
1151 * This method is invoked when the user request needs to be authenticated.
1153 public function onAuthentication()
1155 $this->raiseEvent('OnAuthentication',$this,null);
1159 * Raises OnAuthenticationComplete event.
1160 * This method is invoked right after the user request is authenticated.
1162 public function onAuthenticationComplete()
1164 $this->raiseEvent('OnAuthenticationComplete',$this,null);
1168 * Raises OnAuthorization event.
1169 * This method is invoked when the user request needs to be authorized.
1171 public function onAuthorization()
1173 $this->raiseEvent('OnAuthorization',$this,null);
1177 * Raises OnAuthorizationComplete event.
1178 * This method is invoked right after the user request is authorized.
1180 public function onAuthorizationComplete()
1182 $this->raiseEvent('OnAuthorizationComplete',$this,null);
1186 * Raises OnLoadState event.
1187 * This method is invoked when the application needs to load state (probably stored in session).
1189 public function onLoadState()
1191 $this->loadGlobals();
1192 $this->raiseEvent('OnLoadState',$this,null);
1196 * Raises OnLoadStateComplete event.
1197 * This method is invoked right after the application state has been loaded.
1199 public function onLoadStateComplete()
1201 $this->raiseEvent('OnLoadStateComplete',$this,null);
1205 * Raises OnPreRunService event.
1206 * This method is invoked right before the service is to be run.
1208 public function onPreRunService()
1210 $this->raiseEvent('OnPreRunService',$this,null);
1214 * Runs the requested service.
1216 public function runService()
1219 $this->_service->run();
1223 * Raises OnSaveState event.
1224 * This method is invoked when the application needs to save state (probably stored in session).
1226 public function onSaveState()
1228 $this->raiseEvent('OnSaveState',$this,null);
1229 $this->saveGlobals();
1233 * Raises OnSaveStateComplete event.
1234 * This method is invoked right after the application state has been saved.
1236 public function onSaveStateComplete()
1238 $this->raiseEvent('OnSaveStateComplete',$this,null);
1242 * Raises OnPreFlushOutput event.
1243 * This method is invoked right before the application flushes output to client.
1245 public function onPreFlushOutput()
1247 $this->raiseEvent('OnPreFlushOutput',$this,null);
1251 * Flushes output to client side.
1252 * @param boolean whether to continue buffering after flush if buffering was active
1254 public function flushOutput($continueBuffering = true)
1256 $this->getResponse()->flush($continueBuffering);
1260 * Raises OnEndRequest event.
1261 * This method is invoked when the application completes the processing of the request.
1263 public function onEndRequest()
1265 $this->flushOutput(false); // flush all remaining content in the buffer
1266 $this->saveGlobals(); // save global state
1267 $this->raiseEvent('OnEndRequest',$this,null);
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>
1284 class TApplicationMode extends TEnumerable
1287 const Debug='Debug';
1288 const Normal='Normal';
1289 const Performance='Performance';
1294 * TApplicationConfiguration class.
1296 * This class is used internally by TApplication to parse and represent application configuration.
1298 * @author Qiang Xue <qiang.xue@gmail.com>
1299 * @author Carl G. Mathisen <carlgmathisen@gmail.com>
1303 class TApplicationConfiguration extends TComponent
1306 * @var array list of application initial property values, indexed by property names
1308 private $_properties=array();
1310 * @var array list of namespaces to be used
1312 private $_usings=array();
1314 * @var array list of path aliases, indexed by alias names
1316 private $_aliases=array();
1318 * @var array list of module configurations
1320 private $_modules=array();
1322 * @var array list of service configurations
1324 private $_services=array();
1326 * @var array list of parameters
1328 private $_parameters=array();
1330 * @var array list of included configurations
1332 private $_includes=array();
1334 * @var boolean whether this configuration contains actual stuff
1336 private $_empty=true;
1339 * Parses the application configuration file.
1340 * @param string configuration file name
1341 * @throws TConfigurationException if there is any parsing error
1343 public function loadFromFile($fname)
1345 if(Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
1347 $fcontent = include $fname;
1348 $this->loadFromPhp($fcontent,dirname($fname));
1352 $dom=new TXmlDocument;
1353 $dom->loadFromFile($fname);
1354 $this->loadFromXml($dom,dirname($fname));
1359 * @return boolean whether this configuration contains actual stuff
1361 public function getIsEmpty()
1363 return $this->_empty;
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)
1371 public function loadFromPhp($config, $configPath)
1373 // application properties
1374 if(isset($config['application']))
1376 foreach($config['application'] as $name=>$value)
1378 $this->_properties[$name]=$value;
1380 $this->_empty = false;
1383 if(isset($config['paths']) && is_array($config['paths']))
1384 $this->loadPathsPhp($config['paths'],$configPath);
1386 if(isset($config['modules']) && is_array($config['modules']))
1387 $this->loadModulesPhp($config['modules'],$configPath);
1389 if(isset($config['services']) && is_array($config['services']))
1390 $this->loadServicesPhp($config['services'],$configPath);
1392 if(isset($config['parameters']) && is_array($config['parameters']))
1393 $this->loadParametersPhp($config['parameters'], $configPath);
1395 if(isset($config['includes']) && is_array($config['includes']))
1396 $this->loadExternalXml($config['includes'],$configPath);
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)
1404 public function loadFromXml($dom,$configPath)
1406 // application properties
1407 foreach($dom->getAttributes() as $name=>$value)
1409 $this->_properties[$name]=$value;
1410 $this->_empty=false;
1413 foreach($dom->getElements() as $element)
1415 switch($element->getTagName())
1418 $this->loadPathsXml($element,$configPath);
1421 $this->loadModulesXml($element,$configPath);
1424 $this->loadServicesXml($element,$configPath);
1427 $this->loadParametersXml($element,$configPath);
1430 $this->loadExternalXml($element,$configPath);
1433 //throw new TConfigurationException('appconfig_tag_invalid',$element->getTagName());
1440 * Loads the paths PHP array
1441 * @param array the paths PHP array
1442 * @param string the context path (for specifying relative paths)
1444 protected function loadPathsPhp($pathsNode, $configPath)
1446 if(isset($pathsNode['aliases']) && is_array($pathsNode['aliases']))
1448 foreach($pathsNode['aliases'] as $id=>$path)
1450 $path=str_replace('\\','/',$path);
1451 if(preg_match('/^\\/|.:\\/|.:\\\\/',$path)) // if absolute path
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;
1463 if(isset($pathsNode['using']) && is_array($pathsNode['using']))
1465 foreach($pathsNode['using'] as $namespace)
1467 $this->_usings[] = $namespace;
1473 * Loads the paths XML node.
1474 * @param TXmlElement the paths XML node
1475 * @param string the context path (for specifying relative paths)
1477 protected function loadPathsXml($pathsNode,$configPath)
1479 foreach($pathsNode->getElements() as $element)
1481 switch($element->getTagName())
1485 if(($id=$element->getAttribute('id'))!==null && ($path=$element->getAttribute('path'))!==null)
1487 $path=str_replace('\\','/',$path);
1488 if(preg_match('/^\\/|.:\\/|.:\\\\/',$path)) // if absolute path
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;
1499 throw new TConfigurationException('appconfig_alias_invalid');
1500 $this->_empty=false;
1505 if(($namespace=$element->getAttribute('namespace'))!==null)
1506 $this->_usings[]=$namespace;
1508 throw new TConfigurationException('appconfig_using_invalid');
1509 $this->_empty=false;
1513 throw new TConfigurationException('appconfig_paths_invalid',$element->getTagName());
1519 * Loads the modules PHP array.
1520 * @param array the modules PHP array
1521 * @param string the context path (for specifying relative paths)
1523 protected function loadModulesPhp($modulesNode, $configPath)
1525 foreach($modulesNode as $id=>$module)
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']))
1534 $properties = $module['properties'];
1535 unset($module['properties']);
1537 $properties['id'] = $id;
1538 $this->_modules[$id]=array($type,$properties,$module);
1539 $this->_empty=false;
1544 * Loads the modules XML node.
1545 * @param TXmlElement the modules XML node
1546 * @param string the context path (for specifying relative paths)
1548 protected function loadModulesXml($modulesNode,$configPath)
1550 foreach($modulesNode->getElements() as $element)
1552 if($element->getTagName()==='module')
1554 $properties=$element->getAttributes();
1555 $id=$properties->itemAt('id');
1556 $type=$properties->remove('class');
1558 throw new TConfigurationException('appconfig_moduletype_required',$id);
1559 $element->setParent(null);
1561 $this->_modules[]=array($type,$properties->toArray(),$element);
1563 $this->_modules[$id]=array($type,$properties->toArray(),$element);
1564 $this->_empty=false;
1567 throw new TConfigurationException('appconfig_modules_invalid',$element->getTagName());
1572 * Loads the services PHP array.
1573 * @param array the services PHP array
1574 * @param string the context path (for specifying relative paths)
1576 protected function loadServicesPhp($servicesNode,$configPath)
1578 foreach($servicesNode as $id => $service)
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;
1592 * Loads the services XML node.
1593 * @param TXmlElement the services XML node
1594 * @param string the context path (for specifying relative paths)
1596 protected function loadServicesXml($servicesNode,$configPath)
1598 foreach($servicesNode->getElements() as $element)
1600 if($element->getTagName()==='service')
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;
1612 throw new TConfigurationException('appconfig_services_invalid',$element->getTagName());
1617 * Loads the parameters PHP array.
1618 * @param array the parameters PHP array
1619 * @param string the context path (for specifying relative paths)
1621 protected function loadParametersPhp($parametersNode,$configPath)
1623 foreach($parametersNode as $id => $parameter)
1625 if(is_array($parameter))
1627 if(isset($parameter['class']))
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);
1638 $this->_parameters[$id] = $parameter;
1644 * Loads the parameters XML node.
1645 * @param TXmlElement the parameters XML node
1646 * @param string the context path (for specifying relative paths)
1648 protected function loadParametersXml($parametersNode,$configPath)
1650 foreach($parametersNode->getElements() as $element)
1652 if($element->getTagName()==='parameter')
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)
1659 if(($value=$properties->remove('value'))===null)
1660 $this->_parameters[$id]=$element;
1662 $this->_parameters[$id]=$value;
1665 $this->_parameters[$id]=array($type,$properties->toArray());
1666 $this->_empty=false;
1669 throw new TConfigurationException('appconfig_parameters_invalid',$element->getTagName());
1674 * Loads the external PHP array.
1675 * @param array the application PHP array
1676 * @param string the context path (for specifying relative paths)
1678 protected function loadExternalPhp($includeNode,$configPath)
1680 foreach($includeNode as $include)
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.')';
1689 $$this->_includes[$filePath]=$when;
1690 $this->_empty=false;
1695 * Loads the external XML configurations.
1696 * @param TXmlElement the application DOM element
1697 * @param string the context path (for specifying relative paths)
1699 protected function loadExternalXml($includeNode,$configPath)
1701 if(($when=$includeNode->getAttribute('when'))===null)
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.')';
1708 $this->_includes[$filePath]=$when;
1709 $this->_empty=false;
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
1718 public function getProperties()
1720 return $this->_properties;
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
1730 public function getAliases()
1732 return $this->_aliases;
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
1742 public function getUsings()
1744 return $this->_usings;
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
1759 * @return array list of module configurations to be used
1761 public function getModules()
1763 return $this->_modules;
1767 * @return array list of service configurations
1769 public function getServices()
1771 return $this->_services;
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
1786 public function getParameters()
1788 return $this->_parameters;
1792 * @return array list of external configuration files. Each element is like $filePath=>$condition
1794 public function getExternalConfigurations()
1796 return $this->_includes;
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.
1807 * @author Qiang Xue <qiang.xue@gmail.com>
1811 class TApplicationStatePersister extends TModule implements IStatePersister
1814 * Name of the value stored in cache
1816 const CACHE_NAME='prado:appstate';
1819 * Initializes module.
1820 * @param TXmlElement module configuration (may be null)
1822 public function init($config)
1824 $this->getApplication()->setApplicationStatePersister($this);
1828 * @return string the file path storing the application state
1830 protected function getStateFilePath()
1832 return $this->getApplication()->getRuntimePath().'/global.cache';
1836 * Loads application state from persistent storage.
1837 * @return mixed application state
1839 public function load()
1841 if(($cache=$this->getApplication()->getCache())!==null && ($value=$cache->get(self::CACHE_NAME))!==false)
1842 return unserialize($value);
1845 if(($content=@file_get_contents($this->getStateFilePath()))!==false)
1846 return unserialize($content);
1853 * Saves application state in persistent storage.
1854 * @param mixed application state
1856 public function save($state)
1858 $content=serialize($state);
1860 if(($cache=$this->getApplication()->getCache())!==null)
1862 if($cache->get(self::CACHE_NAME)===$content)
1865 $cache->set(self::CACHE_NAME,$content);
1869 $fileName=$this->getStateFilePath();
1870 file_put_contents($fileName,$content,LOCK_EX);