From 55c4ac1bfe565f1ca7f537fdd8b7a201be28e581 Mon Sep 17 00:00:00 2001 From: xue <> Date: Thu, 10 Nov 2005 12:47:19 +0000 Subject: Initial import of prado framework --- framework/TApplication.php | 766 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 766 insertions(+) create mode 100644 framework/TApplication.php (limited to 'framework/TApplication.php') diff --git a/framework/TApplication.php b/framework/TApplication.php new file mode 100644 index 00000000..973032e5 --- /dev/null +++ b/framework/TApplication.php @@ -0,0 +1,766 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System + */ + +/** + * Includes TPageService class (default service) + */ +require_once(PRADO_DIR.'/Web/Services/TPageService.php'); + +/** + * TApplication class. + * + * TApplication coordinates modules and services, and serves as a configuration + * context for all Prado components. + * + * 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 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. + * + * 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 uses a configuration file to specify the settings of + * the application, the modules, the services, the parameters, and so on. + * + * Examples: + * + * $application=new TApplication($configFile); + * $application->run(); + * + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +class TApplication extends TComponent implements IApplication +{ + /** + * Default service ID + */ + const DEFAULT_SERVICE='page'; + /** + * @var array list of events that define application lifecycles + */ + private static $_steps=array( + 'BeginRequest', + 'Authentication', + 'PostAuthentication', + 'Authorization', + 'PostAuthorization', + 'LoadState', + 'PostLoadState', + 'PreRunService', + 'RunService', + 'PostRunService', + 'SaveState', + 'PostSaveState', + 'EndRequest' + ); + /** + * @var array list of types that the named modules must be of + */ + private static $_moduleTypes=array( + 'request'=>'THttpRequest', + 'response'=>'THttpResponse', + 'session'=>'THttpSession', + 'cache'=>'ICache', + 'error'=>'IErrorHandler' + ); + + /** + * @var string application ID + */ + private $_id; + /** + * @var string unique application ID + */ + private $_uniqueID; + /** + * @var boolean whether the request is completed + */ + private $_requestCompleted=false; + /** + * @var integer application state + */ + private $_step; + /** + * @var IService current service instance + */ + private $_service; + /** + * @var array list of application modules + */ + private $_modules; + /** + * @var TMap list of application parameters + */ + private $_parameters; + /** + * @var string configuration file + */ + private $_configFile; + /** + * @var string cache file + */ + private $_cacheFile; + /** + * @var string user type + */ + private $_userType='System.Security.TUser'; + /** + * @var IUser user instance + */ + private $_user=null; + + /** + * Constructor. + * Loads application configuration and initializes application. + * If a cache is specified and present, it will be used instead of the configuration file. + * If the cache file is specified but is not present, the configuration file + * will be parsed and the result is saved in the cache file. + * @param string configuration file path (absolute or relative to current running script) + * @param string cache file path + */ + public function __construct($configFile,$cacheFile='') + { + parent::__construct(); + Prado::setApplication($this); + $this->_configFile=$configFile; + $this->_cacheFile=$cacheFile; + } + + /** + * Executes the lifecycles of the application. + */ + public function run() + { + try + { + $this->initApplication($this->_configFile,$this->_cacheFile); + $n=count(self::$_steps); + $this->_step=0; + $this->_requestCompleted=false; + while($this->_step<$n) + { + $method='on'.self::$_steps[$this->_step]; + $this->$method(null); + if($this->_requestCompleted && $this->_step<$n-1) + $this->_step=$n-1; + else + $this->_step++; + } + } + catch(Exception $e) + { + $this->onError($e); + } + } + + /** + * 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 string application ID + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string application ID + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * @return string an ID that unique identifies this Prado application from the others + */ + public function getUniqueID() + { + return $this->_uniqueID; + } + + /** + * @return IService the currently requested service + */ + public function getService() + { + return $this->_service; + } + + /** + * Adds a module to application. + * Note, this method does not do module initialization. + * Also, if there is already a module with the same ID, an exception will be raised. + * @param string ID of the module + * @param IModule module object + * @throws TInvalidOperationException if a module with the same ID already exists + */ + public function setModule($id,IModule $module) + { + if(isset($this->_modules[$id])) + throw new TInvalidOperationException('application_module_existing',$id); + else + $this->_modules[$id]=$module; + } + + /** + * @return IModule the module with the specified ID, null if not found + */ + public function getModule($id) + { + return isset($this->_modules[$id])?$this->_modules[$id]:null; + } + + /** + * @return array list of loaded application modules, indexed by module IDs + */ + public function getModules() + { + return $this->_modules; + } + + /** + * @return TMap the list of application parameters + */ + public function getParameters() + { + return $this->_parameters; + } + + /** + * @return THttpRequest the request object + */ + public function getRequest() + { + return isset($this->_modules['request'])?$this->_modules['request']:null; + } + + /** + * @return THttpResponse the response object + */ + public function getResponse() + { + return isset($this->_modules['response'])?$this->_modules['response']:null; + } + + /** + * @return THttpSession the session object + */ + public function getSession() + { + return isset($this->_modules['session'])?$this->_modules['session']:null; + } + + /** + * @return ICache the cache object, null if not exists + */ + public function getCache() + { + return isset($this->_modules['cache'])?$this->_modules['cache']:null; + } + + /** + * @return IErrorHandler the error hanlder module + */ + public function getErrorHandler() + { + return isset($this->_modules['error'])?$this->_modules['error']:null; + } + + /** + * @return IRoleProvider provider for user auth management + */ + public function getAuthManager() + { + return isset($this->_modules['auth'])?$this->_modules['auth']:null; + } + + /** + * @return IUser the application user + */ + public function getUser() + { + return $this->_user; + } + + /** + * @param IUser the application user + */ + public function setUser(IUser $user) + { + $this->_user=$user; + } + + /** + * Loads configuration and initializes application. + * Configuration file will be read and parsed (if a valid cache version exists, + * it will be used instead). Then, modules are created and initialized; + * The requested service is created and initialized. + * @param string configuration file path (absolute or relative to current executing script) + * @param string cache file path, empty if no present or needed + * @throws TConfigurationException if config file is not given, or module is redefined of invalid type, or service not defined or of invalid type + */ + protected function initApplication($configFile,$cacheFile) + { + if($cacheFile==='' || @filemtime($cacheFile)loadFromFile($configFile); + if($cacheFile!=='') + { + if(($fp=fopen($cacheFile,'wb'))!==false) + { + fputs($fp,Prado::serialize($config)); + fclose($fp); + } + else + syslog(LOG_WARNING,'Prado application config cache file '.$cacheFile.' cannot be created.'); + } + } + else + { + $config=Prado::unserialize(file_get_contents($cacheFile)); + } + + // generates unique ID by hashing the configuration file path + $this->_uniqueID=md5(realpath($configFile)); + + // 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 + foreach($config->getProperties() as $name=>$value) + $this->setPropertyByPath($name,$value); + + // load parameters + $this->_parameters=new TMap; + foreach($config->getParameters() as $id=>$parameter) + { + if(is_string($parameter)) + $this->_parameters->add($id,$parameter); + else + { + $component=Prado::createComponent($parameter[0]); + foreach($parameter[1] as $name=>$value) + $component->setPropertyByPath($name,$value); + $this->_parameters->add($id,$component); + } + } + + // load and init modules specified in app config + $this->_modules=array(); + foreach($config->getModules() as $id=>$moduleConfig) + { + if(isset($this->_modules[$id])) + throw new TConfigurationException('application_module_redefined',$id); + $module=Prado::createComponent($moduleConfig[0]); + if(isset(self::$_moduleTypes[$id]) && !($module instanceof self::$_moduleTypes[$id])) + throw new TConfigurationException('application_module_invalid',$id,self::$_moduleTypes[$id]); + $this->_modules[$id]=$module; + foreach($moduleConfig[1] as $name=>$value) + $module->setPropertyByPath($name,$value); + $module->init($this,$moduleConfig[2]); + } + + if(($serviceID=$this->getRequest()->getServiceID())===null) + $serviceID=self::DEFAULT_SERVICE; + if(($serviceConfig=$config->getService($serviceID))!==null) + { + $service=Prado::createComponent($serviceConfig[0]); + if(!($service instanceof IService)) + throw new TConfigurationException('application_service_invalid',$serviceID); + $this->_service=$service; + foreach($serviceConfig[1] as $name=>$value) + $service->setPropertyByPath($name,$value); + $service->init($this,$serviceConfig[2]); + $this->attachEventHandler('RunService',array($service,'run')); + } + else + throw new TConfigurationException('application_service_unknown',$serviceID); + } + + /** + * Raises Error event. + * This method is invoked when an exception is raised during the lifecycles + * of the application. + * @param mixed event parameter + */ + public function onError($param) + { + if($this->hasEventHandler('Error')) + $this->raiseEvent('Error',$this,$param); + else + echo $param; + } + + /** + * Raises BeginRequest 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. + * @param mixed event parameter + */ + public function onBeginRequest($param) + { + $this->raiseEvent('BeginRequest',$this,$param); + } + + /** + * Raises Authentication event. + * This method is invoked when the user request needs to be authenticated. + * @param mixed event parameter + */ + public function onAuthentication($param) + { + $this->raiseEvent('Authentication',$this,$param); + } + + /** + * Raises PostAuthentication event. + * This method is invoked right after the user request is authenticated. + * @param mixed event parameter + */ + public function onPostAuthentication($param) + { + $this->raiseEvent('PostAuthentication',$this,$param); + } + + /** + * Raises Authorization event. + * This method is invoked when the user request needs to be authorized. + * @param mixed event parameter + */ + public function onAuthorization($param) + { + $this->raiseEvent('Authorization',$this,$param); + } + + /** + * Raises PostAuthorization event. + * This method is invoked right after the user request is authorized. + * @param mixed event parameter + */ + public function onPostAuthorization($param) + { + $this->raiseEvent('PostAuthorization',$this,$param); + } + + /** + * Raises LoadState event. + * This method is invoked when the application needs to load state (probably stored in session). + * @param mixed event parameter + */ + public function onLoadState($param) + { + $this->raiseEvent('LoadState',$this,$param); + } + + /** + * Raises PostLoadState event. + * This method is invoked right after the application state has been loaded. + * @param mixed event parameter + */ + public function onPostLoadState($param) + { + $this->raiseEvent('PostLoadState',$this,$param); + } + + /** + * Raises PreRunService event. + * This method is invoked right before the service is to be run. + * @param mixed event parameter + */ + public function onPreRunService($param) + { + $this->raiseEvent('PreRunService',$this,$param); + } + + /** + * Raises RunService event. + * This method is invoked when the service runs. + * @param mixed event parameter + */ + public function onRunService($param) + { + $this->raiseEvent('RunService',$this,$param); + } + + /** + * Raises PostRunService event. + * This method is invoked right after the servie is run. + * @param mixed event parameter + */ + public function onPostRunService($param) + { + $this->raiseEvent('PostRunService',$this,$param); + } + + /** + * Raises SaveState event. + * This method is invoked when the application needs to save state (probably stored in session). + * @param mixed event parameter + */ + public function onSaveState($param) + { + $this->raiseEvent('SaveState',$this,$param); + } + + /** + * Raises PostSaveState event. + * This method is invoked right after the application state has been saved. + * @param mixed event parameter + */ + public function onPostSaveState($param) + { + $this->raiseEvent('PostSaveState',$this,$param); + } + + /** + * Raises EndRequest event. + * This method is invoked when the application completes the processing of the request. + * @param mixed event parameter + */ + public function onEndRequest($param) + { + $this->raiseEvent('EndRequest',$this,$param); + } +} + + +/** + * TApplicationConfiguration class. + * + * This class is used internally by TApplication to parse and represent application configuration. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +class TApplicationConfiguration extends TComponent +{ + /** + * @var array list of application initial property values, indexed by property names + */ + private $_properties=array(); + /** + * @var array list of namespaces to be used + */ + private $_usings=array(); + /** + * @var array list of path aliases, indexed by alias names + */ + private $_aliases=array(); + /** + * @var array list of module configurations + */ + private $_modules=array( + 'request'=>array('THttpRequest',array(),null), + 'response'=>array('THttpResponse',array(),null), + 'error'=>array('TErrorHandler',array(),null) + ); + /** + * @var array list of service configurations + */ + private $_services=array( + 'page'=>array('TPageService',array(),null) + ); + /** + * @var array list of parameters + */ + private $_parameters=array(); + + /** + * Parses the application configuration file. + * @param string configuration file name + * @throws TConfigurationException if config file cannot be read or any parsing error is found + */ + public function loadFromFile($fname) + { + if(!is_file($fname)) + throw new TConfigurationException('application_configuration_inexistent',$fname); + $configPath=dirname($fname); + $dom=new TXmlDocument; + $dom->loadFromFile($fname); + + // application properties + foreach($dom->getAttributes() as $name=>$value) + $this->_properties[$name]=$value; + + // paths + if(($pathsNode=$dom->getElementByTagName('paths'))!==null) + { + foreach($pathsNode->getElementsByTagName('alias') as $aliasNode) + { + if(($id=$aliasNode->getAttribute('id'))!==null && ($path=$aliasNode->getAttribute('path'))!==null) + { + $path=str_replace('\\','/',$path); + if(preg_match('/^\\/|.:\\//',$path)) // if absolute path + $p=realpath($path); + else + $p=realpath($configPath.'/'.$path); + if($p===false || !is_dir($p)) + throw new TConfigurationException('application_alias_path_invalid',$id,$path); + $this->_aliases[$id]=$p; + } + else + throw new TConfigurationException('application_alias_element_invalid'); + } + foreach($pathsNode->getElementsByTagName('using') as $usingNode) + { + if(($namespace=$usingNode->getAttribute('namespace'))!==null) + $this->_usings[]=$namespace; + else + throw new TConfigurationException('application_using_element_invalid'); + } + } + + // application modules + if(($modulesNode=$dom->getElementByTagName('modules'))!==null) + { + foreach($modulesNode->getElementsByTagName('module') as $node) + { + $properties=$node->getAttributes(); + if(($id=$properties->itemAt('id'))===null) + throw new TConfigurationException('application_module_element_invalid'); + if(($type=$properties->remove('type'))===null && isset($this->_modules[$id]) && $this->_modules[$id][2]===null) + { + $type=$this->_modules[$id][0]; + unset($this->_modules[$id]); + } + if($type===null) + throw new TConfigurationException('application_module_element_invalid'); + if(isset($this->_modules[$id])) + throw new TConfigurationException('application_module_redefined',$id); + else + { + $node->setParent(null); + $this->_modules[$id]=array($type,$properties->toArray(),$node); + } + } + } + + // services + if(($servicesNode=$dom->getElementByTagName('services'))!==null) + { + foreach($servicesNode->getElementsByTagName('service') as $node) + { + $properties=$node->getAttributes(); + if(($id=$properties->itemAt('id'))===null) + throw new TConfigurationException('application_service_element_invalid'); + if(($type=$properties->remove('type'))===null && isset($this->_services[$id]) && $this->_services[$id][2]===null) + { + $type=$this->_services[$id][0]; + unset($this->_services[$id]); + } + if($type===null) + throw new TConfigurationException('application_service_element_invalid'); + if(isset($this->_services[$id])) + throw new TConfigurationException('application_service_redefined',$id); + else + { + $node->setParent(null); + $this->_services[$id]=array($type,$properties->toArray(),$node); + } + } + } + + // parameters + if(($parametersNode=$dom->getElementByTagName('parameters'))!==null) + { + foreach($parametersNode->getElementsByTagName('parameter') as $node) + { + $properties=$node->getAttributes(); + if(($id=$properties->remove('id'))===null) + throw new TConfigurationException('application_parameter_element_invalid'); + if(($type=$properties->remove('type'))===null) + $this->_parameters[$id]=$node->getValue(); + else + $this->_parameters[$id]=array($type,$properties->toArray()); + } + } + } + + /** + * @return array list of application initial property values, indexed by property names + */ + public function getProperties() + { + return $this->_properties; + } + + /** + * @return array list of path aliases, indexed by alias names + */ + public function getAliases() + { + return $this->_aliases; + } + + /** + * @return array list of namespaces to be used + */ + public function getUsings() + { + return $this->_usings; + } + + /** + * @return array list of module configurations + */ + public function getModules() + { + return $this->_modules; + } + + /** + * @return array list of service configurations + */ + public function getService($id) + { + return isset($this->_services[$id])?$this->_services[$id]:null; + } + + /** + * @return array list of parameters + */ + public function getParameters() + { + return $this->_parameters; + } +} + +?> \ No newline at end of file -- cgit v1.2.3