AnonSec Team
Server IP : 10.2.73.233  /  Your IP : 216.73.216.59
Web Server : Apache/2.4.59 (Debian)
System : Linux polon 4.19.0-27-amd64 #1 SMP Debian 4.19.316-1 (2024-06-25) x86_64
User : www-data ( 33)
PHP Version : 5.6.40-64+0~20230107.71+debian10~1.gbp673146
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,
MySQL : ON  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON
Directory (0755) :  /home/ifk/web/assets/../prado4.3.2/

[  Home  ][  C0mmand  ][  Upload File  ]

Current File : /home/ifk/web/assets/../prado4.3.2/TApplication.php
<?php
/**
 * TApplication class file
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link https://github.com/pradosoft/prado
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
 */

namespace Prado;

use Prado\Exceptions\TErrorHandler;
use Prado\Exceptions\THttpException;
use Prado\Exceptions\TConfigurationException;
use Prado\I18N\TGlobalization;
use Prado\Security\TSecurityManager;
use Prado\Web\TAssetManager;
use Prado\Web\THttpRequest;
use Prado\Web\THttpResponse;
use Prado\Web\THttpSession;
use Prado\Util\TLogger;

/**
 * TApplication class.
 *
 * TApplication coordinates modules and services, and serves as a configuration
 * context for all Prado components.
 *
 * TApplication uses a configuration file to specify the settings of
 * the application, the modules, the services, the parameters, and so on.
 *
 * TApplication adopts a modular structure. A TApplication instance is a composition
 * of multiple modules. A module is an instance of class implementing
 * {@link IModule} interface. Each module accomplishes certain functionalities
 * that are shared by all Prado components in an application.
 * There are default modules, composer modules, and user-defined modules. The latter
 * offers extreme flexibility of extending TApplication in a plug-and-play fashion.
 * Modules cooperate with each other to serve a user request by following
 * a sequence of lifecycles predefined in TApplication.
 *
 * TApplicationConfiguration loads the composer.json for each installed composer extension
 * and checks the extra field for a "bootstrap" class for the package.
 * Packages can be specified as a configuration module (without a class) to load the
 * composer extension module.  The ID of the module is the name of the package.
 *
 * TApplication has four modes that can be changed by setting {@link setMode Mode}
 * property (in the application configuration file).
 * - <b>Off</b> mode will prevent the application from serving user requests.
 * - <b>Debug</b> mode is mainly used during application development. It ensures
 *   the cache is always up-to-date if caching is enabled. It also allows
 *   exceptions are displayed with rich context information if they occur.
 * - <b>Normal</b> mode is mainly used during production stage. Exception information
 *   will only be recorded in system error logs. The cache is ensured to be
 *   up-to-date if it is enabled.
 * - <b>Performance</b> mode is similar to <b>Normal</b> mode except that it
 *   does not ensure the cache is up-to-date.
 *
 * TApplication dispatches each user request to a particular service which
 * finishes the actual work for the request with the aid from the application
 * modules.
 *
 * TApplication maintains a lifecycle with the following stages:
 * - [construct] : construction of the application instance
 * - [initApplication] : load application configuration and instantiate modules and the requested service
 * - onInitComplete : this event happens right after after module and service initialization. This event is particularly useful for CLI/Shell applications
 * - onBeginRequest : this event happens right after application initialization
 * - onAuthentication : this event happens when authentication is needed for the current request
 * - onAuthenticationComplete : this event happens right after the authentication is done for the current request
 * - onAuthorization : this event happens when authorization is needed for the current request
 * - onAuthorizationComplete : this event happens right after the authorization is done for the current request
 * - onLoadState : this event happens when application state needs to be loaded
 * - onLoadStateComplete : this event happens right after the application state is loaded
 * - onPreRunService : this event happens right before the requested service is to run
 * - runService : the requested service runs
 * - onSaveState : this event happens when application needs to save its state
 * - onSaveStateComplete : this event happens right after the application saves its state
 * - onPreFlushOutput : this event happens right before the application flushes output to client side.
 * - flushOutput : the application flushes output to client side.
 * - onEndRequest : this is the last stage a request is being completed
 * - [destruct] : destruction of the application instance
 * Modules and services can attach their methods to one or several of the above
 * events and do appropriate processing when the events are raised. By this way,
 * the application is able to coordinate the activities of modules and services
 * in the above order. To terminate an application before the whole lifecycle
 * completes, call {@link completeRequest}.
 *
 * Examples:
 * - Create and run a Prado application:
 * <code>
 * $application=new TApplication($configFile);
 * $application->run();
 * </code>
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 3.0
 */
class TApplication extends \Prado\TComponent
{
	/**
	 * Page service ID
	 */
	public const PAGE_SERVICE_ID = 'page';
	/**
	 * Application configuration file name
	 */
	public const CONFIG_FILE_XML = 'application.xml';
	/**
	 * File extension for external config files
	 */
	public const CONFIG_FILE_EXT_XML = '.xml';
	/**
	 * Configuration file type, application.xml and config.xml
	 */
	public const CONFIG_TYPE_XML = 'xml';
	/**
	 * Application configuration file name
	 */
	public const CONFIG_FILE_PHP = 'application.php';
	/**
	 * File extension for external config files
	 */
	public const CONFIG_FILE_EXT_PHP = '.php';
	/**
	 * Configuration file type, application.php and config.php
	 */
	public const CONFIG_TYPE_PHP = 'php';
	/**
	 * Runtime directory name
	 */
	public const RUNTIME_PATH = 'runtime';
	/**
	 * Config cache file
	 */
	public const CONFIGCACHE_FILE = 'config.cache';
	/**
	 * Global data file
	 */
	public const GLOBAL_FILE = 'global.cache';

	/**
	 * @var array list of events that define application lifecycles
	 */
	private static $_steps = [
		'onBeginRequest',
		'onLoadState',
		'onLoadStateComplete',
		'onAuthentication',
		'onAuthenticationComplete',
		'onAuthorization',
		'onAuthorizationComplete',
		'onPreRunService',
		'runService',
		'onSaveState',
		'onSaveStateComplete',
		'onPreFlushOutput',
		'flushOutput'
	];

	/**
	 * @var string application ID
	 */
	private $_id;
	/**
	 * @var string unique application ID
	 */
	private $_uniqueID;
	/**
	 * @var bool whether the request is completed
	 */
	private $_requestCompleted = false;
	/**
	 * @var int application state
	 */
	private $_step;
	/**
	 * @var array available services and their configurations indexed by service IDs
	 */
	private $_services;
	/**
	 * @var IService current service instance
	 */
	private $_service;
	/**
	 * @var array list of loaded application modules
	 */
	private $_modules = [];
	/**
	 * @var array list of application modules yet to be loaded
	 */
	private $_lazyModules = [];
	/**
	 * @var \Prado\Collections\TMap list of application parameters
	 */
	private $_parameters;
	/**
	 * @var string configuration file
	 */
	private $_configFile;
	/**
	 * @var string configuration file extension
	 */
	private $_configFileExt;
	/**
	 * @var string configuration type
	 */
	private $_configType;
	/**
	 * @var string application base path
	 */
	private $_basePath;
	/**
	 * @var string directory storing application state
	 */
	private $_runtimePath;
	/**
	 * @var bool if any global state is changed during the current request
	 */
	private $_stateChanged = false;
	/**
	 * @var array global variables (persistent across sessions, requests)
	 */
	private $_globals = [];
	/**
	 * @var string cache file
	 */
	private $_cacheFile;
	/**
	 * @var TErrorHandler error handler module
	 */
	private $_errorHandler;
	/**
	 * @var THttpRequest request module
	 */
	private $_request;
	/**
	 * @var THttpResponse response module
	 */
	private $_response;
	/**
	 * @var THttpSession session module, could be null
	 */
	private $_session;
	/**
	 * @var \Prado\Caching\ICache cache module, could be null
	 */
	private $_cache;
	/**
	 * @var IStatePersister application state persister
	 */
	private $_statePersister;
	/**
	 * @var \Prado\Security\IUser user instance, could be null
	 */
	private $_user;
	/**
	 * @var TGlobalization module, could be null
	 */
	private $_globalization;
	/**
	 * @var TSecurityManager security manager module
	 */
	private $_security;
	/**
	 * @var TAssetManager asset manager module
	 */
	private $_assetManager;
	/**
	 * @var \Prado\Security\TAuthorizationRuleCollection collection of authorization rules
	 */
	private $_authRules;
	/**
	 * @var string|TApplicationMode application mode
	 */
	private $_mode = TApplicationMode::Debug;

	/**
	 * @var string Customizable page service ID
	 */
	private $_pageServiceID = self::PAGE_SERVICE_ID;

	/**
	 * Constructor.
	 * Sets application base path and initializes the application singleton.
	 * Application base path refers to the root directory storing application
	 * data and code not directly accessible by Web users.
	 * By default, the base path is assumed to be the <b>protected</b>
	 * directory under the directory containing the current running script.
	 * @param string $basePath application base path or configuration file path.
	 *        If the parameter is a file, it is assumed to be the application
	 *        configuration file, and the directory containing the file is treated
	 *        as the application base path.
	 *        If it is a directory, it is assumed to be the application base path,
	 *        and within that directory, a file named <b>application.xml</b>
	 *        will be looked for. If found, the file is considered as the application
	 *        configuration file.
	 * @param bool $cacheConfig whether to cache application configuration. Defaults to true.
	 * @param string $configType configuration type. Defaults to CONFIG_TYPE_XML.
	 * @throws TConfigurationException if configuration file cannot be read or the runtime path is invalid.
	 */
	public function __construct($basePath = 'protected', $cacheConfig = true, $configType = self::CONFIG_TYPE_XML)
	{
		// register application as a singleton
		Prado::setApplication($this);
		$this->setConfigurationType($configType);
		$this->resolvePaths($basePath);

		if ($cacheConfig) {
			$this->_cacheFile = $this->_runtimePath . DIRECTORY_SEPARATOR . self::CONFIGCACHE_FILE;
		}

		// generates unique ID by hashing the runtime path
		$this->_uniqueID = md5($this->_runtimePath);
		$this->_parameters = new \Prado\Collections\TMap();
		$this->_services = [$this->getPageServiceID() => ['TPageService', [], null]];

		Prado::setPathOfAlias('Application', $this->_basePath);
		parent::__construct();
	}

	/**
	 * Resolves application-relevant paths.
	 * This method is invoked by the application constructor
	 * to determine the application configuration file,
	 * application root path and the runtime path.
	 * @param string $basePath the application root path or the application configuration file
	 * @see setBasePath
	 * @see setRuntimePath
	 * @see setConfigurationFile
	 */
	protected function resolvePaths($basePath)
	{
		// determine configuration path and file
		if (empty($basePath) || ($basePath = realpath($basePath)) === false) {
			throw new TConfigurationException('application_basepath_invalid', $basePath);
		}
		if (is_dir($basePath) && is_file($basePath . DIRECTORY_SEPARATOR . $this->getConfigurationFileName())) {
			$configFile = $basePath . DIRECTORY_SEPARATOR . $this->getConfigurationFileName();
		} elseif (is_file($basePath)) {
			$configFile = $basePath;
			$basePath = dirname($configFile);
		} else {
			$configFile = null;
		}

		// determine runtime path
		$runtimePath = $basePath . DIRECTORY_SEPARATOR . self::RUNTIME_PATH;
		if (is_writable($runtimePath)) {
			if ($configFile !== null) {
				$runtimePath .= DIRECTORY_SEPARATOR . basename($configFile) . '-' . Prado::getVersion();
				if (!is_dir($runtimePath)) {
					if (@mkdir($runtimePath) === false) {
						throw new TConfigurationException('application_runtimepath_failed', $runtimePath);
					}
					@chmod($runtimePath, Prado::getDefaultPermissions()); //make it deletable
				}
				$this->setConfigurationFile($configFile);
			}
			$this->setBasePath($basePath);
			$this->setRuntimePath($runtimePath);
		} else {
			throw new TConfigurationException('application_runtimepath_invalid', $runtimePath);
		}
	}

	/**
	 * Executes the lifecycles of the application.
	 * This is the main entry function that leads to the running of the whole
	 * Prado application.
	 */
	public function run()
	{
		try {
			$this->initApplication();
			$n = count(self::$_steps);
			$this->_step = 0;
			$this->_requestCompleted = false;
			while ($this->_step < $n) {
				if ($this->_mode === TApplicationMode::Off) {
					throw new THttpException(503, 'application_unavailable');
				}
				if ($this->_requestCompleted) {
					break;
				}
				$method = self::$_steps[$this->_step];
				Prado::trace("Executing $method()", 'Prado\TApplication');
				$this->$method();
				$this->_step++;
			}
		} catch (\Exception $e) {
			$this->onError($e);
		}
		$this->onEndRequest();
	}

	/**
	 * Completes current request processing.
	 * This method can be used to exit the application lifecycles after finishing
	 * the current cycle.
	 */
	public function completeRequest()
	{
		$this->_requestCompleted = true;
	}

	/**
	 * @return bool whether the current request is processed.
	 */
	public function getRequestCompleted()
	{
		return $this->_requestCompleted;
	}

	/**
	 * Returns a global value.
	 *
	 * A global value is one that is persistent across users sessions and requests.
	 * @param string $key the name of the value to be returned
	 * @param mixed $defaultValue the default value. If $key is not found, $defaultValue will be returned
	 * @return mixed the global value corresponding to $key
	 */
	public function getGlobalState($key, $defaultValue = null)
	{
		return $this->_globals[$key] ?? $defaultValue;
	}

	/**
	 * Sets a global value.
	 *
	 * A global value is one that is persistent across users sessions and requests.
	 * Make sure that the value is serializable and unserializable.
	 * @param string $key the name of the value to be set
	 * @param mixed $value the global value to be set
	 * @param null|mixed $defaultValue the default value. If $key is not found, $defaultValue will be returned
	 * @param bool $forceSave wheter to force an immediate GlobalState save. defaults to false
	 */
	public function setGlobalState($key, $value, $defaultValue = null, $forceSave = false)
	{
		$this->_stateChanged = true;
		if ($value === $defaultValue) {
			unset($this->_globals[$key]);
		} else {
			$this->_globals[$key] = $value;
		}
		if ($forceSave) {
			$this->saveGlobals();
		}
	}

	/**
	 * Clears a global value.
	 *
	 * The value cleared will no longer be available in this request and the following requests.
	 * @param string $key the name of the value to be cleared
	 */
	public function clearGlobalState($key)
	{
		$this->_stateChanged = true;
		unset($this->_globals[$key]);
	}

	/**
	 * Loads global values from persistent storage.
	 * This method is invoked when {@link onLoadState OnLoadState} event is raised.
	 * After this method, values that are stored in previous requests become
	 * available to the current request via {@link getGlobalState}.
	 */
	protected function loadGlobals()
	{
		$this->_globals = $this->getApplicationStatePersister()->load();
	}

	/**
	 * Saves global values into persistent storage.
	 * This method is invoked when {@link onSaveState OnSaveState} event is raised.
	 */
	protected function saveGlobals()
	{
		if ($this->_stateChanged) {
			$this->_stateChanged = false;
			$this->getApplicationStatePersister()->save($this->_globals);
		}
	}

	/**
	 * @return string application ID
	 */
	public function getID()
	{
		return $this->_id;
	}

	/**
	 * @param string $value application ID
	 */
	public function setID($value)
	{
		$this->_id = $value;
	}

	/**
	 * @return string page service ID
	 */
	public function getPageServiceID()
	{
		return $this->_pageServiceID;
	}

	/**
	 * @param string $value page service ID
	 */
	public function setPageServiceID($value)
	{
		$this->_pageServiceID = $value;
	}

	/**
	 * @return string an ID that uniquely identifies this Prado application from the others
	 */
	public function getUniqueID()
	{
		return $this->_uniqueID;
	}

	/**
	 * @return string|TApplicationMode application mode. Defaults to TApplicationMode::Debug.
	 */
	public function getMode()
	{
		return $this->_mode;
	}

	/**
	 * @param TApplicationMode $value application mode
	 */
	public function setMode($value)
	{
		$this->_mode = TPropertyValue::ensureEnum($value, '\Prado\TApplicationMode');
	}

	/**
	 * @return string the directory containing the application configuration file (absolute path)
	 */
	public function getBasePath()
	{
		return $this->_basePath;
	}

	/**
	 * @param string $value the directory containing the application configuration file
	 */
	public function setBasePath($value)
	{
		$this->_basePath = $value;
	}

	/**
	 * @return string the application configuration file (absolute path)
	 */
	public function getConfigurationFile()
	{
		return $this->_configFile;
	}

	/**
	 * @param string $value the application configuration file (absolute path)
	 */
	public function setConfigurationFile($value)
	{
		$this->_configFile = $value;
	}

	/**
	 * @return string the application configuration file (absolute path)
	 */
	public function getConfigurationType()
	{
		return $this->_configType;
	}

	/**
	 * @param string $value the application configuration type. 'xml' and 'php' are valid values
	 */
	public function setConfigurationType($value)
	{
		$this->_configType = $value;
	}

	/**
	 * @return string the application configuration type. default is 'xml'
	 */
	public function getConfigurationFileExt()
	{
		if ($this->_configFileExt === null) {
			switch ($this->_configType) {
				case TApplication::CONFIG_TYPE_PHP:
					$this->_configFileExt = TApplication::CONFIG_FILE_EXT_PHP;
					break;
				default:
					$this->_configFileExt = TApplication::CONFIG_FILE_EXT_XML;
			}
		}
		return $this->_configFileExt;
	}

	/**
	 * @return string the default configuration file name
	 */
	public function getConfigurationFileName()
	{
		static $fileName;
		if ($fileName == null) {
			switch ($this->_configType) {
				case TApplication::CONFIG_TYPE_PHP:
					$fileName = TApplication::CONFIG_FILE_PHP;
					break;
				default:
					$fileName = TApplication::CONFIG_FILE_XML;
			}
		}
		return $fileName;
	}

	/**
	 * @return string the directory storing cache data and application-level persistent data. (absolute path)
	 */
	public function getRuntimePath()
	{
		return $this->_runtimePath;
	}

	/**
	 * @param string $value the directory storing cache data and application-level persistent data. (absolute path)
	 */
	public function setRuntimePath($value)
	{
		$this->_runtimePath = $value;
		if ($this->_cacheFile) {
			$this->_cacheFile = $this->_runtimePath . DIRECTORY_SEPARATOR . self::CONFIGCACHE_FILE;
		}
		// generates unique ID by hashing the runtime path
		$this->_uniqueID = md5($this->_runtimePath);
	}

	/**
	 * @return TService the currently requested service
	 */
	public function getService()
	{
		return $this->_service;
	}

	/**
	 * @param IService $value the currently requested service
	 */
	public function setService($value)
	{
		$this->_service = $value;
	}

	/**
	 * Adds a module to application.
	 * Note, this method does not do module initialization.
	 * @param string $id ID of the module
	 * @param null|IModule $module module object or null if the module has not been loaded yet
	 */
	public function setModule($id, IModule $module = null)
	{
		if (isset($this->_modules[$id])) {
			throw new TConfigurationException('application_moduleid_duplicated', $id);
		} else {
			$this->_modules[$id] = $module;
		}
	}

	/**
	 * @param mixed $id
	 * @return null|TModule the module with the specified ID, null if not found
	 */
	public function getModule($id)
	{
		if (!array_key_exists($id, $this->_modules)) {
			return null;
		}

		// force loading of a lazy module
		if ($this->_modules[$id] === null) {
			$module = $this->internalLoadModule($id, true);
			$module[0]->init($module[1]);
		}

		return $this->_modules[$id];
	}

	/**
	 * Returns a list of application modules indexed by module IDs.
	 * Modules that have not been loaded yet are returned as null objects.
	 * @return array<TModule> list of loaded application modules, indexed by module IDs
	 */
	public function getModules()
	{
		return $this->_modules;
	}

	/**
	 * Returns a list of application modules of a specific class.
	 * Lazy Loading Modules are not loaded, and are null but have an ID Key.
	 * When null modules are found, load them with {@link getModule}. eg.
	 * <code>
	 *	foreach (Prado::getApplication()->getModulesByType('Prado\\Caching\\ICache') as $id => $module) {
	 *		$module = (!$module) ? $app->getModule($id) : $module;
	 *		...
	 *	}
	 * </code>
	 * @param string $type class name of the modules to look for.
	 * @param bool $strict should the module be the class or can the module be a subclass
	 * @return array keys are the ids of the module and values are module of a specific class
	 * @since 4.2.0
	 */
	public function getModulesByType($type, $strict = false)
	{
		$m = [];
		foreach ($this->_modules as $id => $module) {
			if ($module === null && isset($this->_lazyModules[$id])) {
				[$moduleClass, $initProperties, $configElement] = $this->_lazyModules[$id];
				if ($strict ? ($moduleClass === $type) : ($moduleClass instanceof $type)) {
					$m[$id] = null;
				}
			} elseif ($module !== null && ($strict ? (get_class($module) === $type) : $module->isa($type))) {
				$m[$id] = $module;
			}
		}
		return $m;
	}

	/**
	 * Returns the list of application parameters.
	 * Since the parameters are returned as a {@link \Prado\Collections\TMap} object, you may use
	 * the returned result to access, add or remove individual parameters.
	 * @return \Prado\Collections\TMap the list of application parameters
	 */
	public function getParameters()
	{
		return $this->_parameters;
	}

	/**
	 * @return THttpRequest the request module
	 */
	public function getRequest()
	{
		if (!$this->_request) {
			$this->_request = new \Prado\Web\THttpRequest();
			$this->_request->init(null);
		}
		return $this->_request;
	}

	/**
	 * @param THttpRequest $request the request module
	 */
	public function setRequest(THttpRequest $request)
	{
		$this->_request = $request;
	}

	/**
	 * @return THttpResponse the response module
	 */
	public function getResponse()
	{
		if (!$this->_response) {
			$this->_response = new THttpResponse();
			$this->_response->init(null);
		}
		return $this->_response;
	}

	/**
	 * @param THttpResponse $response the request module
	 */
	public function setResponse(THttpResponse $response)
	{
		$this->_response = $response;
	}

	/**
	 * @return THttpSession the session module, null if session module is not installed
	 */
	public function getSession()
	{
		if (!$this->_session) {
			$this->_session = new THttpSession();
			$this->_session->init(null);
		}
		return $this->_session;
	}

	/**
	 * @param THttpSession $session the session module
	 */
	public function setSession(THttpSession $session)
	{
		$this->_session = $session;
	}

	/**
	 * @return TErrorHandler the error handler module
	 */
	public function getErrorHandler()
	{
		if (!$this->_errorHandler) {
			$this->_errorHandler = new TErrorHandler();
			$this->_errorHandler->init(null);
		}
		return $this->_errorHandler;
	}

	/**
	 * @param TErrorHandler $handler the error handler module
	 */
	public function setErrorHandler(TErrorHandler $handler)
	{
		$this->_errorHandler = $handler;
	}

	/**
	 * @return TSecurityManager the security manager module
	 */
	public function getSecurityManager()
	{
		if (!$this->_security) {
			$this->_security = new TSecurityManager();
			$this->_security->init(null);
		}
		return $this->_security;
	}

	/**
	 * @param TSecurityManager $sm the security manager module
	 */
	public function setSecurityManager(TSecurityManager $sm)
	{
		$this->_security = $sm;
	}

	/**
	 * @return TAssetManager asset manager
	 */
	public function getAssetManager()
	{
		if (!$this->_assetManager) {
			$this->_assetManager = new TAssetManager();
			$this->_assetManager->init(null);
		}
		return $this->_assetManager;
	}

	/**
	 * @param TAssetManager $value asset manager
	 */
	public function setAssetManager(TAssetManager $value)
	{
		$this->_assetManager = $value;
	}

	/**
	 * @return IStatePersister application state persister
	 */
	public function getApplicationStatePersister()
	{
		if (!$this->_statePersister) {
			$this->_statePersister = new TApplicationStatePersister();
			$this->_statePersister->init(null);
		}
		return $this->_statePersister;
	}

	/**
	 * @param IStatePersister $persister application state persister
	 */
	public function setApplicationStatePersister(IStatePersister $persister)
	{
		$this->_statePersister = $persister;
	}

	/**
	 * @return null|\Prado\Caching\ICache the cache module, null if cache module is not installed
	 */
	public function getCache()
	{
		return $this->_cache;
	}

	/**
	 * @param \Prado\Caching\ICache $cache the cache module
	 */
	public function setCache(\Prado\Caching\ICache $cache)
	{
		$this->_cache = $cache;
	}

	/**
	 * @return \Prado\Security\IUser the application user
	 */
	public function getUser()
	{
		return $this->_user;
	}

	/**
	 * @param \Prado\Security\IUser $user the application user
	 */
	public function setUser(\Prado\Security\IUser $user)
	{
		$this->_user = $user;
	}

	/**
	 * @param bool $createIfNotExists whether to create globalization if it does not exist
	 * @return null|TGlobalization globalization module
	 */
	public function getGlobalization($createIfNotExists = true)
	{
		if ($this->_globalization === null && $createIfNotExists) {
			$this->_globalization = new TGlobalization();
			$this->_globalization->init(null);
		}
		return $this->_globalization;
	}

	/**
	 * @param \Prado\I18N\TGlobalization $glob globalization module
	 */
	public function setGlobalization(\Prado\I18N\TGlobalization $glob)
	{
		$this->_globalization = $glob;
	}

	/**
	 * @return \Prado\Security\TAuthorizationRuleCollection list of authorization rules for the current request
	 */
	public function getAuthorizationRules()
	{
		if ($this->_authRules === null) {
			$this->_authRules = new \Prado\Security\TAuthorizationRuleCollection();
		}
		return $this->_authRules;
	}

	protected function getApplicationConfigurationClass()
	{
		return '\Prado\TApplicationConfiguration';
	}

	protected function internalLoadModule($id, $force = false)
	{
		[$moduleClass, $initProperties, $configElement] = $this->_lazyModules[$id];
		if (isset($initProperties['lazy']) && $initProperties['lazy'] && !$force) {
			Prado::trace("Postponed loading of lazy module $id ({$moduleClass})", '\Prado\TApplication');
			$this->setModule($id, null);
			return null;
		}

		Prado::trace("Loading module $id ({$moduleClass})", '\Prado\TApplication');
		$module = Prado::createComponent($moduleClass);
		foreach ($initProperties as $name => $value) {
			if ($name === 'lazy') {
				continue;
			}
			$module->setSubProperty($name, $value);
		}
		$this->setModule($id, $module);
		// keep the key to avoid reuse of the old module id
		$this->_lazyModules[$id] = null;
		$module->dyPreInit($configElement);

		return [$module, $configElement];
	}
	/**
	 * Applies an application configuration.
	 * @param TApplicationConfiguration $config the configuration
	 * @param bool $withinService whether the configuration is specified within a service.
	 */
	public function applyConfiguration($config, $withinService = false)
	{
		if ($config->getIsEmpty()) {
			return;
		}

		// set path aliases and using namespaces
		foreach ($config->getAliases() as $alias => $path) {
			Prado::setPathOfAlias($alias, $path);
		}
		foreach ($config->getUsings() as $using) {
			Prado::using($using);
		}

		// set application properties
		if (!$withinService) {
			foreach ($config->getProperties() as $name => $value) {
				$this->setSubProperty($name, $value);
			}
		}

		if (empty($this->_services)) {
			$this->_services = [$this->getPageServiceID() => ['Prado\Web\Services\TPageService', [], null]];
		}

		// load parameters
		foreach ($config->getParameters() as $id => $parameter) {
			if (is_array($parameter)) {
				$component = Prado::createComponent($parameter[0]);
				foreach ($parameter[1] as $name => $value) {
					$component->setSubProperty($name, $value);
				}
				$component->dyInit(null);
				$this->_parameters->add($id, $component);
			} else {
				$this->_parameters->add($id, $parameter);
			}
		}

		// load and init modules specified in app config
		$modules = [];
		foreach ($config->getModules() as $id => $moduleConfig) {
			if (!is_string($id)) {
				$id = '_module' . count($this->_lazyModules);
			}
			$this->_lazyModules[$id] = $moduleConfig;
			if ($module = $this->internalLoadModule($id)) {
				$modules[] = $module;
			}
		}
		foreach ($modules as $module) {
			$module[0]->init($module[1]);
		}

		// load service
		foreach ($config->getServices() as $serviceID => $serviceConfig) {
			$this->_services[$serviceID] = $serviceConfig;
		}

		// external configurations
		foreach ($config->getExternalConfigurations() as $filePath => $condition) {
			if ($condition !== true) {
				$condition = $this->evaluateExpression($condition);
			}
			if ($condition) {
				if (($path = Prado::getPathOfNamespace($filePath, $this->getConfigurationFileExt())) === null || !is_file($path)) {
					throw new TConfigurationException('application_includefile_invalid', $filePath);
				}
				$cn = $this->getApplicationConfigurationClass();
				$c = new $cn();
				$c->loadFromFile($path);
				$this->applyConfiguration($c, $withinService);
			}
		}
	}

	/**
	 * Loads configuration and initializes application.
	 * Configuration file will be read and parsed (if a valid cached version exists,
	 * it will be used instead). Then, modules are created and initialized;
	 * Afterwards, the requested service is created and initialized.
	 * Lastly, the onInitComplete event is raised.
	 * @throws TConfigurationException if module is redefined of invalid type, or service not defined or of invalid type
	 */
	protected function initApplication()
	{
		Prado::trace('Initializing application', 'Prado\TApplication');

		if ($this->_configFile !== null) {
			if ($this->_cacheFile === null || @filemtime($this->_cacheFile) < filemtime($this->_configFile)) {
				$config = new TApplicationConfiguration();
				$config->loadFromFile($this->_configFile);
				if ($this->_cacheFile !== null) {
					file_put_contents($this->_cacheFile, serialize($config), LOCK_EX);
				}
			} else {
				$config = unserialize(file_get_contents($this->_cacheFile));
			}

			$this->applyConfiguration($config, false);
		}

		if (($serviceID = $this->getRequest()->resolveRequest(array_keys($this->_services))) === null) {
			$serviceID = $this->getPageServiceID();
		}

		$this->startService($serviceID);

		$this->onInitComplete();
	}

	/**
	 * Starts the specified service.
	 * The service instance will be created. Its properties will be initialized
	 * and the configurations will be applied, if any.
	 * @param string $serviceID service ID
	 */
	public function startService($serviceID)
	{
		if (isset($this->_services[$serviceID])) {
			[$serviceClass, $initProperties, $configElement] = $this->_services[$serviceID];
			$service = Prado::createComponent($serviceClass);
			if (!($service instanceof TService)) {
				throw new THttpException(500, 'application_service_invalid', $serviceClass);
			}
			if (!$service->getEnabled()) {
				throw new THttpException(500, 'application_service_unavailable', $serviceClass);
			}
			$service->setID($serviceID);
			$this->setService($service);

			foreach ($initProperties as $name => $value) {
				$service->setSubProperty($name, $value);
			}

			if ($configElement !== null) {
				$config = new TApplicationConfiguration();
				if ($this->getConfigurationType() == self::CONFIG_TYPE_PHP) {
					$config->loadFromPhp($configElement, $this->getBasePath());
				} else {
					$config->loadFromXml($configElement, $this->getBasePath());
				}
				$this->applyConfiguration($config, true);
			}

			$service->init($configElement);
		} else {
			throw new THttpException(500, 'application_service_unknown', $serviceID);
		}
	}

	/**
	 * Raises OnError event.
	 * This method is invoked when an exception is raised during the lifecycles
	 * of the application.
	 * @param mixed $param event parameter
	 */
	public function onError($param)
	{
		Prado::log($param->getMessage(), TLogger::ERROR, 'Prado\TApplication');
		$this->raiseEvent('OnError', $this, $param);
		$this->getErrorHandler()->handleError($this, $param);
	}

	/**
	 * Raises onInitComplete event.
	 * At the time when this method is invoked, application modules are loaded,
	 * user request is resolved and the corresponding service is loaded and
	 * initialized. The application is about to start processing the user
	 * request.  This call is important for CLI/Shell applications that do not have
	 * a web service lifecycle stack.  This is the first and last event for finalization
	 * of any loaded modules in CLI/Shell mode.
	 * @since 4.2.0
	 */
	public function onInitComplete()
	{
		$this->raiseEvent('onInitComplete', $this, null);
	}

	/**
	 * Raises OnBeginRequest event.
	 * At the time when this method is invoked, application modules are loaded
	 * and initialized, user request is resolved and the corresponding service
	 * is loaded and initialized. The application is about to start processing
	 * the user request.
	 */
	public function onBeginRequest()
	{
		$this->raiseEvent('OnBeginRequest', $this, null);
	}

	/**
	 * Raises OnAuthentication event.
	 * This method is invoked when the user request needs to be authenticated.
	 */
	public function onAuthentication()
	{
		$this->raiseEvent('OnAuthentication', $this, null);
	}

	/**
	 * Raises OnAuthenticationComplete event.
	 * This method is invoked right after the user request is authenticated.
	 */
	public function onAuthenticationComplete()
	{
		$this->raiseEvent('OnAuthenticationComplete', $this, null);
	}

	/**
	 * Raises OnAuthorization event.
	 * This method is invoked when the user request needs to be authorized.
	 */
	public function onAuthorization()
	{
		$this->raiseEvent('OnAuthorization', $this, null);
	}

	/**
	 * Raises OnAuthorizationComplete event.
	 * This method is invoked right after the user request is authorized.
	 */
	public function onAuthorizationComplete()
	{
		$this->raiseEvent('OnAuthorizationComplete', $this, null);
	}

	/**
	 * Raises OnLoadState event.
	 * This method is invoked when the application needs to load state (probably stored in session).
	 */
	public function onLoadState()
	{
		$this->loadGlobals();
		$this->raiseEvent('OnLoadState', $this, null);
	}

	/**
	 * Raises OnLoadStateComplete event.
	 * This method is invoked right after the application state has been loaded.
	 */
	public function onLoadStateComplete()
	{
		$this->raiseEvent('OnLoadStateComplete', $this, null);
	}

	/**
	 * Raises OnPreRunService event.
	 * This method is invoked right before the service is to be run.
	 */
	public function onPreRunService()
	{
		$this->raiseEvent('OnPreRunService', $this, null);
	}

	/**
	 * Runs the requested service.
	 */
	public function runService()
	{
		if ($this->_service) {
			$this->_service->run();
		}
	}

	/**
	 * Raises OnSaveState event.
	 * This method is invoked when the application needs to save state (probably stored in session).
	 */
	public function onSaveState()
	{
		$this->raiseEvent('OnSaveState', $this, null);
		$this->saveGlobals();
	}

	/**
	 * Raises OnSaveStateComplete event.
	 * This method is invoked right after the application state has been saved.
	 */
	public function onSaveStateComplete()
	{
		$this->raiseEvent('OnSaveStateComplete', $this, null);
	}

	/**
	 * Raises OnPreFlushOutput event.
	 * This method is invoked right before the application flushes output to client.
	 */
	public function onPreFlushOutput()
	{
		$this->raiseEvent('OnPreFlushOutput', $this, null);
	}

	/**
	 * Flushes output to client side.
	 * @param bool $continueBuffering whether to continue buffering after flush if buffering was active
	 */
	public function flushOutput($continueBuffering = true)
	{
		$this->getResponse()->flush($continueBuffering);
	}

	/**
	 * Raises OnEndRequest event.
	 * This method is invoked when the application completes the processing of the request.
	 */
	public function onEndRequest()
	{
		$this->flushOutput(false); // flush all remaining content in the buffer
		$this->raiseEvent('OnEndRequest', $this, null);
		$this->saveGlobals();  // save global state
	}
}

AnonSec - 2021