From 6845c00a3f752abecd9ef763848329bceaf63df4 Mon Sep 17 00:00:00 2001 From: xue <> Date: Fri, 31 Mar 2006 12:42:22 +0000 Subject: Reorganized folders under framework. This may break existing applications. --- .gitattributes | 27 +- buildscripts/phpbuilder/files.txt | 3 +- demos/personal/protected/application.xml | 2 +- demos/quickstart/protected/application.xml | 4 +- framework/Caching/TAPCCache.php | 152 +++ framework/Caching/TCache.php | 88 ++ framework/Caching/TMemCache.php | 256 +++++ framework/Caching/TSqliteCache.php | 265 +++++ framework/Data/TAPCCache.php | 152 --- framework/Data/TCache.php | 88 -- framework/Data/TDataFieldAccessor.php | 117 --- framework/Data/TMemCache.php | 256 ----- framework/Data/TSimpleDateFormatter.php | 354 ------- framework/Data/TSqliteCache.php | 265 ----- framework/Data/TTarFileExtractor.php | 574 ---------- framework/Data/TXmlDocument.php | 455 -------- framework/IO/TTarFileExtractor.php | 572 ++++++++++ framework/IO/TTextWriter.php | 60 ++ framework/Log/TLogRouter.php | 666 ------------ framework/Log/TLogger.php | 130 --- framework/PradoBase.php | 604 +++++++++++ framework/TApplication.php | 40 +- framework/TModule.php | 57 + framework/TService.php | 64 ++ framework/Util/TDataFieldAccessor.php | 117 +++ framework/Util/TLogRouter.php | 666 ++++++++++++ framework/Util/TLogger.php | 130 +++ framework/Util/TSimpleDateFormatter.php | 354 +++++++ framework/Util/TVarDumper.php | 123 +++ framework/Web/TAssetManager.php | 2 +- framework/Web/UI/TControl.php | 41 + framework/Web/UI/WebControls/TBaseDataList.php | 3 +- framework/Web/UI/WebControls/TCompareValidator.php | 2 +- framework/Web/UI/WebControls/TDataGridColumn.php | 5 + .../Web/UI/WebControls/TDataTypeValidator.php | 2 +- framework/Web/UI/WebControls/TDatePicker.php | 12 +- framework/Web/UI/WebControls/TListControl.php | 4 + framework/Web/UI/WebControls/TRangeValidator.php | 2 +- framework/Web/UI/WebControls/TWizard.php | 1 + framework/Xml/TXmlDocument.php | 455 ++++++++ framework/core.php | 1094 -------------------- framework/interfaces.php | 172 +++ framework/prado.php | 10 +- 43 files changed, 4241 insertions(+), 4205 deletions(-) create mode 100644 framework/Caching/TAPCCache.php create mode 100644 framework/Caching/TCache.php create mode 100644 framework/Caching/TMemCache.php create mode 100644 framework/Caching/TSqliteCache.php delete mode 100644 framework/Data/TAPCCache.php delete mode 100644 framework/Data/TCache.php delete mode 100644 framework/Data/TDataFieldAccessor.php delete mode 100644 framework/Data/TMemCache.php delete mode 100644 framework/Data/TSimpleDateFormatter.php delete mode 100644 framework/Data/TSqliteCache.php delete mode 100644 framework/Data/TTarFileExtractor.php delete mode 100644 framework/Data/TXmlDocument.php create mode 100644 framework/IO/TTarFileExtractor.php create mode 100644 framework/IO/TTextWriter.php delete mode 100644 framework/Log/TLogRouter.php delete mode 100644 framework/Log/TLogger.php create mode 100644 framework/PradoBase.php create mode 100644 framework/TModule.php create mode 100644 framework/TService.php create mode 100644 framework/Util/TDataFieldAccessor.php create mode 100644 framework/Util/TLogRouter.php create mode 100644 framework/Util/TLogger.php create mode 100644 framework/Util/TSimpleDateFormatter.php create mode 100644 framework/Util/TVarDumper.php create mode 100644 framework/Xml/TXmlDocument.php delete mode 100644 framework/core.php create mode 100644 framework/interfaces.php diff --git a/.gitattributes b/.gitattributes index 65ec8a99..c36622ca 100644 --- a/.gitattributes +++ b/.gitattributes @@ -323,6 +323,10 @@ framework/3rdParty/geshi/geshi/prado.php -text framework/3rdParty/geshi/geshi/xml.php -text framework/3rdParty/geshi/highlight.css -text framework/3rdParty/readme.html -text +framework/Caching/TAPCCache.php -text +framework/Caching/TCache.php -text +framework/Caching/TMemCache.php -text +framework/Caching/TSqliteCache.php -text framework/Collections/TAttributeCollection.php -text framework/Collections/TDummyDataSource.php -text framework/Collections/TList.php -text @@ -330,14 +334,6 @@ framework/Collections/TMap.php -text framework/Collections/TPagedDataSource.php -text framework/Collections/TPagedList.php -text framework/Collections/TStack.php -text -framework/Data/TAPCCache.php -text -framework/Data/TCache.php -text -framework/Data/TDataFieldAccessor.php -text -framework/Data/TMemCache.php -text -framework/Data/TSimpleDateFormatter.php -text -framework/Data/TSqliteCache.php -text -framework/Data/TTarFileExtractor.php -text -framework/Data/TXmlDocument.php -text framework/DataAccess/TAdodbProvider.php -text framework/DataAccess/TCreoleProvider.php -text framework/DataAccess/TDatabaseProvider.php -text @@ -646,8 +642,9 @@ framework/I18N/core/data/zh_TW.dat -text framework/I18N/core/util.php -text framework/I18N/schema/mysql.sql -text framework/I18N/schema/sqlite.sql -text -framework/Log/TLogRouter.php -text -framework/Log/TLogger.php -text +framework/IO/TTarFileExtractor.php -text +framework/IO/TTextWriter.php -text +framework/PradoBase.php -text framework/Security/TAuthManager.php -text framework/Security/TAuthorizationRule.php -text framework/Security/TMembershipManager.php -text @@ -656,6 +653,13 @@ framework/Security/TUserManager.php -text framework/TApplication.php -text framework/TApplicationComponent.php -text framework/TComponent.php -text +framework/TModule.php -text +framework/TService.php -text +framework/Util/TDataFieldAccessor.php -text +framework/Util/TLogRouter.php -text +framework/Util/TLogger.php -text +framework/Util/TSimpleDateFormatter.php -text +framework/Util/TVarDumper.php -text framework/Web/Javascripts/TJSON.php -text framework/Web/Javascripts/TJavaScript.php -text framework/Web/Javascripts/colorpicker/background.png -text @@ -821,7 +825,8 @@ framework/Web/UI/WebControls/TValidationSummary.php -text framework/Web/UI/WebControls/TWebControl.php -text framework/Web/UI/WebControls/TWebControlAdapter.php -text framework/Web/UI/WebControls/TWizard.php -text -framework/core.php -text +framework/Xml/TXmlDocument.php -text +framework/interfaces.php -text framework/prado.php -text /index.html -text requirements/index.php -text diff --git a/buildscripts/phpbuilder/files.txt b/buildscripts/phpbuilder/files.txt index 0ec9127f..2c5e03f6 100644 --- a/buildscripts/phpbuilder/files.txt +++ b/buildscripts/phpbuilder/files.txt @@ -9,11 +9,10 @@ Collections/TAttributeCollection.php # Collections/TPagedDataSource.php # Collections/TDummyDataSource.php -Data/TXmlDocument.php +Xml/TXmlDocument.php Web/THttpUtility.php Web/Javascripts/TJavaScript.php Data/TCache.php -Data/TDataFieldAccessor.php Log/TLogger.php core.php diff --git a/demos/personal/protected/application.xml b/demos/personal/protected/application.xml index 48fa0490..2565d1a0 100644 --- a/demos/personal/protected/application.xml +++ b/demos/personal/protected/application.xml @@ -12,7 +12,7 @@ - + diff --git a/demos/quickstart/protected/application.xml b/demos/quickstart/protected/application.xml index cf00807c..0eba3be7 100644 --- a/demos/quickstart/protected/application.xml +++ b/demos/quickstart/protected/application.xml @@ -3,10 +3,10 @@ diff --git a/framework/Caching/TAPCCache.php b/framework/Caching/TAPCCache.php new file mode 100644 index 00000000..da493f63 --- /dev/null +++ b/framework/Caching/TAPCCache.php @@ -0,0 +1,152 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Caching + */ + +/** + * TAPCCache class + * + * TAPCCache implements a cache application module based on {@link http://www.php.net/apc APC}. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * To use this module, the APC PHP extension must be loaded and set in the php.ini file the following: + * + * apc.cache_by_default=0 + * + * + * Some usage examples of TAPCCache are as follows, + * + * $cache=new TAPCCache; // TAPCCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TAPCCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TAPCCache may be configured in application configuration file as follows + * + * + * @author Alban Hanry + * @version $Revision: $ $Date: $ + * @package System.Caching + * @since 3.0b + */ +class TAPCCache extends TModule implements ICache +{ + /** + * @var boolean if the module is initialized + */ + private $_initialized=false; + + /** + * @var string a unique prefix used to identify this cache instance from the others + */ + protected $_prefix=null; + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if apc extension is not installed or not started, check your php.ini + */ + public function init($config) + { + if(!extension_loaded('apc')) + throw new TConfigurationException('apccache_extension_required'); + $application=$this->getApplication(); + $this->_prefix=$application->getUniqueID(); + $application->setCache($this); + $this->_initialized=true; + } + + /** + * Retrieves a value from cache with a specified key. + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function get($key) + { + if(($value=apc_fetch($this->_prefix.$key))!==false) + return Prado::unserialize($value); + else + return $value; + } + + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($key,$value,$expiry=0) + { + return apc_store($this->_prefix.$key,Prado::serialize($value),$expiry); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * APC does not support this mode. So calling this method will raise an exception. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * @return boolean true if the value is successfully stored into cache, false otherwise + * @throw new TNotSupportedException if this method is invoked + */ + public function add($key,$value,$expiry=0) + { + throw new TNotSupportedException('apccache_add_unsupported'); + } + + /** + * Stores a value identified by a key into cache only if the cache contains this key. + * The existing value and expiration time will be overwritten with the new ones. + * APC does not support this mode. So calling this method will raise an exception. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * @return boolean true if the value is successfully stored into cache, false otherwise + * @throw new TNotSupportedException if this method is invoked + */ + public function replace($key,$value,$expiry=0) + { + throw new TNotSupportedException('apccache_replace_unsupported'); + } + + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($key) + { + return apc_delete($this->_prefix.$key); + } + + /** + * Deletes all values from cache. + */ + public function flush() + { + return apc_clear_cache('user'); + } + +} + +?> \ No newline at end of file diff --git a/framework/Caching/TCache.php b/framework/Caching/TCache.php new file mode 100644 index 00000000..d246b9d0 --- /dev/null +++ b/framework/Caching/TCache.php @@ -0,0 +1,88 @@ + + * @version $Revision: $ $Date: $ + * @package System.Caching + * @since 3.0 + */ +interface ICache +{ + /** + * Retrieves a value from cache with a specified key. + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function get($id); + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0); + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0); + /** + * Stores a value identified by a key into cache only if the cache contains this key. + * The existing value and expiration time will be overwritten with the new ones. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function replace($id,$value,$expire=0); + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($id); + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush(); +} + +interface IDependency +{ + +} + +class TTimeDependency +{ +} + +class TFileDependency +{ +} + +class TDirectoryDependency +{ +} + +?> \ No newline at end of file diff --git a/framework/Caching/TMemCache.php b/framework/Caching/TMemCache.php new file mode 100644 index 00000000..bcd49036 --- /dev/null +++ b/framework/Caching/TMemCache.php @@ -0,0 +1,256 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Caching + */ + +/** + * TMemCache class + * + * TMemCache implements a cache application module based on {@link http://www.danga.com/memcached/ memcached}. + * + * TMemCache can be configured with the Host and Port properties, which + * specify the host and port of the memcache server to be used. + * By default, they take the value 'localhost' and 11211, respectively. + * These properties must be set before {@link init} is invoked. + * + * The following basic cache operations are implemented: + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link replace} : store the value only if cache has this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time can + * be specified by the number of seconds (maximum 60*60*24*30) + * or a UNIX timestamp. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Also note, there is no security measure to protected data in memcache. + * All data in memcache can be accessed by any process running in the system. + * + * To use this module, the memcache PHP extension must be loaded. + * + * Some usage examples of TMemCache are as follows, + * + * $cache=new TMemCache; // TMemCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TMemCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TMemCache may be configured in application configuration file as follows + * + * where {@link getHost Host} and {@link getPort Port} are configurable properties + * of TMemCache. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Caching + * @since 3.0 + */ +class TMemCache extends TModule implements ICache +{ + /** + * @var boolean if the module is initialized + */ + private $_initialized=false; + /** + * @var Memcache the Memcache instance + */ + private $_cache=null; + /** + * @var string a unique prefix used to identify this cache instance from the others + */ + private $_prefix=null; + /** + * @var string host name of the memcache server + */ + private $_host='localhost'; + /** + * @var integer the port number of the memcache server + */ + private $_port=11211; + + /** + * Destructor. + * Disconnect the memcache server. + */ + public function __destruct() + { + if($this->_cache!==null) + $this->_cache->close(); + } + + /** + * Initializes this module. + * This method is required by the IModule interface. It makes sure that + * UniquePrefix has been set, creates a Memcache instance and connects + * to the memcache server. + * @param TApplication Prado application, can be null + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if memcache extension is not installed or memcache sever connection fails + */ + public function init($config) + { + if(!extension_loaded('memcache')) + throw new TConfigurationException('memcache_extension_required'); + $this->_cache=new Memcache; + if($this->_cache->connect($this->_host,$this->_port)===false) + throw new TConfigurationException('memcache_connection_failed',$this->_host,$this->_port); + $application=$this->getApplication(); + $this->_prefix=$application->getUniqueID(); + $application->setCache($this); + $this->_initialized=true; + } + + /** + * @return string host name of the memcache server + */ + public function getHost() + { + return $this->_host; + } + + /** + * @param string host name of the memcache server + * @throws TInvalidOperationException if the module is already initialized + */ + public function setHost($value) + { + if($this->_initialized) + throw new TInvalidOperationException('memcache_host_unchangeable'); + else + $this->_host=$value; + } + + /** + * @return integer port number of the memcache server + */ + public function getPort() + { + return $this->_port; + } + + /** + * @param integer port number of the memcache server + * @throws TInvalidOperationException if the module is already initialized + */ + public function setPort($value) + { + if($this->_initialized) + throw new TInvalidOperationException('memcache_port_unchangeable'); + else + $this->_port=TPropertyValue::ensureInteger($value); + } + + /** + * Retrieves a value from cache with a specified key. + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function get($key) + { + return $this->_cache->get($this->generateUniqueKey($key)); + } + + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. + * + * Note, avoid using this method whenever possible. Database insertion is + * very expensive. Try using {@link add} instead, which will not store the value + * if the key is already in cache. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($key,$value,$expire=0) + { + return $this->_cache->set($this->generateUniqueKey($key),$value,0,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($key,$value,$expiry=0) + { + return $this->_cache->add($this->generateUniqueKey($key),$value,0,$expiry); + } + + /** + * Stores a value identified by a key into cache only if the cache contains this key. + * The existing value and expiration time will be overwritten with the new ones. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function replace($key,$value,$expiry=0) + { + return $this->_cache->replace($this->generateUniqueKey($key),$value,0,$expiry); + } + + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($key) + { + return $this->_cache->delete($this->generateUniqueKey($key)); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return $this->_cache->flush(); + } + + /** + * Generates a unique key based on a given user key. + * This method generates a unique key with the memcache. + * The key is made unique by prefixing with a unique string that is supposed + * to be unique among applications using the same memcache. + * @param string user key + * @param string a unique key + */ + protected function generateUniqueKey($key) + { + return md5($this->_prefix.$key); + } +} + +?> \ No newline at end of file diff --git a/framework/Caching/TSqliteCache.php b/framework/Caching/TSqliteCache.php new file mode 100644 index 00000000..13aac8ca --- /dev/null +++ b/framework/Caching/TSqliteCache.php @@ -0,0 +1,265 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Caching + */ + +/** + * TSqliteCache class + * + * TSqliteCache implements a cache application module based on SQLite database. + * + * The database file is specified by the {@link setDbFile DbFile} property. + * If not set, the database file will be created under the system state path. + * If the specified database file does not exist, it will be created automatically. + * Make sure the directory containing the specified DB file and the file itself is + * writable by the Web server process. + * + * The following basic cache operations are implemented: + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link replace} : store the value only if cache has this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time can + * be specified by the number of seconds (maximum 60*60*24*30) + * or a UNIX timestamp. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Do not use the same database file for multiple applications using TSqliteCache. + * Also note, cache is shared by all user sessions of an application. + * + * To use this module, the sqlite PHP extension must be loaded. Sqlite extension + * is no longer loaded by default since PHP 5.1. + * + * Some usage examples of TSqliteCache are as follows, + * + * $cache=new TSqliteCache; // TSqliteCache may also be loaded as a Prado application module + * $cache->setDbFile($dbFilePath); + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TSqliteCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TMemCache may be configured in application configuration file as follows + * + * where {@link getDbFile DbFile} is a property specifying the location of the + * SQLite DB file (in the namespace format). + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Caching + * @since 3.0 + */ +class TSqliteCache extends TModule implements ICache +{ + /** + * name of the table storing cache data + */ + const CACHE_TABLE='cache'; + /** + * extension of the db file name + */ + const DB_FILE_EXT='.db'; + /** + * maximum number of seconds specified as expire + */ + const EXPIRE_LIMIT=2592000; // 30 days + + /** + * @var boolean if the module has been initialized + */ + private $_initialized=false; + /** + * @var SQLiteDatabase the sqlite database instance + */ + private $_db=null; + /** + * @var string the database file name + */ + private $_file=null; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + $this->_db=null; + } + + /** + * Initializes this module. + * This method is required by the IModule interface. It checks if the DbFile + * property is set, and creates a SQLiteDatabase instance for it. + * The database or the cache table does not exist, they will be created. + * Expired values are also deleted. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if sqlite extension is not installed, + * DbFile is set invalid, or any error happens during creating database or cache table. + */ + public function init($config) + { + if(!function_exists('sqlite_open')) + throw new TConfigurationException('sqlitecache_extension_required'); + if($this->_file===null) + $this->_file=$this->getApplication()->getRuntimePath().'/sqlite.cache'; + $error=''; + if(($this->_db=new SQLiteDatabase($this->_file,0666,$error))===false) + throw new TConfigurationException('sqlitecache_connection_failed',$error); + if(($res=$this->_db->query('SELECT * FROM sqlite_master WHERE tbl_name=\''.self::CACHE_TABLE.'\' AND type=\'table\''))!=false) + { + if($res->numRows()===0) + { + if($this->_db->query('CREATE TABLE '.self::CACHE_TABLE.' (key CHAR(128) PRIMARY KEY, value BLOB, serialized INT, expire INT)')===false) + throw new TConfigurationException('sqlitecache_table_creation_failed',sqlite_error_string(sqlite_last_error())); + } + } + else + throw new TConfigurationException('sqlitecache_table_creation_failed',sqlite_error_string(sqlite_last_error())); + $this->_db->query('DELETE FROM '.self::CACHE_TABLE.' WHERE expire<>0 AND expire<'.time()); + $this->_initialized=true; + $this->getApplication()->setCache($this); + } + + /** + * @return string database file path (in namespace form) + */ + public function getDbFile() + { + return $this->_file; + } + + /** + * @param string database file path (in namespace form) + * @throws TInvalidOperationException if the module is already initialized + * @throws TConfigurationException if the file is not in proper namespace format + */ + public function setDbFile($value) + { + if($this->_initialized) + throw new TInvalidOperationException('sqlitecache_dbfile_unchangeable'); + else if(($this->_file=Prado::getPathOfNamespace($value,self::DB_FILE_EXT))===null) + throw new TConfigurationException('sqlitecache_dbfile_invalid',$value); + } + + /** + * Retrieves a value from cache with a specified key. + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function get($key) + { + $sql='SELECT serialized,value FROM '.self::CACHE_TABLE.' WHERE key=\''.md5($key).'\' AND (expire=0 OR expire>'.time().')'; + if(($ret=$this->_db->query($sql))!=false && ($row=$ret->fetch(SQLITE_ASSOC))!==false) + return $row['serialized']?Prado::unserialize($row['value']):$row['value']; + else + return false; + } + + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. + * + * Note, avoid using this method whenever possible. Database insertion is + * very expensive. Try using {@link add} instead, which will not store the value + * if the key is already in cache. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($key,$value,$expire=0) + { + $serialized=is_string($value)?0:1; + $value1=sqlite_escape_string($serialized?Prado::serialize($value):$value); + if($expire && $expire<=self::EXPIRE_LIMIT) + $expire=time()+$expire; + $sql='REPLACE INTO '.self::CACHE_TABLE.' VALUES(\''.md5($key).'\',\''.$value1.'\','.$serialized.','.$expire.')'; + return $this->_db->query($sql)!==false; + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($key,$value,$expire=0) + { + $serialized=is_string($value)?0:1; + $value1=sqlite_escape_string($serialized?Prado::serialize($value):$value); + if($expire && $expire<=self::EXPIRE_LIMIT) + $expire=time()+$expire; + $sql='INSERT INTO '.self::CACHE_TABLE.' VALUES(\''.md5($key).'\',\''.$value1.'\','.$serialized.','.$expire.')'; + return @$this->_db->query($sql)!==false; + } + + /** + * Stores a value identified by a key into cache only if the cache contains this key. + * The existing value and expiration time will be overwritten with the new ones. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the expiration time of the value, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function replace($key,$value,$expire=0) + { + $serialized=is_string($value)?0:1; + $value1=sqlite_escape_string($serialized?Prado::serialize($value):$value); + if($expire && $expire<=self::EXPIRE_LIMIT) + $expire=time()+$expire; + $sql='UPDATE '.self::CACHE_TABLE.' SET value=\''.$value1.'\', serialized='.$serialized.',expire='.$expire.' WHERE key=\''.md5($key).'\''; + $this->_db->query($sql); + $ret=$this->_db->query('SELECT serialized FROM '.self::CACHE_TABLE.' WHERE key=\''.md5($key).'\''); + return ($ret!=false && $ret->numRows()>0); + } + + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($key) + { + $sql='DELETE FROM '.self::CACHE_TABLE.' WHERE key=\''.md5($key).'\''; + return $this->_db->query($sql)!==false; + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return $this->_db->query('DELETE FROM '.self::CACHE_TABLE)!==false; + } +} + +?> \ No newline at end of file diff --git a/framework/Data/TAPCCache.php b/framework/Data/TAPCCache.php deleted file mode 100644 index 5c0a6eba..00000000 --- a/framework/Data/TAPCCache.php +++ /dev/null @@ -1,152 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Data - */ - -/** - * TAPCCache class - * - * TAPCCache implements a cache application module based on {@link http://www.php.net/apc APC}. - * - * By definition, cache does not ensure the existence of a value - * even if it never expires. Cache is not meant to be an persistent storage. - * - * To use this module, the APC PHP extension must be loaded and set in the php.ini file the following: - * - * apc.cache_by_default=0 - * - * - * Some usage examples of TAPCCache are as follows, - * - * $cache=new TAPCCache; // TAPCCache may also be loaded as a Prado application module - * $cache->init(null); - * $cache->add('object',$object); - * $object2=$cache->get('object'); - * - * - * If loaded, TAPCCache will register itself with {@link TApplication} as the - * cache module. It can be accessed via {@link TApplication::getCache()}. - * - * TAPCCache may be configured in application configuration file as follows - * - * - * @author Alban Hanry - * @version $Revision: $ $Date: $ - * @package System.Data - * @since 3.0b - */ -class TAPCCache extends TModule implements ICache -{ - /** - * @var boolean if the module is initialized - */ - private $_initialized=false; - - /** - * @var string a unique prefix used to identify this cache instance from the others - */ - protected $_prefix=null; - - /** - * Initializes this module. - * This method is required by the IModule interface. - * @param TXmlElement configuration for this module, can be null - * @throws TConfigurationException if apc extension is not installed or not started, check your php.ini - */ - public function init($config) - { - if(!extension_loaded('apc')) - throw new TConfigurationException('apccache_extension_required'); - $application=$this->getApplication(); - $this->_prefix=$application->getUniqueID(); - $application->setCache($this); - $this->_initialized=true; - } - - /** - * Retrieves a value from cache with a specified key. - * @return mixed the value stored in cache, false if the value is not in the cache or expired. - */ - public function get($key) - { - if(($value=apc_fetch($this->_prefix.$key))!==false) - return Prado::unserialize($value); - else - return $value; - } - - /** - * Stores a value identified by a key into cache. - * If the cache already contains such a key, the existing value and - * expiration time will be replaced with the new ones. - * - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function set($key,$value,$expiry=0) - { - return apc_store($this->_prefix.$key,Prado::serialize($value),$expiry); - } - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * APC does not support this mode. So calling this method will raise an exception. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * @return boolean true if the value is successfully stored into cache, false otherwise - * @throw new TNotSupportedException if this method is invoked - */ - public function add($key,$value,$expiry=0) - { - throw new TNotSupportedException('apccache_add_unsupported'); - } - - /** - * Stores a value identified by a key into cache only if the cache contains this key. - * The existing value and expiration time will be overwritten with the new ones. - * APC does not support this mode. So calling this method will raise an exception. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * @return boolean true if the value is successfully stored into cache, false otherwise - * @throw new TNotSupportedException if this method is invoked - */ - public function replace($key,$value,$expiry=0) - { - throw new TNotSupportedException('apccache_replace_unsupported'); - } - - /** - * Deletes a value with the specified key from cache - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - public function delete($key) - { - return apc_delete($this->_prefix.$key); - } - - /** - * Deletes all values from cache. - */ - public function flush() - { - return apc_clear_cache('user'); - } - -} - -?> \ No newline at end of file diff --git a/framework/Data/TCache.php b/framework/Data/TCache.php deleted file mode 100644 index b598801d..00000000 --- a/framework/Data/TCache.php +++ /dev/null @@ -1,88 +0,0 @@ - - * @version $Revision: $ $Date: $ - * @package System.Data - * @since 3.0 - */ -interface ICache -{ - /** - * Retrieves a value from cache with a specified key. - * @return mixed the value stored in cache, false if the value is not in the cache or expired. - */ - public function get($id); - /** - * Stores a value identified by a key into cache. - * If the cache already contains such a key, the existing value and - * expiration time will be replaced with the new ones. - * - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function set($id,$value,$expire=0); - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * Nothing will be done if the cache already contains the key. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function add($id,$value,$expire=0); - /** - * Stores a value identified by a key into cache only if the cache contains this key. - * The existing value and expiration time will be overwritten with the new ones. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function replace($id,$value,$expire=0); - /** - * Deletes a value with the specified key from cache - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - public function delete($id); - /** - * Deletes all values from cache. - * Be careful of performing this operation if the cache is shared by multiple applications. - */ - public function flush(); -} - -interface IDependency -{ - -} - -class TTimeDependency -{ -} - -class TFileDependency -{ -} - -class TDirectoryDependency -{ -} - -?> \ No newline at end of file diff --git a/framework/Data/TDataFieldAccessor.php b/framework/Data/TDataFieldAccessor.php deleted file mode 100644 index aa6d7be7..00000000 --- a/framework/Data/TDataFieldAccessor.php +++ /dev/null @@ -1,117 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Data - */ - -/** - * TDataFieldAccessor class - * - * TDataFieldAccessor is a utility class that provides access to a field of some data. - * The accessor attempts to obtain the field value in the following order: - * - If the data is an array, then the field is treated as an array index - * and the corresponding element value is returned; - * - If the data is a TMap or TList object, then the field is treated as a key - * into the map or list, and the corresponding value is returned. - * - If the data is an object, the field is treated as a property or subproperty - * defined with getter methods. For example, if the object has a method called - * getMyValue(), then field 'MyValue' will retrive the result of this method call. - * If getMyValue() returns an object which contains a method getMySubValue(), - * then field 'MyValue.MySubValue' will return that method call result. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Data - * @since 3.0 - */ -class TDataFieldAccessor -{ - /** - * Evaluates the data value at the specified field. - * - If the data is an array, then the field is treated as an array index - * and the corresponding element value is returned; - * - If the data is a TMap or TList object, then the field is treated as a key - * into the map or list, and the corresponding value is returned. - * - If the data is an object, the field is treated as a property or subproperty - * defined with getter methods. For example, if the object has a method called - * getMyValue(), then field 'MyValue' will retrive the result of this method call. - * If getMyValue() returns an object which contains a method getMySubValue(), - * then field 'MyValue.MySubValue' will return that method call result. - * @param mixed data containing the field value, can be an array, TMap, TList or object. - * @param mixed field value - * @return mixed value at the specified field - * @throw TInvalidDataValueException if field or data is invalid - */ - public static function getDataFieldValue($data,$field) - { - if(Prado::getApplication()->getMode()===TApplication::STATE_PERFORMANCE) - { - if(is_array($data) || ($data instanceof ArrayAccess)) - return $data[$field]; - else if(is_object($data)) - { - if(strpos($field,'.')===false) // simple field - { - if(property_exists($data,$field)) - return $data->{$field}; - else - return call_user_func(array($data,'get'.$field)); - } - else // field in the format of xxx.yyy.zzz - { - $object=$data; - foreach(explode('.',$field) as $f) - $object=call_user_func(array($object,'get'.$f)); - return $object; - } - } - else - throw new TInvalidDataValueException('datafieldaccessor_data_invalid',$field); - } - else - { - if(is_array($data) || ($data instanceof ArrayAccess)) - { - if(isset($data[$field]) || $data[$field]===null) - return $data[$field]; - else - throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field); - } - else if(is_object($data)) - { - if(strpos($field,'.')===false) // simple field - { - if(property_exists($data,$field)) - return $data->{$field}; - else if(is_callable(array($data,'get'.$field))) - return call_user_func(array($data,'get'.$field)); - else - throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field); - } - else // field in the format of xxx.yyy.zzz - { - $object=$data; - foreach(explode('.',$field) as $f) - { - $getter='get'.$f; - if(is_callable(array($object,$getter))) - $object=call_user_func(array($object,$getter)); - else - throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field); - } - return $object; - } - } - else - throw new TInvalidDataValueException('datafieldaccessor_data_invalid',$field); - } - } -} - -?> \ No newline at end of file diff --git a/framework/Data/TMemCache.php b/framework/Data/TMemCache.php deleted file mode 100644 index c71de1de..00000000 --- a/framework/Data/TMemCache.php +++ /dev/null @@ -1,256 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Data - */ - -/** - * TMemCache class - * - * TMemCache implements a cache application module based on {@link http://www.danga.com/memcached/ memcached}. - * - * TMemCache can be configured with the Host and Port properties, which - * specify the host and port of the memcache server to be used. - * By default, they take the value 'localhost' and 11211, respectively. - * These properties must be set before {@link init} is invoked. - * - * The following basic cache operations are implemented: - * - {@link get} : retrieve the value with a key (if any) from cache - * - {@link set} : store the value with a key into cache - * - {@link add} : store the value only if cache does not have this key - * - {@link replace} : store the value only if cache has this key - * - {@link delete} : delete the value with the specified key from cache - * - {@link flush} : delete all values from cache - * - * Each value is associated with an expiration time. The {@link get} operation - * ensures that any expired value will not be returned. The expiration time can - * be specified by the number of seconds (maximum 60*60*24*30) - * or a UNIX timestamp. A expiration time 0 represents never expire. - * - * By definition, cache does not ensure the existence of a value - * even if it never expires. Cache is not meant to be an persistent storage. - * - * Also note, there is no security measure to protected data in memcache. - * All data in memcache can be accessed by any process running in the system. - * - * To use this module, the memcache PHP extension must be loaded. - * - * Some usage examples of TMemCache are as follows, - * - * $cache=new TMemCache; // TMemCache may also be loaded as a Prado application module - * $cache->init(null); - * $cache->add('object',$object); - * $object2=$cache->get('object'); - * - * - * If loaded, TMemCache will register itself with {@link TApplication} as the - * cache module. It can be accessed via {@link TApplication::getCache()}. - * - * TMemCache may be configured in application configuration file as follows - * - * where {@link getHost Host} and {@link getPort Port} are configurable properties - * of TMemCache. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Data - * @since 3.0 - */ -class TMemCache extends TModule implements ICache -{ - /** - * @var boolean if the module is initialized - */ - private $_initialized=false; - /** - * @var Memcache the Memcache instance - */ - private $_cache=null; - /** - * @var string a unique prefix used to identify this cache instance from the others - */ - private $_prefix=null; - /** - * @var string host name of the memcache server - */ - private $_host='localhost'; - /** - * @var integer the port number of the memcache server - */ - private $_port=11211; - - /** - * Destructor. - * Disconnect the memcache server. - */ - public function __destruct() - { - if($this->_cache!==null) - $this->_cache->close(); - } - - /** - * Initializes this module. - * This method is required by the IModule interface. It makes sure that - * UniquePrefix has been set, creates a Memcache instance and connects - * to the memcache server. - * @param TApplication Prado application, can be null - * @param TXmlElement configuration for this module, can be null - * @throws TConfigurationException if memcache extension is not installed or memcache sever connection fails - */ - public function init($config) - { - if(!extension_loaded('memcache')) - throw new TConfigurationException('memcache_extension_required'); - $this->_cache=new Memcache; - if($this->_cache->connect($this->_host,$this->_port)===false) - throw new TConfigurationException('memcache_connection_failed',$this->_host,$this->_port); - $application=$this->getApplication(); - $this->_prefix=$application->getUniqueID(); - $application->setCache($this); - $this->_initialized=true; - } - - /** - * @return string host name of the memcache server - */ - public function getHost() - { - return $this->_host; - } - - /** - * @param string host name of the memcache server - * @throws TInvalidOperationException if the module is already initialized - */ - public function setHost($value) - { - if($this->_initialized) - throw new TInvalidOperationException('memcache_host_unchangeable'); - else - $this->_host=$value; - } - - /** - * @return integer port number of the memcache server - */ - public function getPort() - { - return $this->_port; - } - - /** - * @param integer port number of the memcache server - * @throws TInvalidOperationException if the module is already initialized - */ - public function setPort($value) - { - if($this->_initialized) - throw new TInvalidOperationException('memcache_port_unchangeable'); - else - $this->_port=TPropertyValue::ensureInteger($value); - } - - /** - * Retrieves a value from cache with a specified key. - * @return mixed the value stored in cache, false if the value is not in the cache or expired. - */ - public function get($key) - { - return $this->_cache->get($this->generateUniqueKey($key)); - } - - /** - * Stores a value identified by a key into cache. - * If the cache already contains such a key, the existing value and - * expiration time will be replaced with the new ones. - * - * Note, avoid using this method whenever possible. Database insertion is - * very expensive. Try using {@link add} instead, which will not store the value - * if the key is already in cache. - * - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function set($key,$value,$expire=0) - { - return $this->_cache->set($this->generateUniqueKey($key),$value,0,$expire); - } - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * Nothing will be done if the cache already contains the key. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function add($key,$value,$expiry=0) - { - return $this->_cache->add($this->generateUniqueKey($key),$value,0,$expiry); - } - - /** - * Stores a value identified by a key into cache only if the cache contains this key. - * The existing value and expiration time will be overwritten with the new ones. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function replace($key,$value,$expiry=0) - { - return $this->_cache->replace($this->generateUniqueKey($key),$value,0,$expiry); - } - - /** - * Deletes a value with the specified key from cache - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - public function delete($key) - { - return $this->_cache->delete($this->generateUniqueKey($key)); - } - - /** - * Deletes all values from cache. - * Be careful of performing this operation if the cache is shared by multiple applications. - */ - public function flush() - { - return $this->_cache->flush(); - } - - /** - * Generates a unique key based on a given user key. - * This method generates a unique key with the memcache. - * The key is made unique by prefixing with a unique string that is supposed - * to be unique among applications using the same memcache. - * @param string user key - * @param string a unique key - */ - protected function generateUniqueKey($key) - { - return md5($this->_prefix.$key); - } -} - -?> \ No newline at end of file diff --git a/framework/Data/TSimpleDateFormatter.php b/framework/Data/TSimpleDateFormatter.php deleted file mode 100644 index ec69a045..00000000 --- a/framework/Data/TSimpleDateFormatter.php +++ /dev/null @@ -1,354 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2006 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Data - */ - -/** - * TSimpleDateFormatter class. - * - * Formats and parses dates using the SimpleDateFormat pattern. - * This pattern is compatible with the I18N and java's SimpleDateFormatter. - * - * Pattern | Description - * ---------------------------------------------------- - * d | Day of month 1 to 31, no padding - * dd | Day of monath 01 to 31, zero leading - * M | Month digit 1 to 12, no padding - * MM | Month digit 01 to 12, zero leading - * yy | 2 year digit, e.g., 96, 05 - * yyyy | 4 year digit, e.g., 2005 - * ---------------------------------------------------- - * - * - * Usage example, to format a date - * - * $formatter = new TSimpleDateFormatter("dd/MM/yyy"); - * echo $formatter->format(time()); - * - * - * To parse the date string into a date timestamp. - * - * $formatter = new TSimpleDateFormatter("d-M-yyy"); - * echo $formatter->parse("24-6-2005"); - * - * - * @author Wei Zhuo - * @version $Revision: $ $Date: $ - * @package System.Data - * @since 3.0 - */ -class TSimpleDateFormatter -{ - /** - * Formatting pattern. - * @var string - */ - private $pattern; - - /** - * Charset, default is 'UTF-8' - * @var string - */ - private $charset = 'UTF-8'; - - /** - * Constructor, create a new date time formatter. - * @param string formatting pattern. - * @param string pattern and value charset - */ - public function __construct($pattern, $charset='UTF-8') - { - $this->setPattern($pattern); - $this->setCharset($charset); - } - - /** - * @return string formatting pattern. - */ - public function getPattern() - { - return $this->pattern; - } - - /** - * @param string formatting pattern. - */ - public function setPattern($pattern) - { - $this->pattern = $pattern; - } - - /** - * @return string formatting charset. - */ - public function getCharset() - { - return $this->charset; - } - - /** - * @param string formatting charset. - */ - public function setCharset($charset) - { - $this->charset = $charset; - } - - /** - * Format the date according to the pattern. - * @param string|int the date to format, either integer or a string readable by strtotime. - * @return string formatted date. - */ - public function format($value) - { - $date = $this->getDate($value); - $bits['yyyy'] = $date['year']; - $bits['yy'] = substr("{$date['year']}", -2); - - $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT); - $bits['M'] = $date['mon']; - - $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT); - $bits['d'] = $date['mday']; - - return str_replace(array_keys($bits), $bits, $this->pattern); - } - - public function getMonthPattern() - { - if(is_int(strpos($this->pattern, 'MMMM'))) - return 'MMMM'; - if(is_int(strpos($this->pattern, 'MMM'))) - return 'MMM'; - if(is_int(strpos($this->pattern, 'MM'))) - return 'MM'; - if(is_int(strpos($this->pattern, 'M'))) - return 'M'; - return false; - } - - public function getDayPattern() - { - if(is_int(strpos($this->pattern, 'dd'))) - return 'dd'; - if(is_int(strpos($this->pattern, 'd'))) - return 'd'; - return false; - } - - public function getYearPattern() - { - if(is_int(strpos($this->pattern, 'yyyy'))) - return 'yyyy'; - if(is_int(strpos($this->pattern, 'yy'))) - return 'yy'; - return false; - } - - public function getDayMonthYearOrdering() - { - $ordering = array(); - if(is_int($day= strpos($this->pattern, 'd'))) - $ordering['day'] = $day; - if(is_int($month= strpos($this->pattern, 'M'))) - $ordering['month'] = $month; - if(is_int($year= strpos($this->pattern, 'yy'))) - $ordering['year'] = $year; - asort($ordering); - return array_keys($ordering); - } - - /** - * Gets the time stamp from string or integer. - * @param string|int date to parse - * @return int parsed date time stamp - */ - private function getDate($value) - { - if(is_int($value)) - return @getdate($value); - $date = @strtotime($value); - if($date < 0) - throw new TInvalidDataValueException('invalid_date', $value); - return @getdate($date); - } - - /** - * @return boolean true if the given value matches with the date pattern. - */ - public function isValidDate($value) - { - return !is_null($this->parse($value, false)); - } - - /** - * Parse the string according to the pattern. - * @param string date string to parse - * @return int date time stamp - * @throws TInvalidDataValueException if date string is malformed. - */ - public function parse($value,$defaultToCurrentTime=true) - { - if(!is_string($value)) - throw new TInvalidDataValueException('date_to_parse_must_be_string', $value); - - if(empty($this->pattern)) return time(); - - $date = $this->getDate(time()); - - if($this->length(trim($value)) < 1) - return $defaultToCurrentTime ? $date : null; - - $pattern = $this->pattern; - - $i_val = 0; - $i_format = 0; - $pattern_length = $this->length($pattern); - $c = ''; - $token=''; - $x=null; $y=null; - - - if($defaultToCurrentTime) - { - $year = "{$date['year']}"; - $month = $date['mon']; - $day = $date['mday']; - } - else - { - $year = null; - $month = null; - $day = null; - } - - while ($i_format < $pattern_length) - { - $c = $this->charAt($pattern,$i_format); - $token=''; - while ($this->charEqual($pattern, $i_format, $c) - && ($i_format < $pattern_length)) - { - $token .= $this->charAt($pattern, $i_format++); - } - - if ($token=='yyyy' || $token=='yy' || $token=='y') - { - if ($token=='yyyy') { $x=4;$y=4; } - if ($token=='yy') { $x=2;$y=2; } - if ($token=='y') { $x=2;$y=4; } - $year = $this->getInteger($value,$i_val,$x,$y); - if(is_null($year)) - throw new TInvalidDataValueException('Invalid year', $value); - $i_val += strlen($year); - if(strlen($year) == 2) - { - $iYear = intval($year); - if($iYear > 70) - $year = $iYear + 1900; - else - $year = $iYear + 2000; - } - $year = intval($year); - } - elseif($token=='MM' || $token=='M') - { - $month=$this->getInteger($value,$i_val, - $this->length($token),2); - $iMonth = intval($month); - if(is_null($month) || $iMonth < 1 || $iMonth > 12 ) - throw new TInvalidDataValueException('Invalid month', $value); - $i_val += strlen($month); - $month = $iMonth; - } - elseif ($token=='dd' || $token=='d') - { - $day = $this->getInteger($value,$i_val, - $this->length($token), 2); - $iDay = intval($day); - if(is_null($day) || $iDay < 1 || $iDay >31) - throw new TInvalidDataValueException('Invalid day', $value); - $i_val += strlen($day); - $day = $iDay; - } - else - { - if($this->substring($value, $i_val, $this->length($token)) != $token) - throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value); - else - $i_val += $this->length($token); - } - } - if ($i_val != $this->length($value)) - throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value); - - if(!$defaultToCurrentTime && (is_null($month) || is_null($day) || is_null($year))) - return null; - else - return $this->getDate(@mktime(0, 0, 0, $month, $day, $year)); - } - - /** - * Calculate the length of a string, may be consider iconv_strlen? - */ - private function length($string) - { - //use iconv_strlen or just strlen? - return strlen($string); - } - - /** - * Get the char at a position. - */ - private function charAt($string, $pos) - { - return $this->substring($string, $pos, 1); - } - - /** - * Gets a portion of a string, uses iconv_substr. - */ - private function substring($string, $start, $length) - { - return iconv_substr($string, $start, $length); - } - - /** - * Returns true if char at position equals a particular char. - */ - private function charEqual($string, $pos, $char) - { - return $this->charAt($string, $pos) == $char; - } - - /** - * Gets integer from part of a string, allows integers of any length. - * @param string string to retrieve the integer from. - * @param int starting position - * @param int minimum integer length - * @param int maximum integer length - * @return string integer portition of the string, null otherwise - */ - private function getInteger($str,$i,$minlength,$maxlength) - { - //match for digits backwards - for ($x = $maxlength; $x >= $minlength; $x--) - { - $token= $this->substring($str, $i,$x); - if ($this->length($token) < $minlength) - return null; - if (preg_match('/^\d+$/', $token)) - return $token; - } - return null; - } -} - -?> \ No newline at end of file diff --git a/framework/Data/TSqliteCache.php b/framework/Data/TSqliteCache.php deleted file mode 100644 index 1654d577..00000000 --- a/framework/Data/TSqliteCache.php +++ /dev/null @@ -1,265 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Data - */ - -/** - * TSqliteCache class - * - * TSqliteCache implements a cache application module based on SQLite database. - * - * The database file is specified by the {@link setDbFile DbFile} property. - * If not set, the database file will be created under the system state path. - * If the specified database file does not exist, it will be created automatically. - * Make sure the directory containing the specified DB file and the file itself is - * writable by the Web server process. - * - * The following basic cache operations are implemented: - * - {@link get} : retrieve the value with a key (if any) from cache - * - {@link set} : store the value with a key into cache - * - {@link add} : store the value only if cache does not have this key - * - {@link replace} : store the value only if cache has this key - * - {@link delete} : delete the value with the specified key from cache - * - {@link flush} : delete all values from cache - * - * Each value is associated with an expiration time. The {@link get} operation - * ensures that any expired value will not be returned. The expiration time can - * be specified by the number of seconds (maximum 60*60*24*30) - * or a UNIX timestamp. A expiration time 0 represents never expire. - * - * By definition, cache does not ensure the existence of a value - * even if it never expires. Cache is not meant to be an persistent storage. - * - * Do not use the same database file for multiple applications using TSqliteCache. - * Also note, cache is shared by all user sessions of an application. - * - * To use this module, the sqlite PHP extension must be loaded. Sqlite extension - * is no longer loaded by default since PHP 5.1. - * - * Some usage examples of TSqliteCache are as follows, - * - * $cache=new TSqliteCache; // TSqliteCache may also be loaded as a Prado application module - * $cache->setDbFile($dbFilePath); - * $cache->init(null); - * $cache->add('object',$object); - * $object2=$cache->get('object'); - * - * - * If loaded, TSqliteCache will register itself with {@link TApplication} as the - * cache module. It can be accessed via {@link TApplication::getCache()}. - * - * TMemCache may be configured in application configuration file as follows - * - * where {@link getDbFile DbFile} is a property specifying the location of the - * SQLite DB file (in the namespace format). - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Data - * @since 3.0 - */ -class TSqliteCache extends TModule implements ICache -{ - /** - * name of the table storing cache data - */ - const CACHE_TABLE='cache'; - /** - * extension of the db file name - */ - const DB_FILE_EXT='.db'; - /** - * maximum number of seconds specified as expire - */ - const EXPIRE_LIMIT=2592000; // 30 days - - /** - * @var boolean if the module has been initialized - */ - private $_initialized=false; - /** - * @var SQLiteDatabase the sqlite database instance - */ - private $_db=null; - /** - * @var string the database file name - */ - private $_file=null; - - /** - * Destructor. - * Disconnect the db connection. - */ - public function __destruct() - { - $this->_db=null; - } - - /** - * Initializes this module. - * This method is required by the IModule interface. It checks if the DbFile - * property is set, and creates a SQLiteDatabase instance for it. - * The database or the cache table does not exist, they will be created. - * Expired values are also deleted. - * @param TXmlElement configuration for this module, can be null - * @throws TConfigurationException if sqlite extension is not installed, - * DbFile is set invalid, or any error happens during creating database or cache table. - */ - public function init($config) - { - if(!function_exists('sqlite_open')) - throw new TConfigurationException('sqlitecache_extension_required'); - if($this->_file===null) - $this->_file=$this->getApplication()->getRuntimePath().'/sqlite.cache'; - $error=''; - if(($this->_db=new SQLiteDatabase($this->_file,0666,$error))===false) - throw new TConfigurationException('sqlitecache_connection_failed',$error); - if(($res=$this->_db->query('SELECT * FROM sqlite_master WHERE tbl_name=\''.self::CACHE_TABLE.'\' AND type=\'table\''))!=false) - { - if($res->numRows()===0) - { - if($this->_db->query('CREATE TABLE '.self::CACHE_TABLE.' (key CHAR(128) PRIMARY KEY, value BLOB, serialized INT, expire INT)')===false) - throw new TConfigurationException('sqlitecache_table_creation_failed',sqlite_error_string(sqlite_last_error())); - } - } - else - throw new TConfigurationException('sqlitecache_table_creation_failed',sqlite_error_string(sqlite_last_error())); - $this->_db->query('DELETE FROM '.self::CACHE_TABLE.' WHERE expire<>0 AND expire<'.time()); - $this->_initialized=true; - $this->getApplication()->setCache($this); - } - - /** - * @return string database file path (in namespace form) - */ - public function getDbFile() - { - return $this->_file; - } - - /** - * @param string database file path (in namespace form) - * @throws TInvalidOperationException if the module is already initialized - * @throws TConfigurationException if the file is not in proper namespace format - */ - public function setDbFile($value) - { - if($this->_initialized) - throw new TInvalidOperationException('sqlitecache_dbfile_unchangeable'); - else if(($this->_file=Prado::getPathOfNamespace($value,self::DB_FILE_EXT))===null) - throw new TConfigurationException('sqlitecache_dbfile_invalid',$value); - } - - /** - * Retrieves a value from cache with a specified key. - * @return mixed the value stored in cache, false if the value is not in the cache or expired. - */ - public function get($key) - { - $sql='SELECT serialized,value FROM '.self::CACHE_TABLE.' WHERE key=\''.md5($key).'\' AND (expire=0 OR expire>'.time().')'; - if(($ret=$this->_db->query($sql))!=false && ($row=$ret->fetch(SQLITE_ASSOC))!==false) - return $row['serialized']?Prado::unserialize($row['value']):$row['value']; - else - return false; - } - - /** - * Stores a value identified by a key into cache. - * If the cache already contains such a key, the existing value and - * expiration time will be replaced with the new ones. - * - * Note, avoid using this method whenever possible. Database insertion is - * very expensive. Try using {@link add} instead, which will not store the value - * if the key is already in cache. - * - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function set($key,$value,$expire=0) - { - $serialized=is_string($value)?0:1; - $value1=sqlite_escape_string($serialized?Prado::serialize($value):$value); - if($expire && $expire<=self::EXPIRE_LIMIT) - $expire=time()+$expire; - $sql='REPLACE INTO '.self::CACHE_TABLE.' VALUES(\''.md5($key).'\',\''.$value1.'\','.$serialized.','.$expire.')'; - return $this->_db->query($sql)!==false; - } - - /** - * Stores a value identified by a key into cache if the cache does not contain this key. - * Nothing will be done if the cache already contains the key. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function add($key,$value,$expire=0) - { - $serialized=is_string($value)?0:1; - $value1=sqlite_escape_string($serialized?Prado::serialize($value):$value); - if($expire && $expire<=self::EXPIRE_LIMIT) - $expire=time()+$expire; - $sql='INSERT INTO '.self::CACHE_TABLE.' VALUES(\''.md5($key).'\',\''.$value1.'\','.$serialized.','.$expire.')'; - return @$this->_db->query($sql)!==false; - } - - /** - * Stores a value identified by a key into cache only if the cache contains this key. - * The existing value and expiration time will be overwritten with the new ones. - * @param string the key identifying the value to be cached - * @param mixed the value to be cached - * @param integer the expiration time of the value, - * 0 means never expire, - * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. - * a number greater than 60*60*24*30 means a UNIX timestamp after which the value will expire. - * @return boolean true if the value is successfully stored into cache, false otherwise - */ - public function replace($key,$value,$expire=0) - { - $serialized=is_string($value)?0:1; - $value1=sqlite_escape_string($serialized?Prado::serialize($value):$value); - if($expire && $expire<=self::EXPIRE_LIMIT) - $expire=time()+$expire; - $sql='UPDATE '.self::CACHE_TABLE.' SET value=\''.$value1.'\', serialized='.$serialized.',expire='.$expire.' WHERE key=\''.md5($key).'\''; - $this->_db->query($sql); - $ret=$this->_db->query('SELECT serialized FROM '.self::CACHE_TABLE.' WHERE key=\''.md5($key).'\''); - return ($ret!=false && $ret->numRows()>0); - } - - /** - * Deletes a value with the specified key from cache - * @param string the key of the value to be deleted - * @return boolean if no error happens during deletion - */ - public function delete($key) - { - $sql='DELETE FROM '.self::CACHE_TABLE.' WHERE key=\''.md5($key).'\''; - return $this->_db->query($sql)!==false; - } - - /** - * Deletes all values from cache. - * Be careful of performing this operation if the cache is shared by multiple applications. - */ - public function flush() - { - return $this->_db->query('DELETE FROM '.self::CACHE_TABLE)!==false; - } -} - -?> \ No newline at end of file diff --git a/framework/Data/TTarFileExtractor.php b/framework/Data/TTarFileExtractor.php deleted file mode 100644 index 12749d16..00000000 --- a/framework/Data/TTarFileExtractor.php +++ /dev/null @@ -1,574 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Data - */ - -/* vim: set ts=4 sw=4: */ -// +----------------------------------------------------------------------+ -// | PHP Version 4 | -// +----------------------------------------------------------------------+ -// | Copyright (c) 1997-2003 The PHP Group | -// +----------------------------------------------------------------------+ -// | This source file is subject to version 3.0 of the PHP license, | -// | that is bundled with this package in the file LICENSE, and is | -// | available through the world-wide-web at the following url: | -// | http://www.php.net/license/3_0.txt. | -// | If you did not receive a copy of the PHP license and are unable to | -// | obtain it through the world-wide-web, please send a note to | -// | license@php.net so we can mail you a copy immediately. | -// +----------------------------------------------------------------------+ -// | Author: Vincent Blavet | -// +----------------------------------------------------------------------+ -// -// $Id: Tar.php,v 1.29 2005/03/17 21:02:31 vblavet Exp $ - -/** - * TTarFileExtractor class - * - * @author Vincent Blavet - * @version $Revision: $ $Date: $ - * @package System.Data - * @since 3.0 - */ -class TTarFileExtractor -{ - /** - * @var string Name of the Tar - */ - private $_tarname=''; - - /** - * @var file descriptor - */ - private $_file=0; - - /** - * @var string Local Tar name of a remote Tar (http:// or ftp://) - */ - private $_temp_tarname=''; - - /** - * Archive_Tar Class constructor. This flavour of the constructor only - * declare a new Archive_Tar object, identifying it by the name of the - * tar file. - * - * @param string $p_tarname The name of the tar archive to create - * @access public - */ - public function __construct($p_tarname) - { - $this->_tarname = $p_tarname; - } - - public function __destruct() - { - $this->_close(); - // ----- Look for a local copy to delete - if ($this->_temp_tarname != '') - @unlink($this->_temp_tarname); - } - - public function extract($p_path='') - { - return $this->extractModify($p_path, ''); - } - - /** - * This method extract all the content of the archive in the directory - * indicated by $p_path. When relevant the memorized path of the - * files/dir can be modified by removing the $p_remove_path path at the - * beginning of the file/dir path. - * While extracting a file, if the directory path does not exists it is - * created. - * While extracting a file, if the file already exists it is replaced - * without looking for last modification date. - * While extracting a file, if the file already exists and is write - * protected, the extraction is aborted. - * While extracting a file, if a directory with the same name already - * exists, the extraction is aborted. - * While extracting a directory, if a file with the same name already - * exists, the extraction is aborted. - * While extracting a file/directory if the destination directory exist - * and is write protected, or does not exist but can not be created, - * the extraction is aborted. - * If after extraction an extracted file does not show the correct - * stored file size, the extraction is aborted. - * When the extraction is aborted, a PEAR error text is set and false - * is returned. However the result can be a partial extraction that may - * need to be manually cleaned. - * - * @param string $p_path The path of the directory where the - * files/dir need to by extracted. - * @param string $p_remove_path Part of the memorized path that can be - * removed if present at the beginning of - * the file/dir path. - * @return boolean true on success, false on error. - * @access public - */ - protected function extractModify($p_path, $p_remove_path) - { - $v_result = true; - $v_list_detail = array(); - - if ($v_result = $this->_openRead()) { - $v_result = $this->_extractList($p_path, $v_list_detail, - "complete", 0, $p_remove_path); - $this->_close(); - } - - return $v_result; - } - - protected function _error($p_message) - { - throw new Exception($p_message); - } - - private function _isArchive($p_filename=null) - { - if ($p_filename == null) { - $p_filename = $this->_tarname; - } - clearstatcache(); - return @is_file($p_filename); - } - - private function _openRead() - { - if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { - - // ----- Look if a local copy need to be done - if ($this->_temp_tarname == '') { - $this->_temp_tarname = uniqid('tar').'.tmp'; - if (!$v_file_from = @fopen($this->_tarname, 'rb')) { - $this->_error('Unable to open in read mode \'' - .$this->_tarname.'\''); - $this->_temp_tarname = ''; - return false; - } - if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { - $this->_error('Unable to open in write mode \'' - .$this->_temp_tarname.'\''); - $this->_temp_tarname = ''; - return false; - } - while ($v_data = @fread($v_file_from, 1024)) - @fwrite($v_file_to, $v_data); - @fclose($v_file_from); - @fclose($v_file_to); - } - - // ----- File to open if the local copy - $v_filename = $this->_temp_tarname; - - } else - // ----- File to open if the normal Tar file - $v_filename = $this->_tarname; - - $this->_file = @fopen($v_filename, "rb"); - - if ($this->_file == 0) { - $this->_error('Unable to open in read mode \''.$v_filename.'\''); - return false; - } - - return true; - } - - private function _close() - { - //if (isset($this->_file)) { - if (is_resource($this->_file)) - { - @fclose($this->_file); - $this->_file = 0; - } - - // ----- Look if a local copy need to be erase - // Note that it might be interesting to keep the url for a time : ToDo - if ($this->_temp_tarname != '') { - @unlink($this->_temp_tarname); - $this->_temp_tarname = ''; - } - - return true; - } - - private function _cleanFile() - { - $this->_close(); - - // ----- Look for a local copy - if ($this->_temp_tarname != '') { - // ----- Remove the local copy but not the remote tarname - @unlink($this->_temp_tarname); - $this->_temp_tarname = ''; - } else { - // ----- Remove the local tarname file - @unlink($this->_tarname); - } - $this->_tarname = ''; - - return true; - } - - private function _readBlock() - { - $v_block = null; - if (is_resource($this->_file)) { - $v_block = @fread($this->_file, 512); - } - return $v_block; - } - - private function _jumpBlock($p_len=null) - { - if (is_resource($this->_file)) { - if ($p_len === null) - $p_len = 1; - - @fseek($this->_file, @ftell($this->_file)+($p_len*512)); - } - return true; - } - - private function _readHeader($v_binary_data, &$v_header) - { - if (strlen($v_binary_data)==0) { - $v_header['filename'] = ''; - return true; - } - - if (strlen($v_binary_data) != 512) { - $v_header['filename'] = ''; - $this->_error('Invalid block size : '.strlen($v_binary_data)); - return false; - } - - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum+=ord(substr($v_binary_data,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156; $i<512; $i++) - $v_checksum+=ord(substr($v_binary_data,$i,1)); - - $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" - ."a8checksum/a1typeflag/a100link/a6magic/a2version/" - ."a32uname/a32gname/a8devmajor/a8devminor", - $v_binary_data); - - // ----- Extract the checksum - $v_header['checksum'] = OctDec(trim($v_data['checksum'])); - if ($v_header['checksum'] != $v_checksum) { - $v_header['filename'] = ''; - - // ----- Look for last block (empty block) - if (($v_checksum == 256) && ($v_header['checksum'] == 0)) - return true; - - $this->_error('Invalid checksum for file "'.$v_data['filename'] - .'" : '.$v_checksum.' calculated, ' - .$v_header['checksum'].' expected'); - return false; - } - - // ----- Extract the properties - $v_header['filename'] = trim($v_data['filename']); - $v_header['mode'] = OctDec(trim($v_data['mode'])); - $v_header['uid'] = OctDec(trim($v_data['uid'])); - $v_header['gid'] = OctDec(trim($v_data['gid'])); - $v_header['size'] = OctDec(trim($v_data['size'])); - $v_header['mtime'] = OctDec(trim($v_data['mtime'])); - if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { - $v_header['size'] = 0; - } - return true; - } - - private function _readLongHeader(&$v_header) - { - $v_filename = ''; - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_content = $this->_readBlock(); - $v_filename .= $v_content; - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - $v_filename .= $v_content; - } - - // ----- Read the next header - $v_binary_data = $this->_readBlock(); - - if (!$this->_readHeader($v_binary_data, $v_header)) - return false; - - $v_header['filename'] = $v_filename; - - return true; - } - - protected function _extractList($p_path, &$p_list_detail, $p_mode, - $p_file_list, $p_remove_path) - { - $v_result=true; - $v_nb = 0; - $v_extract_all = true; - $v_listing = false; - - $p_path = $this->_translateWinPath($p_path, false); - if ($p_path == '' || (substr($p_path, 0, 1) != '/' - && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { - $p_path = "./".$p_path; - } - $p_remove_path = $this->_translateWinPath($p_remove_path); - - // ----- Look for path to remove format (should end by /) - if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) - $p_remove_path .= '/'; - $p_remove_path_size = strlen($p_remove_path); - - switch ($p_mode) { - case "complete" : - $v_extract_all = true; - $v_listing = false; - break; - case "partial" : - $v_extract_all = false; - $v_listing = false; - break; - case "list" : - $v_extract_all = false; - $v_listing = true; - break; - default : - $this->_error('Invalid extract mode ('.$p_mode.')'); - return false; - } - - clearstatcache(); - - while (strlen($v_binary_data = $this->_readBlock()) != 0) - { - $v_extract_file = false; - $v_extraction_stopped = 0; - - if (!$this->_readHeader($v_binary_data, $v_header)) - return false; - - if ($v_header['filename'] == '') { - continue; - } - - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) - return false; - } - - if ((!$v_extract_all) && (is_array($p_file_list))) { - // ----- By default no unzip if the file is not found - $v_extract_file = false; - - for ($i=0; $i strlen($p_file_list[$i])) - && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) - == $p_file_list[$i])) { - $v_extract_file = true; - break; - } - } - - // ----- It is a file, so compare the file names - elseif ($p_file_list[$i] == $v_header['filename']) { - $v_extract_file = true; - break; - } - } - } else { - $v_extract_file = true; - } - - // ----- Look if this file need to be extracted - if (($v_extract_file) && (!$v_listing)) - { - if (($p_remove_path != '') - && (substr($v_header['filename'], 0, $p_remove_path_size) - == $p_remove_path)) - $v_header['filename'] = substr($v_header['filename'], - $p_remove_path_size); - if (($p_path != './') && ($p_path != '/')) { - while (substr($p_path, -1) == '/') - $p_path = substr($p_path, 0, strlen($p_path)-1); - - if (substr($v_header['filename'], 0, 1) == '/') - $v_header['filename'] = $p_path.$v_header['filename']; - else - $v_header['filename'] = $p_path.'/'.$v_header['filename']; - } - if (file_exists($v_header['filename'])) { - if ( (@is_dir($v_header['filename'])) - && ($v_header['typeflag'] == '')) { - $this->_error('File '.$v_header['filename'] - .' already exists as a directory'); - return false; - } - if ( ($this->_isArchive($v_header['filename'])) - && ($v_header['typeflag'] == "5")) { - $this->_error('Directory '.$v_header['filename'] - .' already exists as a file'); - return false; - } - if (!is_writeable($v_header['filename'])) { - $this->_error('File '.$v_header['filename'] - .' already exists and is write protected'); - return false; - } - if (filemtime($v_header['filename']) > $v_header['mtime']) { - // To be completed : An error or silent no replace ? - } - } - - // ----- Check the directory availability and create it if necessary - elseif (($v_result - = $this->_dirCheck(($v_header['typeflag'] == "5" - ?$v_header['filename'] - :dirname($v_header['filename'])))) != 1) { - $this->_error('Unable to create path for '.$v_header['filename']); - return false; - } - - if ($v_extract_file) { - if ($v_header['typeflag'] == "5") { - if (!@file_exists($v_header['filename'])) { - if (!@mkdir($v_header['filename'], 0777)) { - $this->_error('Unable to create directory {' - .$v_header['filename'].'}'); - return false; - } - } - } else { - if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { - $this->_error('Error while opening {'.$v_header['filename'] - .'} in write binary mode'); - return false; - } else { - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_content = $this->_readBlock(); - fwrite($v_dest_file, $v_content, 512); - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); - } - - @fclose($v_dest_file); - - // ----- Change the file mode, mtime - @touch($v_header['filename'], $v_header['mtime']); - // To be completed - //chmod($v_header[filename], DecOct($v_header[mode])); - } - - // ----- Check the file size - clearstatcache(); - if (filesize($v_header['filename']) != $v_header['size']) { - $this->_error('Extracted file '.$v_header['filename'] - .' does not have the correct file size \'' - .filesize($v_header['filename']) - .'\' ('.$v_header['size'] - .' expected). Archive may be corrupted.'); - return false; - } - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - - /* TBC : Seems to be unused ... - if ($this->_compress) - $v_end_of_file = @gzeof($this->_file); - else - $v_end_of_file = @feof($this->_file); - */ - - if ($v_listing || $v_extract_file || $v_extraction_stopped) { - // ----- Log extracted files - if (($v_file_dir = dirname($v_header['filename'])) - == $v_header['filename']) - $v_file_dir = ''; - if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) - $v_file_dir = '/'; - - $p_list_detail[$v_nb++] = $v_header; - } - } - - return true; - } - - /** - * Check if a directory exists and create it (including parent - * dirs) if not. - * - * @param string $p_dir directory to check - * - * @return bool true if the directory exists or was created - */ - protected function _dirCheck($p_dir) - { - if ((@is_dir($p_dir)) || ($p_dir == '')) - return true; - - $p_parent_dir = dirname($p_dir); - - if (($p_parent_dir != $p_dir) && - ($p_parent_dir != '') && - (!$this->_dirCheck($p_parent_dir))) - return false; - - if (!@mkdir($p_dir, 0777)) { - $this->_error("Unable to create directory '$p_dir'"); - return false; - } - - return true; - } - - protected function _translateWinPath($p_path, $p_remove_disk_letter=true) - { - if (substr(PHP_OS, 0, 3) == 'WIN') { - // ----- Look for potential disk letter - if ( ($p_remove_disk_letter) - && (($v_position = strpos($p_path, ':')) != false)) { - $p_path = substr($p_path, $v_position+1); - } - // ----- Change potential windows directory separator - if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { - $p_path = strtr($p_path, '\\', '/'); - } - } - return $p_path; - } -} -?> diff --git a/framework/Data/TXmlDocument.php b/framework/Data/TXmlDocument.php deleted file mode 100644 index dfd65ebe..00000000 --- a/framework/Data/TXmlDocument.php +++ /dev/null @@ -1,455 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Data - */ - -/** - * TXmlElement class. - * - * TXmlElement represents an XML element node. - * You can obtain its tagname, attributes, text between the openning and closing - * tags via the TagName, Attributes, and Value properties, respectively. - * You can also retrieve its parent and child elements by Parent and Elements - * properties, respectively. - * - * TBD: xpath - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -class TXmlElement extends TComponent -{ - /** - * @var TXmlElement parent of this element - */ - private $_parent=null; - /** - * @var string tagname of this element - */ - private $_tagName; - /** - * @var string text enclosed between openning and closing tags of this element - */ - private $_value; - /** - * @var TXmlElementList list of child elements of this element - */ - private $_elements=null; - /** - * @var TMap attributes of this element - */ - private $_attributes=null; - - /** - * Constructor. - * @param string tagname for this element - */ - public function __construct($tagName) - { - $this->setTagName($tagName); - } - - /** - * @return TXmlElement parent element of this element - */ - public function getParent() - { - return $this->_parent; - } - - /** - * @param TXmlElement parent element of this element - */ - public function setParent($parent) - { - $this->_parent=$parent; - } - - /** - * @return string tagname of this element - */ - public function getTagName() - { - return $this->_tagName; - } - - /** - * @param string tagname of this element - */ - public function setTagName($tagName) - { - $this->_tagName=$tagName; - } - - /** - * @return string text enclosed between opening and closing tag of this element - */ - public function getValue() - { - return $this->_value; - } - - /** - * @param string text enclosed between opening and closing tag of this element - */ - public function setValue($value) - { - $this->_value=$value; - } - - /** - * @return boolean true if this element has child elements - */ - public function getHasElement() - { - return $this->_elements!==null && $this->_elements->getCount()>0; - } - - /** - * @return boolean true if this element has attributes - */ - public function getHasAttribute() - { - return $this->_attributes!==null && $this->_attributes->getCount()>0; - } - - /** - * @return string the attribute specified by the name, null if no such attribute - */ - public function getAttribute($name) - { - if($this->_attributes!==null) - return $this->_attributes->itemAt($name); - else - return null; - } - - /** - * @return TXmlElementList list of child elements - */ - public function getElements() - { - if(!$this->_elements) - $this->_elements=new TXmlElementList($this); - return $this->_elements; - } - - /** - * @return TMap list of attributes - */ - public function getAttributes() - { - if(!$this->_attributes) - $this->_attributes=new TMap; - return $this->_attributes; - } - - /** - * @return TXmlElement the first child element that has the specified tagname, null if not found - */ - public function getElementByTagName($tagName) - { - if($this->_elements) - { - foreach($this->_elements as $element) - if($element->_tagName===$tagName) - return $element; - } - return null; - } - - /** - * @return TList list of all child elements that have the specified tagname - */ - public function getElementsByTagName($tagName) - { - $list=new TList; - if($this->_elements) - { - foreach($this->_elements as $element) - if($element->_tagName===$tagName) - $list->add($element); - } - return $list; - } - - /** - * @return string string representation of this element - */ - public function toString($indent=0) - { - $attr=''; - if($this->_attributes!==null) - { - foreach($this->_attributes as $name=>$value) - $attr.=" $name=\"$value\""; - } - $prefix=str_repeat(' ',$indent*4); - if($this->getHasElement()) - { - $str=$prefix."<{$this->_tagName}$attr>\n"; - foreach($this->getElements() as $element) - $str.=$element->toString($indent+1)."\n"; - $str.=$prefix."_tagName}>"; - return $str; - } - else if($this->getValue()!=='') - { - return $prefix."<{$this->_tagName}$attr>{$this->_value}_tagName}>"; - } - else - return $prefix."<{$this->_tagName}$attr />"; - } -} - -/** - * TXmlDocument class. - * - * TXmlDocument represents a DOM representation of an XML file. - * Besides all properties and methods inherited from {@link TXmlElement}, - * you can load an XML file or string by {@link loadFromFile} or {@link loadFromString}. - * You can also get the version and encoding of the XML document by - * the Version and Encoding properties. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -class TXmlDocument extends TXmlElement -{ - /** - * @var string version of this XML document - */ - private $_version; - /** - * @var string encoding of this XML document - */ - private $_encoding; - - /** - * Constructor. - * @param string version of this XML document - * @param string encoding of this XML document - */ - public function __construct($version='1.0',$encoding='') - { - parent::__construct(''); - $this->setversion($version); - $this->setEncoding($encoding); - } - - /** - * @return string version of this XML document - */ - public function getVersion() - { - return $this->_version; - } - - /** - * @param string version of this XML document - */ - public function setVersion($version) - { - $this->_version=$version; - } - - /** - * @return string encoding of this XML document - */ - public function getEncoding() - { - return $this->_encoding; - } - - /** - * @param string encoding of this XML document - */ - public function setEncoding($encoding) - { - $this->_encoding=$encoding; - } - - /** - * Loads and parses an XML document. - * @param string the XML file path - * @return boolean whether the XML file is parsed successfully - * @throws TIOException if the file fails to be opened. - */ - public function loadFromFile($file) - { - if(($str=@file_get_contents($file))!==false) - return $this->loadFromString($str); - else - throw new TIOException('xmldocument_file_read_failed',$file); - } - - /** - * Loads and parses an XML string. - * The version and encoding will be determined based on the parsing result. - * @param string the XML string - * @return boolean whether the XML string is parsed successfully - */ - public function loadFromString($string) - { - // TODO: since PHP 5.1, we can get parsing errors and throw them as exception - $doc=new DOMDocument(); - if($doc->loadXML($string)===false) - return false; - - $this->setEncoding($doc->encoding); - $this->setVersion($doc->version); - - $element=$doc->documentElement; - $this->setTagName($element->tagName); - $this->setValue($element->nodeValue); - $elements=$this->getElements(); - $attributes=$this->getAttributes(); - $elements->clear(); - $attributes->clear(); - foreach($element->attributes as $name=>$attr) - $attributes->add($name,$attr->value); - foreach($element->childNodes as $child) - { - if($child instanceof DOMElement) - $elements->add($this->buildElement($child)); - } - - return true; - } - - /** - * Saves this XML document as an XML file. - * @param string the name of the file to be stored with XML output - * @throws TIOException if the file cannot be written - */ - public function saveToFile($file) - { - if(($fw=fopen($file,'w'))!==false) - { - fwrite($fw,$this->saveToString()); - fclose($fw); - } - else - throw new TIOException('xmldocument_file_write_failed',$file); - } - - /** - * Saves this XML document as an XML string - * @return string the XML string of this XML document - */ - public function saveToString() - { - $version=empty($this->_version)?' version="1.0"':' version="'.$this->_version.'"'; - $encoding=empty($this->_encoding)?'':' encoding="'.$this->_encoding.'"'; - return "\n".$this->toString(0); - } - - /** - * Recursively converts DOM XML nodes into TXmlElement - * @param DOMXmlNode the node to be converted - * @return TXmlElement the converted TXmlElement - */ - private function buildElement($node) - { - $element=new TXmlElement($node->tagName); - $element->setValue($node->nodeValue); - foreach($node->attributes as $name=>$attr) - $element->getAttributes()->add($name,$attr->value); - foreach($node->childNodes as $child) - { - if($child instanceof DOMElement) - $element->getElements()->add($this->buildElement($child)); - } - return $element; - } -} - - -/** - * TXmlElement class. - * - * TXmlElement represents an XML element node. - * You can obtain its tagname, attributes, text between the openning and closing - * tags via the TagName, Attributes, and Value properties, respectively. - * You can also retrieve its parent and child elements by Parent and Elements - * properties, respectively. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -class TXmlElementList extends TList -{ - /** - * @var TXmlElement owner of this list - */ - private $_o; - - /** - * Constructor. - * @param TXmlElement owner of this list - */ - public function __construct(TXmlElement $owner) - { - $this->_o=$owner; - } - - /** - * @return TXmlElement owner of this list - */ - protected function getOwner() - { - return $this->_o; - } - - - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by performing additional - * operations for each newly added TXmlElement object. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a TXmlElement object. - */ - public function insertAt($index,$item) - { - if($item instanceof TXmlElement) - { - parent::insertAt($index,$item); - if($item->getParent()!==null) - $item->getParent()->getElements()->remove($item); - $item->setParent($this->_o); - } - else - throw new TInvalidDataTypeException('xmlelementlist_xmlelement_required'); - } - - /** - * Removes an item at the specified position. - * This overrides the parent implementation by performing additional - * cleanup work when removing a TXmlElement object. - * @param integer the index of the item to be removed. - * @return mixed the removed item. - */ - public function removeAt($index) - { - $item=parent::removeAt($index); - if($item instanceof TXmlElement) - $item->setParent(null); - return $item; - } -} - -?> \ No newline at end of file diff --git a/framework/IO/TTarFileExtractor.php b/framework/IO/TTarFileExtractor.php new file mode 100644 index 00000000..611662c5 --- /dev/null +++ b/framework/IO/TTarFileExtractor.php @@ -0,0 +1,572 @@ + + * @copyright Copyright © 1997-2003 The PHP Group + * @version $Revision: $ $Date: $ + * @package System.IO + */ + +/* vim: set ts=4 sw=4: */ +// +----------------------------------------------------------------------+ +// | PHP Version 4 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 3.0 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available through the world-wide-web at the following url: | +// | http://www.php.net/license/3_0.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | license@php.net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Author: Vincent Blavet | +// +----------------------------------------------------------------------+ +// +// $Id: Tar.php,v 1.29 2005/03/17 21:02:31 vblavet Exp $ + +/** + * TTarFileExtractor class + * + * @author Vincent Blavet + * @version $Revision: $ $Date: $ + * @package System.IO + * @since 3.0 + */ +class TTarFileExtractor +{ + /** + * @var string Name of the Tar + */ + private $_tarname=''; + + /** + * @var file descriptor + */ + private $_file=0; + + /** + * @var string Local Tar name of a remote Tar (http:// or ftp://) + */ + private $_temp_tarname=''; + + /** + * Archive_Tar Class constructor. This flavour of the constructor only + * declare a new Archive_Tar object, identifying it by the name of the + * tar file. + * + * @param string $p_tarname The name of the tar archive to create + * @access public + */ + public function __construct($p_tarname) + { + $this->_tarname = $p_tarname; + } + + public function __destruct() + { + $this->_close(); + // ----- Look for a local copy to delete + if ($this->_temp_tarname != '') + @unlink($this->_temp_tarname); + } + + public function extract($p_path='') + { + return $this->extractModify($p_path, ''); + } + + /** + * This method extract all the content of the archive in the directory + * indicated by $p_path. When relevant the memorized path of the + * files/dir can be modified by removing the $p_remove_path path at the + * beginning of the file/dir path. + * While extracting a file, if the directory path does not exists it is + * created. + * While extracting a file, if the file already exists it is replaced + * without looking for last modification date. + * While extracting a file, if the file already exists and is write + * protected, the extraction is aborted. + * While extracting a file, if a directory with the same name already + * exists, the extraction is aborted. + * While extracting a directory, if a file with the same name already + * exists, the extraction is aborted. + * While extracting a file/directory if the destination directory exist + * and is write protected, or does not exist but can not be created, + * the extraction is aborted. + * If after extraction an extracted file does not show the correct + * stored file size, the extraction is aborted. + * When the extraction is aborted, a PEAR error text is set and false + * is returned. However the result can be a partial extraction that may + * need to be manually cleaned. + * + * @param string $p_path The path of the directory where the + * files/dir need to by extracted. + * @param string $p_remove_path Part of the memorized path that can be + * removed if present at the beginning of + * the file/dir path. + * @return boolean true on success, false on error. + * @access public + */ + protected function extractModify($p_path, $p_remove_path) + { + $v_result = true; + $v_list_detail = array(); + + if ($v_result = $this->_openRead()) { + $v_result = $this->_extractList($p_path, $v_list_detail, + "complete", 0, $p_remove_path); + $this->_close(); + } + + return $v_result; + } + + protected function _error($p_message) + { + throw new Exception($p_message); + } + + private function _isArchive($p_filename=null) + { + if ($p_filename == null) { + $p_filename = $this->_tarname; + } + clearstatcache(); + return @is_file($p_filename); + } + + private function _openRead() + { + if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { + + // ----- Look if a local copy need to be done + if ($this->_temp_tarname == '') { + $this->_temp_tarname = uniqid('tar').'.tmp'; + if (!$v_file_from = @fopen($this->_tarname, 'rb')) { + $this->_error('Unable to open in read mode \'' + .$this->_tarname.'\''); + $this->_temp_tarname = ''; + return false; + } + if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { + $this->_error('Unable to open in write mode \'' + .$this->_temp_tarname.'\''); + $this->_temp_tarname = ''; + return false; + } + while ($v_data = @fread($v_file_from, 1024)) + @fwrite($v_file_to, $v_data); + @fclose($v_file_from); + @fclose($v_file_to); + } + + // ----- File to open if the local copy + $v_filename = $this->_temp_tarname; + + } else + // ----- File to open if the normal Tar file + $v_filename = $this->_tarname; + + $this->_file = @fopen($v_filename, "rb"); + + if ($this->_file == 0) { + $this->_error('Unable to open in read mode \''.$v_filename.'\''); + return false; + } + + return true; + } + + private function _close() + { + //if (isset($this->_file)) { + if (is_resource($this->_file)) + { + @fclose($this->_file); + $this->_file = 0; + } + + // ----- Look if a local copy need to be erase + // Note that it might be interesting to keep the url for a time : ToDo + if ($this->_temp_tarname != '') { + @unlink($this->_temp_tarname); + $this->_temp_tarname = ''; + } + + return true; + } + + private function _cleanFile() + { + $this->_close(); + + // ----- Look for a local copy + if ($this->_temp_tarname != '') { + // ----- Remove the local copy but not the remote tarname + @unlink($this->_temp_tarname); + $this->_temp_tarname = ''; + } else { + // ----- Remove the local tarname file + @unlink($this->_tarname); + } + $this->_tarname = ''; + + return true; + } + + private function _readBlock() + { + $v_block = null; + if (is_resource($this->_file)) { + $v_block = @fread($this->_file, 512); + } + return $v_block; + } + + private function _jumpBlock($p_len=null) + { + if (is_resource($this->_file)) { + if ($p_len === null) + $p_len = 1; + + @fseek($this->_file, @ftell($this->_file)+($p_len*512)); + } + return true; + } + + private function _readHeader($v_binary_data, &$v_header) + { + if (strlen($v_binary_data)==0) { + $v_header['filename'] = ''; + return true; + } + + if (strlen($v_binary_data) != 512) { + $v_header['filename'] = ''; + $this->_error('Invalid block size : '.strlen($v_binary_data)); + return false; + } + + // ----- Calculate the checksum + $v_checksum = 0; + // ..... First part of the header + for ($i=0; $i<148; $i++) + $v_checksum+=ord(substr($v_binary_data,$i,1)); + // ..... Ignore the checksum value and replace it by ' ' (space) + for ($i=148; $i<156; $i++) + $v_checksum += ord(' '); + // ..... Last part of the header + for ($i=156; $i<512; $i++) + $v_checksum+=ord(substr($v_binary_data,$i,1)); + + $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" + ."a8checksum/a1typeflag/a100link/a6magic/a2version/" + ."a32uname/a32gname/a8devmajor/a8devminor", + $v_binary_data); + + // ----- Extract the checksum + $v_header['checksum'] = OctDec(trim($v_data['checksum'])); + if ($v_header['checksum'] != $v_checksum) { + $v_header['filename'] = ''; + + // ----- Look for last block (empty block) + if (($v_checksum == 256) && ($v_header['checksum'] == 0)) + return true; + + $this->_error('Invalid checksum for file "'.$v_data['filename'] + .'" : '.$v_checksum.' calculated, ' + .$v_header['checksum'].' expected'); + return false; + } + + // ----- Extract the properties + $v_header['filename'] = trim($v_data['filename']); + $v_header['mode'] = OctDec(trim($v_data['mode'])); + $v_header['uid'] = OctDec(trim($v_data['uid'])); + $v_header['gid'] = OctDec(trim($v_data['gid'])); + $v_header['size'] = OctDec(trim($v_data['size'])); + $v_header['mtime'] = OctDec(trim($v_data['mtime'])); + if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { + $v_header['size'] = 0; + } + return true; + } + + private function _readLongHeader(&$v_header) + { + $v_filename = ''; + $n = floor($v_header['size']/512); + for ($i=0; $i<$n; $i++) { + $v_content = $this->_readBlock(); + $v_filename .= $v_content; + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + $v_filename .= $v_content; + } + + // ----- Read the next header + $v_binary_data = $this->_readBlock(); + + if (!$this->_readHeader($v_binary_data, $v_header)) + return false; + + $v_header['filename'] = $v_filename; + + return true; + } + + protected function _extractList($p_path, &$p_list_detail, $p_mode, + $p_file_list, $p_remove_path) + { + $v_result=true; + $v_nb = 0; + $v_extract_all = true; + $v_listing = false; + + $p_path = $this->_translateWinPath($p_path, false); + if ($p_path == '' || (substr($p_path, 0, 1) != '/' + && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { + $p_path = "./".$p_path; + } + $p_remove_path = $this->_translateWinPath($p_remove_path); + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) + $p_remove_path .= '/'; + $p_remove_path_size = strlen($p_remove_path); + + switch ($p_mode) { + case "complete" : + $v_extract_all = true; + $v_listing = false; + break; + case "partial" : + $v_extract_all = false; + $v_listing = false; + break; + case "list" : + $v_extract_all = false; + $v_listing = true; + break; + default : + $this->_error('Invalid extract mode ('.$p_mode.')'); + return false; + } + + clearstatcache(); + + while (strlen($v_binary_data = $this->_readBlock()) != 0) + { + $v_extract_file = false; + $v_extraction_stopped = 0; + + if (!$this->_readHeader($v_binary_data, $v_header)) + return false; + + if ($v_header['filename'] == '') { + continue; + } + + // ----- Look for long filename + if ($v_header['typeflag'] == 'L') { + if (!$this->_readLongHeader($v_header)) + return false; + } + + if ((!$v_extract_all) && (is_array($p_file_list))) { + // ----- By default no unzip if the file is not found + $v_extract_file = false; + + for ($i=0; $i strlen($p_file_list[$i])) + && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) + == $p_file_list[$i])) { + $v_extract_file = true; + break; + } + } + + // ----- It is a file, so compare the file names + elseif ($p_file_list[$i] == $v_header['filename']) { + $v_extract_file = true; + break; + } + } + } else { + $v_extract_file = true; + } + + // ----- Look if this file need to be extracted + if (($v_extract_file) && (!$v_listing)) + { + if (($p_remove_path != '') + && (substr($v_header['filename'], 0, $p_remove_path_size) + == $p_remove_path)) + $v_header['filename'] = substr($v_header['filename'], + $p_remove_path_size); + if (($p_path != './') && ($p_path != '/')) { + while (substr($p_path, -1) == '/') + $p_path = substr($p_path, 0, strlen($p_path)-1); + + if (substr($v_header['filename'], 0, 1) == '/') + $v_header['filename'] = $p_path.$v_header['filename']; + else + $v_header['filename'] = $p_path.'/'.$v_header['filename']; + } + if (file_exists($v_header['filename'])) { + if ( (@is_dir($v_header['filename'])) + && ($v_header['typeflag'] == '')) { + $this->_error('File '.$v_header['filename'] + .' already exists as a directory'); + return false; + } + if ( ($this->_isArchive($v_header['filename'])) + && ($v_header['typeflag'] == "5")) { + $this->_error('Directory '.$v_header['filename'] + .' already exists as a file'); + return false; + } + if (!is_writeable($v_header['filename'])) { + $this->_error('File '.$v_header['filename'] + .' already exists and is write protected'); + return false; + } + if (filemtime($v_header['filename']) > $v_header['mtime']) { + // To be completed : An error or silent no replace ? + } + } + + // ----- Check the directory availability and create it if necessary + elseif (($v_result + = $this->_dirCheck(($v_header['typeflag'] == "5" + ?$v_header['filename'] + :dirname($v_header['filename'])))) != 1) { + $this->_error('Unable to create path for '.$v_header['filename']); + return false; + } + + if ($v_extract_file) { + if ($v_header['typeflag'] == "5") { + if (!@file_exists($v_header['filename'])) { + if (!@mkdir($v_header['filename'], 0777)) { + $this->_error('Unable to create directory {' + .$v_header['filename'].'}'); + return false; + } + } + } else { + if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { + $this->_error('Error while opening {'.$v_header['filename'] + .'} in write binary mode'); + return false; + } else { + $n = floor($v_header['size']/512); + for ($i=0; $i<$n; $i++) { + $v_content = $this->_readBlock(); + fwrite($v_dest_file, $v_content, 512); + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); + } + + @fclose($v_dest_file); + + // ----- Change the file mode, mtime + @touch($v_header['filename'], $v_header['mtime']); + // To be completed + //chmod($v_header[filename], DecOct($v_header[mode])); + } + + // ----- Check the file size + clearstatcache(); + if (filesize($v_header['filename']) != $v_header['size']) { + $this->_error('Extracted file '.$v_header['filename'] + .' does not have the correct file size \'' + .filesize($v_header['filename']) + .'\' ('.$v_header['size'] + .' expected). Archive may be corrupted.'); + return false; + } + } + } else { + $this->_jumpBlock(ceil(($v_header['size']/512))); + } + } else { + $this->_jumpBlock(ceil(($v_header['size']/512))); + } + + /* TBC : Seems to be unused ... + if ($this->_compress) + $v_end_of_file = @gzeof($this->_file); + else + $v_end_of_file = @feof($this->_file); + */ + + if ($v_listing || $v_extract_file || $v_extraction_stopped) { + // ----- Log extracted files + if (($v_file_dir = dirname($v_header['filename'])) + == $v_header['filename']) + $v_file_dir = ''; + if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) + $v_file_dir = '/'; + + $p_list_detail[$v_nb++] = $v_header; + } + } + + return true; + } + + /** + * Check if a directory exists and create it (including parent + * dirs) if not. + * + * @param string $p_dir directory to check + * + * @return bool true if the directory exists or was created + */ + protected function _dirCheck($p_dir) + { + if ((@is_dir($p_dir)) || ($p_dir == '')) + return true; + + $p_parent_dir = dirname($p_dir); + + if (($p_parent_dir != $p_dir) && + ($p_parent_dir != '') && + (!$this->_dirCheck($p_parent_dir))) + return false; + + if (!@mkdir($p_dir, 0777)) { + $this->_error("Unable to create directory '$p_dir'"); + return false; + } + + return true; + } + + protected function _translateWinPath($p_path, $p_remove_disk_letter=true) + { + if (substr(PHP_OS, 0, 3) == 'WIN') { + // ----- Look for potential disk letter + if ( ($p_remove_disk_letter) + && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position+1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; + } +} +?> diff --git a/framework/IO/TTextWriter.php b/framework/IO/TTextWriter.php new file mode 100644 index 00000000..a6bf5751 --- /dev/null +++ b/framework/IO/TTextWriter.php @@ -0,0 +1,60 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.IO + */ + +/** + * TTextWriter class. + * + * TTextWriter implements a memory-based text writer. + * Content written by TTextWriter are stored in memory + * and can be obtained by calling {@link flush()}. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +class TTextWriter extends TComponent implements ITextWriter +{ + private $_str=''; + + /** + * Flushes the content that has been written. + * @return string the content being flushed + */ + public function flush() + { + $str=$this->_str; + $this->_str=''; + return $str; + } + + /** + * Writes a string. + * @param string string to be written + */ + public function write($str) + { + $this->_str.=$str; + } + + /** + * Writers a string and terminates it with a newline. + * @param string content to be written + * @see write + */ + public function writeLine($str='') + { + $this->write($str."\n"); + } +} + +?> \ No newline at end of file diff --git a/framework/Log/TLogRouter.php b/framework/Log/TLogRouter.php deleted file mode 100644 index d5bf3cb1..00000000 --- a/framework/Log/TLogRouter.php +++ /dev/null @@ -1,666 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Log - */ - -/** - * TLogRouter class. - * - * TLogRouter manages routes that record log messages in different media different ways. - * For example, a file log route {@link TFileLogRoute} records log messages - * in log files. An email log route {@link TEmailLogRoute} sends log messages - * to email addresses. - * - * Log routes may be configured in application or page folder configuration files - * or an external configuration file specified by {@link setConfigFile ConfigFile}. - * The format is as follows, - * - * <route class="TFileLogRoute" Categories="System.Web.UI" Levels="Warning" /> - * <route class="TEmailLogRoute" Categories="Application" Levels="Fatal" Emails="admin@pradosoft.com" /> - * - * You can specify multiple routes with different filtering conditions and different - * targets, even if the routes are of the same type. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Log - * @since 3.0 - */ -class TLogRouter extends TModule -{ - /** - * File extension of external configuration file - */ - const CONFIG_FILE_EXT='.xml'; - /** - * @var array list of routes available - */ - private $_routes=array(); - /** - * @var string external configuration file - */ - private $_configFile=null; - - /** - * Initializes this module. - * This method is required by the IModule interface. - * @param TXmlElement configuration for this module, can be null - * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. - */ - public function init($config) - { - if($this->_configFile!==null) - { - if(is_file($this->_configFile)) - { - $dom=new TXmlDocument; - $dom->loadFromFile($this->_configFile); - $this->loadConfig($dom); - } - else - throw new TConfigurationException('logrouter_configfile_invalid',$this->_configFile); - } - $this->loadConfig($config); - $this->getApplication()->attachEventHandler('OnEndRequest',array($this,'collectLogs')); - } - - /** - * Loads configuration from an XML element - * @param TXmlElement configuration node - * @throws TConfigurationException if log route class or type is not specified - */ - private function loadConfig($xml) - { - foreach($xml->getElementsByTagName('route') as $routeConfig) - { - $properties=$routeConfig->getAttributes(); - if(($class=$properties->remove('class'))===null) - throw new TConfigurationException('logrouter_routeclass_required'); - $route=Prado::createComponent($class); - if(!($route instanceof TLogRoute)) - throw new TConfigurationException('logrouter_routetype_invalid'); - foreach($properties as $name=>$value) - $route->setSubproperty($name,$value); - $this->_routes[]=$route; - $route->init($routeConfig); - } - } - - /** - * @return string external configuration file. Defaults to null. - */ - public function getConfigFile() - { - return $this->_configFile; - } - - /** - * @param string external configuration file in namespace format. The file - * must be suffixed with '.xml'. - * @throws TInvalidDataValueException if the file is invalid. - */ - public function setConfigFile($value) - { - if(($this->_configFile=Prado::getPathOfNamespace($value,self::LOG_FILE_EXT))===null) - throw new TConfigurationException('logrouter_configfile_invalid',$value); - } - - /** - * Collects log messages from a logger. - * This method is an event handler to application's EndRequest event. - * @param mixed event parameter - */ - public function collectLogs($param) - { - $logger=Prado::getLogger(); - foreach($this->_routes as $route) - $route->collectLogs($logger); - } -} - -/** - * TLogRoute class. - * - * TLogRoute is the base class for all log route classes. - * A log route object retrieves log messages from a logger and sends it - * somewhere, such as files, emails. - * The messages being retrieved may be filtered first before being sent - * to the destination. The filters include log level filter and log category filter. - * - * To specify level filter, set {@link setLevels Levels} property, - * which takes a string of comma-separated desired level names (e.g. 'Error, Debug'). - * To specify category filter, set {@link setCategories Categories} property, - * which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO'). - * - * Level filter and category filter are combinational, i.e., only messages - * satisfying both filter conditions will they be returned. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Log - * @since 3.0 - */ -abstract class TLogRoute extends TApplicationComponent -{ - /** - * @var array lookup table for level names - */ - protected static $_levelNames=array( - TLogger::DEBUG=>'Debug', - TLogger::INFO=>'Info', - TLogger::NOTICE=>'Notice', - TLogger::WARNING=>'Warning', - TLogger::ERROR=>'Error', - TLogger::ALERT=>'Alert', - TLogger::FATAL=>'Fatal' - ); - /** - * @var array lookup table for level values - */ - protected static $_levelValues=array( - 'debug'=>TLogger::DEBUG, - 'info'=>TLogger::INFO, - 'notice'=>TLogger::NOTICE, - 'warning'=>TLogger::WARNING, - 'error'=>TLogger::ERROR, - 'alert'=>TLogger::ALERT, - 'fatal'=>TLogger::FATAL - ); - /** - * @var integer log level filter (bits) - */ - private $_levels=null; - /** - * @var array log category filter - */ - private $_categories=null; - - /** - * Initializes the route. - * @param TXmlElement configurations specified in {@link TLogRouter}. - */ - public function init($config) - { - } - - /** - * @return integer log level filter - */ - public function getLevels() - { - return $this->_levels; - } - - /** - * @param integer|string integer log level filter (in bits). If the value is - * a string, it is assumed to be comma-separated level names. Valid level names - * include 'Debug', 'Info', 'Notice', 'Warning', 'Error', 'Alert' and 'Fatal'. - */ - public function setLevels($levels) - { - if(is_integer($levels)) - $this->_levels=$levels; - else - { - $this->_levels=null; - $levels=strtolower($levels); - foreach(explode(',',$levels) as $level) - { - $level=trim($level); - if(isset(self::$_levelValues[$level])) - $this->_levels|=self::$_levelValues[$level]; - } - } - } - - /** - * @return array list of categories to be looked for - */ - public function getCategories() - { - return $this->_categories; - } - - /** - * @param array|string list of categories to be looked for. If the value is a string, - * it is assumed to be comma-separated category names. - */ - public function setCategories($categories) - { - if(is_array($categories)) - $this->_categories=$categories; - else - { - $this->_categories=null; - foreach(explode(',',$categories) as $category) - { - if(($category=trim($category))!=='') - $this->_categories[]=$category; - } - } - } - - /** - * @param integer level value - * @return string level name - */ - protected function getLevelName($level) - { - return isset(self::$_levelNames[$level])?self::$_levelNames[$level]:'Unknown'; - } - - /** - * @param string level name - * @return integer level value - */ - protected function getLevelValue($level) - { - return isset(self::$_levelValues[$level])?self::$_levelValues[$level]:0; - } - - /** - * Formats a log message given different fields. - * @param string message content - * @param integer message level - * @param string message category - * @param integer timestamp - * @return string formatted message - */ - protected function formatLogMessage($message,$level,$category,$time) - { - return @date('M d H:i:s',$time).' ['.$this->getLevelName($level).'] ['.$category.'] '.$message."\n"; - } - - /** - * Retrieves log messages from logger to log route specific destination. - * @param TLogger logger instance - */ - public function collectLogs(TLogger $logger) - { - $logs=$logger->getLogs($this->getLevels(),$this->getCategories()); - if(!empty($logs)) - $this->processLogs($logs); - } - - /** - * Processes log messages and sends them to specific destination. - * Derived child classes must implement this method. - * @param array list of messages. Each array element is a (formatted) message string. - */ - abstract protected function processLogs($logs); -} - -/** - * TFileLogRoute class. - * - * TFileLogRoute records log messages in files. - * The log files are stored under {@link setLogPath LogPath} and the file name - * is specified by {@link setLogFile LogFile}. If the size of the log file is - * greater than {@link setMaxFileSize MaxFileSize} (in kilo-bytes), a rotation - * is performed, which renames the current log file by suffixing the file name - * with '.1'. All existing log files are moved backwards one place, i.e., '.2' - * to '.3', '.1' to '.2'. The property {@link setMaxLogFiles MaxLogFiles} - * specifies how many files to be kept. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Log - * @since 3.0 - */ -class TFileLogRoute extends TLogRoute -{ - /** - * @var integer maximum log file size - */ - private $_maxFileSize=512; // in KB - /** - * @var integer number of log files used for rotation - */ - private $_maxLogFiles=2; - /** - * @var string directory storing log files - */ - private $_logPath=null; - /** - * @var string log file name - */ - private $_logFile='prado.log'; - - /** - * @return string directory storing log files. Defaults to application runtime path. - */ - public function getLogPath() - { - if($this->_logPath===null) - $this->_logPath=$this->getApplication()->getRuntimePath(); - return $this->_logPath; - } - - /** - * @param string directory (in namespace format) storing log files. - * @throws TConfigurationException if log path is invalid - */ - public function setLogPath($value) - { - if(($this->_logPath=Prado::getPathOfNamespace($value))===null || !is_dir($this->_logPath) || !is_writable($this->_logPath)) - throw new TConfigurationException('filelogroute_logpath_invalid',$value); - } - - /** - * @return string log file name. Defaults to 'prado.log'. - */ - public function getLogFile() - { - return $this->_logFile; - } - - /** - * @param string log file name - */ - public function setLogFile($value) - { - $this->_logFile=$value; - } - - /** - * @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB). - */ - public function getMaxFileSize() - { - return $this->_maxFileSize; - } - - /** - * @param integer maximum log file size in kilo-bytes (KB). - * @throws TInvalidDataValueException if the value is smaller than 1. - */ - public function setMaxFileSize($value) - { - $this->_maxFileSize=TPropertyValue::ensureInteger($value); - if($this->_maxFileSize<=0) - throw new TInvalidDataValueException('filelogroute_maxfilesize_invalid'); - } - - /** - * @return integer number of files used for rotation. Defaults to 2. - */ - public function getMaxLogFiles() - { - return $this->_maxLogFiles; - } - - /** - * @param integer number of files used for rotation. - */ - public function setMaxLogFiles($value) - { - $this->_maxLogFiles=TPropertyValue::ensureInteger($value); - if($this->_maxLogFiles<1) - throw new TInvalidDataValueException('filelogroute_maxlogfiles_invalid'); - } - - /** - * Saves log messages in files. - * @param array list of log messages - */ - protected function processLogs($logs) - { - $logFile=$this->getLogPath().'/'.$this->getLogFile(); - if(@filesize($logFile)>$this->_maxFileSize*1024) - $this->rotateFiles(); - foreach($logs as $log) - error_log($this->formatLogMessage($log[0],$log[1],$log[2],$log[3]),3,$logFile); - } - - /** - * Rotates log files. - */ - protected function rotateFiles() - { - $file=$this->getLogPath().'/'.$this->getLogFile(); - for($i=$this->_maxLogFiles;$i>0;--$i) - { - $rotateFile=$file.'.'.$i; - if(is_file($rotateFile)) - { - if($i===$this->_maxLogFiles) - unlink($rotateFile); - else - rename($rotateFile,$file.'.'.($i+1)); - } - } - if(is_file($file)) - rename($file,$file.'.1'); - } -} - -/** - * TEmailLogRoute class. - * - * TEmailLogRoute sends selected log messages to email addresses. - * The target email addresses may be specified via {@link setEmails Emails} property. - * Optionally, you may set the email {@link setSubject Subject} and the - * {@link setSentFrom SentFrom} address. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Log - * @since 3.0 - */ -class TEmailLogRoute extends TLogRoute -{ - /** - * Regex pattern for email address. - */ - const EMAIL_PATTERN='/^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/'; - /** - * Default email subject. - */ - const DEFAULT_SUBJECT='Prado Application Log'; - /** - * @var array list of destination email addresses. - */ - private $_emails=array(); - /** - * @var string email subject - */ - private $_subject=''; - /** - * @var string email sent from address - */ - private $_from=''; - - /** - * Initializes the route. - * @param TXmlElement configurations specified in {@link TLogRouter}. - * @throws TConfigurationException if {@link getSentFrom SentFrom} is empty and - * 'sendmail_from' in php.ini is also empty. - */ - public function init($config) - { - if($this->_from==='') - $this->_from=ini_get('sendmail_from'); - if($this->_from==='') - throw new TConfigurationException('emaillogroute_sentfrom_required'); - } - - /** - * Sends log messages to specified email addresses. - * @param array list of log messages - */ - protected function processLogs($logs) - { - $message=''; - foreach($logs as $log) - $message.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]); - $message=wordwrap($message,70); - foreach($this->_emails as $email) - mail($email,$this->getSubject(),$message,"From:{$this->_from}\r\n"); - - } - - /** - * @return array list of destination email addresses - */ - public function getEmails() - { - return $this->_emails; - } - - /** - * @return array|string list of destination email addresses. If the value is - * a string, it is assumed to be comma-separated email addresses. - */ - public function setEmails($emails) - { - if(is_array($emails)) - $this->_emails=$emails; - else - { - $this->_emails=array(); - foreach(explode(',',$emails) as $email) - { - $email=trim($email); - if(preg_match(self::EMAIL_PATTERN,$email)) - $this->_emails[]=$email; - } - } - } - - /** - * @return string email subject. Defaults to TEmailLogRoute::DEFAULT_SUBJECT - */ - public function getSubject() - { - if($this->_subject===null) - $this->_subject=self::DEFAULT_SUBJECT; - return $this->_subject; - } - - /** - * @param string email subject. - */ - public function setSubject($value) - { - $this->_subject=$value; - } - - /** - * @return string send from address of the email - */ - public function getSentFrom() - { - return $this->_from; - } - - /** - * @param string send from address of the email - */ - public function setSentFrom($value) - { - $this->_from=$value; - } -} - -/** - * TBrowserLogRoute class. - * - * TBrowserLogRoute prints selected log messages in the response. - * - * @author Xiang Wei Zhuo - * @version $Revision: $ $Date: $ - * @package System.Log - * @since 3.0 - */ -class TBrowserLogRoute extends TLogRoute -{ - public function processLogs($logs) - { - if(empty($logs) || $this->getApplication()->getMode()==='Performance') return; - $first = $logs[0][3]; - $prev = $first; - $even = true; - $response = $this->getApplication()->getResponse(); - $response->write($this->renderHeader()); - foreach($logs as $log) - { - $timing['total'] = $log[3] - $first; - $timing['delta'] = $log[3] - $prev; - $timing['even'] = !($even = !$even); - $prev=$log[3]; - $response->write($this->renderMessage($log,$timing)); - } - $response->write($this->renderFooter()); - } - - protected function renderHeader() - { - $string = << - - - Application Log - - -   - CategoryMessageTime Spent (s)Cumulated Time Spent (s) - -EOD; - return $string; - } - - protected function renderMessage($log, $info) - { - $bgcolor = $info['even'] ? "#fff" : "#eee"; - $total = sprintf('%0.6f', $info['total']); - $delta = sprintf('%0.6f', $info['delta']); - $color = $this->getColorLevel($log[1]); - $msg = preg_replace('/\(line[^\)]+\)$/','',$log[0]); //remove line number info - $msg = THttpUtility::htmlEncode($msg); - $string = << -   - {$log[2]} - {$msg} - {$delta} - {$total} - -EOD; - return $string; - } - - protected function getColorLevel($level) - { - switch($level) - { - case TLogger::DEBUG: return 'green'; - case TLogger::INFO: return 'black'; - case TLogger::NOTICE: return '#3333FF'; - case TLogger::WARNING: return '#33FFFF'; - case TLogger::ERROR: return '#ff9933'; - case TLogger::ALERT: return '#ff00ff'; - case TLogger::FATAL: return 'red'; - } - return ''; - } - - protected function renderFooter() - { - $string = ""; - foreach(self::$_levelValues as $name => $level) - { - $string .= "getColorLevel($level); - $string .= ";margin: 0.5em;\">".strtoupper($name).""; - } - $string .= ""; - return $string; - } -} -?> \ No newline at end of file diff --git a/framework/Log/TLogger.php b/framework/Log/TLogger.php deleted file mode 100644 index 2eff5568..00000000 --- a/framework/Log/TLogger.php +++ /dev/null @@ -1,130 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System.Log - */ - -/** - * TLogger class. - * - * TLogger records log messages in memory and implements the methods to - * retrieve the messages with filter conditions, including log levels and - * log categories. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Log - * @since 3.0 - */ -class TLogger extends TComponent -{ - /** - * Log levels. - */ - const DEBUG=0x01; - const INFO=0x02; - const NOTICE=0x04; - const WARNING=0x08; - const ERROR=0x10; - const ALERT=0x20; - const FATAL=0x40; - /** - * @var array log messages - */ - private $_logs=array(); - /** - * @var integer log levels (bits) to be filtered - */ - private $_levels; - /** - * @var array list of categories to be filtered - */ - private $_categories; - - /** - * Logs a message. - * Messages logged by this method may be retrieved via {@link getLogs}. - * @param string message to be logged - * @param integer level of the message. Valid values include - * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, - * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. - * @param string category of the message - */ - public function log($message,$level,$category='Uncategorized') - { - $this->_logs[]=array($message,$level,$category,microtime(true)); - } - - /** - * Retrieves log messages. - * Messages may be filtered by log levels and/or categories. - * A level filter is specified by an integer, whose bits indicate the levels interested. - * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels. - * A category filter is specified by concatenating interested category names - * with commas. A message whose category name starts with any filtering category - * will be returned. For example, a category filter 'System.Web, System.IO' - * will return messages under categories such as 'System.Web', 'System.IO', - * 'System.Web.UI', 'System.Web.UI.WebControls', etc. - * Level filter and category filter are combinational, i.e., only messages - * satisfying both filter conditions will they be returned. - * @param integer level filter - * @param string category filter - * @param array list of messages. Each array elements represents one message - * with the following structure: - * array( - * [0] => message - * [1] => level - * [2] => category - * [3] => timestamp); - */ - public function getLogs($levels=null,$categories=null) - { - $this->_levels=$levels; - $this->_categories=$categories; - if(empty($levels) && empty($categories)) - return $this->_logs; - else if(empty($levels)) - return array_values(array_filter(array_filter($this->_logs,array($this,'filterByCategories')))); - else if(empty($categories)) - return array_values(array_filter(array_filter($this->_logs,array($this,'filterByLevels')))); - else - { - $ret=array_values(array_filter(array_filter($this->_logs,array($this,'filterByLevels')))); - return array_values(array_filter(array_filter($ret,array($this,'filterByCategories')))); - } - } - - /** - * Filter function used by {@link getLogs} - * @param array element to be filtered - */ - private function filterByCategories($value) - { - foreach($this->_categories as $category) - { - if($value[2]===$category || strpos($value[2],$category.'.')===0) - return $value; - } - return false; - } - - /** - * Filter function used by {@link getLogs} - * @param array element to be filtered - */ - private function filterByLevels($value) - { - if($value[1] & $this->_levels) - return $value; - else - return false; - } -} - -?> \ No newline at end of file diff --git a/framework/PradoBase.php b/framework/PradoBase.php new file mode 100644 index 00000000..09dcc9e9 --- /dev/null +++ b/framework/PradoBase.php @@ -0,0 +1,604 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System + */ + +/** + * Defines the PRADO framework installation path. + */ +if(!defined('PRADO_DIR')) + define('PRADO_DIR',dirname(__FILE__)); + +/** + * Includes the classes essential for PradoBase class + */ +require_once(PRADO_DIR.'/TComponent.php'); +require_once(PRADO_DIR.'/Exceptions/TException.php'); +require_once(PRADO_DIR.'/Util/TLogger.php'); + +/** + * PradoBase class. + * + * PradoBase implements a few fundamental static methods. + * + * To use the static methods, Use Prado as the class name rather than PradoBase. + * PradoBase is meant to serve as the base class of Prado. The latter might be + * rewritten for customization. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +class PradoBase +{ + /** + * File extension for Prado class files. + */ + const CLASS_FILE_EXT='.php'; + /** + * @var array list of path aliases + */ + private static $_aliases=array('System'=>PRADO_DIR); + /** + * @var array list of namespaces currently in use + */ + private static $_usings=array(); + /** + * @var TApplication the application instance + */ + private static $_application=null; + /** + * @var TLogger logger instance + */ + private static $_logger=null; + + /** + * @return string the version of Prado framework + */ + public static function getVersion() + { + return '3.0RC1'; + } + + /** + * Initializes error handlers. + * This method set error and exception handlers to be functions + * defined in this class. + */ + public static function initErrorHandlers() + { + /** + * Sets error handler to be Prado::phpErrorHandler + */ + set_error_handler(array('PradoBase','phpErrorHandler'),error_reporting()); + /** + * Sets exception handler to be Prado::exceptionHandler + */ + set_exception_handler(array('PradoBase','exceptionHandler')); + } + + /** + * Class autoload loader. + * This method is provided to be invoked within an __auload() magic method. + * @param string class name + */ + public static function autoload($className) + { + include_once($className.self::CLASS_FILE_EXT); + if(!class_exists($className,false) && !interface_exists($className,false)) + self::fatalError("Class file for '$className' cannot be found."); + } + + /** + * @return string a string that can be displayed on your Web page showing powered-by-PRADO information + */ + public static function poweredByPrado() + { + return 'Powered by PRADO'; + } + + /** + * PHP error handler. + * This method should be registered as PHP error handler using + * {@link set_error_handler}. The method throws an exception that + * contains the error information. + * @param integer the level of the error raised + * @param string the error message + * @param string the filename that the error was raised in + * @param integer the line number the error was raised at + */ + public static function phpErrorHandler($errno,$errstr,$errfile,$errline) + { + if(error_reporting()!=0) + throw new TPhpErrorException($errno,$errstr,$errfile,$errline); + } + + /** + * Default exception handler. + * This method should be registered as default exception handler using + * {@link set_exception_handler}. The method tries to use the errorhandler + * module of the Prado application to handle the exception. + * If the application or the module does not exist, it simply echoes the + * exception. + * @param Exception exception that is not caught + */ + public static function exceptionHandler($exception) + { + if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null) + { + $errorHandler->handleError(null,$exception); + } + else + { + echo $exception; + } + exit(1); + } + + /** + * Stores the application instance in the class static member. + * This method helps implement a singleton pattern for TApplication. + * Repeated invocation of this method or the application constructor + * will cause the throw of an exception. + * This method should only be used by framework developers. + * @param TApplication the application instance + * @throws TInvalidOperationException if this method is invoked twice or more. + */ + public static function setApplication($application) + { + if(self::$_application!==null) + throw new TInvalidOperationException('prado_application_singleton_required'); + self::$_application=$application; + } + + /** + * @return TApplication the application singleton, null if the singleton has not be created yet. + */ + public static function getApplication() + { + return self::$_application; + } + + /** + * @return string the path of the framework + */ + public static function getFrameworkPath() + { + return PRADO_DIR; + } + + /** + * Serializes a data. + * The original PHP serialize function has a bug that may not serialize + * properly an object. + * @param mixed data to be serialized + * @return string the serialized data + */ + public static function serialize($data) + { + $arr[0]=$data; + return serialize($arr); + } + + /** + * Unserializes a data. + * The original PHP unserialize function has a bug that may not unserialize + * properly an object. + * @param string data to be unserialized + * @return mixed unserialized data, null if unserialize failed + */ + public static function unserialize($str) + { + $arr=unserialize($str); + return isset($arr[0])?$arr[0]:null; + } + + /** + * Creates a component with the specified type. + * A component type can be either the component class name + * or a namespace referring to the path of the component class file. + * For example, 'TButton', 'System.Web.UI.WebControls.TButton' are both + * valid component type. + * This method can also pass parameters to component constructors. + * All paramters passed to this method except the first one (the component type) + * will be supplied as component constructor paramters. + * @param string component type + * @return TComponent component instance of the specified type + * @throws TInvalidDataValueException if the component type is unknown + */ + public static function createComponent($type) + { + self::using($type); + if(($pos=strrpos($type,'.'))!==false) + $type=substr($type,$pos+1); + if(($n=func_num_args())>1) + { + $args=func_get_args(); + $s='$args[1]'; + for($i=2;$i<$n;++$i) + $s.=",\$args[$i]"; + eval("\$component=new $type($s);"); + return $component; + } + else + return new $type; + } + + /** + * Uses a namespace. + * A namespace ending with an asterisk '*' refers to a directory, otherwise it represents a PHP file. + * If the namespace corresponds to a directory, the directory will be appended + * to the include path. If the namespace corresponds to a file, it will be included (include_once). + * @param string namespace to be used + * @throws TInvalidDataValueException if the namespace is invalid + */ + public static function using($namespace) + { + if(isset(self::$_usings[$namespace]) || class_exists($namespace,false)) + return; + if(($pos=strrpos($namespace,'.'))===false) // a class name + { + try + { + include_once($namespace.self::CLASS_FILE_EXT); + } + catch(Exception $e) + { + if(!class_exists($namespace,false)) + throw new TInvalidOperationException('prado_component_unknown',$namespace); + else + throw $e; + } + } + else if(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null) + { + $className=substr($namespace,$pos+1); + if($className==='*') // a directory + { + if(is_dir($path)) + { + self::$_usings[$namespace]=$path; + set_include_path(get_include_path().PATH_SEPARATOR.$path); + } + else + throw new TInvalidDataValueException('prado_using_invalid',$namespace); + } + else // a file + { + if(is_file($path)) + { + self::$_usings[$namespace]=$path; + if(!class_exists($className,false)) + { + try + { + include_once($path); + } + catch(Exception $e) + { + if(!class_exists($className,false)) + throw new TInvalidOperationException('prado_component_unknown',$className); + else + throw $e; + } + } + } + else + throw new TInvalidDataValueException('prado_using_invalid',$namespace); + } + } + else + throw new TInvalidDataValueException('prado_using_invalid',$namespace); + } + + /** + * Translates a namespace into a file path. + * The first segment of the namespace is considered as a path alias + * which is replaced with the actual path. The rest segments are + * subdirectory names appended to the aliased path. + * If the namespace ends with an asterisk '*', it represents a directory; + * Otherwise it represents a file whose extension name is specified by the second parameter (defaults to empty). + * Note, this method does not ensure the existence of the resulting file path. + * @param string namespace + * @param string extension to be appended if the namespace refers to a file + * @return string file path corresponding to the namespace, null if namespace is invalid + */ + public static function getPathOfNamespace($namespace,$ext='') + { + if(isset(self::$_usings[$namespace])) + return self::$_usings[$namespace]; + else if(isset(self::$_aliases[$namespace])) + return self::$_aliases[$namespace]; + else + { + $segs=explode('.',$namespace); + $alias=array_shift($segs); + if(($file=array_pop($segs))!==null && ($root=self::getPathOfAlias($alias))!==null) + return rtrim($root.'/'.implode('/',$segs),'/').(($file==='*')?'':'/'.$file.$ext); + else + return null; + } + } + + /** + * @param string alias to the path + * @return string the path corresponding to the alias, null if alias not defined. + */ + public static function getPathOfAlias($alias) + { + return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null; + } + + /** + * @param string alias to the path + * @param string the path corresponding to the alias + * @throws TInvalidOperationException if the alias is already defined + * @throws TInvalidDataValueException if the path is not a valid file path + */ + public static function setPathOfAlias($alias,$path) + { + if(isset(self::$_aliases[$alias])) + throw new TInvalidOperationException('prado_alias_redefined',$alias); + else if(($rp=realpath($path))!==false && is_dir($rp)) + { + if(strpos($alias,'.')===false) + self::$_aliases[$alias]=$rp; + else + throw new TInvalidDataValueException('prado_aliasname_invalid',$alias); + } + else + throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path); + } + + /** + * Fatal error handler. + * This method displays an error message together with the current call stack. + * The application will exit after calling this method. + * @param string error message + */ + public static function fatalError($msg) + { + echo '

Fatal Error

'; + echo '

'.$msg.'

'; + if(!function_exists('debug_backtrace')) + return; + echo '

Debug Backtrace

'; + echo '
';
+		$index=-1;
+		foreach(debug_backtrace() as $t)
+		{
+			$index++;
+			if($index==0)  // hide the backtrace of this function
+				continue;
+			echo '#'.$index.' ';
+			if(isset($t['file']))
+				echo basename($t['file']) . ':' . $t['line'];
+			else
+			   echo '';
+			echo ' -- ';
+			if(isset($t['class']))
+				echo $t['class'] . $t['type'];
+			echo $t['function'];
+			if(isset($t['args']) && sizeof($t['args']) > 0)
+				echo '(...)';
+			else
+				echo '()';
+			echo "\n";
+		}
+		echo '
'; + exit(1); + } + + /** + * Returns a list of user preferred languages. + * The languages are returned as an array. Each array element + * represents a single language preference. The languages are ordered + * according to user preferences. The first language is the most preferred. + * @return array list of user preferred languages. + */ + public static function getUserLanguages() + { + static $languages=null; + if($languages===null) + { + if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) + $languages[0]='en'; + else + { + $languages=array(); + foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language) + { + $array=split(';q=',trim($language)); + $languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0; + } + arsort($languages); + $languages=array_keys($languages); + if(empty($languages)) + $languages[0]='en'; + } + } + return $languages; + } + + /** + * Returns the most preferred language by the client user. + * @return string the most preferred language by the client user, defaults to English. + */ + public static function getPreferredLanguage() + { + static $language=null; + if($language===null) + { + $langs=Prado::getUserLanguages(); + $lang=explode('-',$langs[0]); + if(empty($lang[0]) || !ctype_alpha($lang[0])) + $language='en'; + else + $language=$lang[0]; + } + return $language; + } + + /** + * Writes a log message. + * This method wraps {@link log()} by checking the application mode. + * When the application is in Debug mode, debug backtrace information is appended + * to the message and the message is logged at DEBUG level. + * When the application is in Performance mode, this method does nothing. + * Otherwise, the message is logged at INFO level. + * @param string message to be logged + * @param string category of the message + * @see log, getLogger + */ + public static function trace($msg,$category='Uncategorized') + { + if(self::$_application && self::$_application->getMode()===TApplication::STATE_PERFORMANCE) + return; + if(!self::$_application || self::$_application->getMode()===TApplication::STATE_DEBUG) + { + $trace=debug_backtrace(); + if(isset($trace[0]['file']) && isset($trace[0]['line'])) + $msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})"; + $level=TLogger::DEBUG; + } + else + $level=TLogger::INFO; + self::log($msg,$level,$category); + } + + /** + * Logs a message. + * Messages logged by this method may be retrieved via {@link TLogger::getLogs} + * and may be recorded in different media, such as file, email, database, using + * {@link TLogRouter}. + * @param string message to be logged + * @param integer level of the message. Valid values include + * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, + * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. + * @param string category of the message + */ + public static function log($msg,$level=TLogger::INFO,$category='Uncategorized') + { + if(self::$_logger===null) + self::$_logger=new TLogger; + self::$_logger->log($msg,$level,$category); + } + + /** + * @return TLogger message logger + */ + public static function getLogger() + { + if(self::$_logger===null) + self::$_logger=new TLogger; + return self::$_logger; + } + + /** + * Converts a variable into a string representation. + * This method achieves the similar functionality as var_dump and print_r + * but is more robust when handling complex objects such as PRADO controls. + * @param mixed variable to be dumped + * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. + * @return string the string representation of the variable + */ + public static function varDump($var,$depth=10) + { + require_once(PRADO_DIR.'/Util/TVarDumper.php'); + return TVarDumper::dump($var,$depth); + } +} + + +if(version_compare(phpversion(),'5.1.0','>=')) +{ + /** + * TReflectionClass class. + * This class is written to cope with the incompatibility between different PHP versions. + * It is equivalent to ReflectionClass if PHP version >= 5.1.0 + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ + class TReflectionClass extends ReflectionClass + { + } +} +else // PHP < 5.1.0 +{ + /** + * TReflectionClass class. + * This class is written to cope with the incompatibility between different PHP versions. + * It mainly provides a way to detect if a method exists for a given class name. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ + class TReflectionClass extends ReflectionClass + { + /** + * @param string method name + * @return boolean whether the method exists + */ + public function hasMethod($method) + { + try + { + return $this->getMethod($method)!==null; + } + catch(Exception $e) + { + return false; + } + } + + /** + * @param string property name + * @return boolean whether the property exists + */ + public function hasProperty($property) + { + try + { + return $this->getProperty($property)!==null; + } + catch(Exception $e) + { + return false; + } + } + } + + if(!function_exists('property_exists')) + { + /** + * Detects whether an object contains the specified member variable. + * @param object + * @param string member variable (property) name + * @return boolean + */ + function property_exists($object, $property) + { + if(is_object($object)) + return array_key_exists($property, get_object_vars($object)); + else + return false; + } + } +} + +?> \ No newline at end of file diff --git a/framework/TApplication.php b/framework/TApplication.php index b94a4e53..9d8db867 100644 --- a/framework/TApplication.php +++ b/framework/TApplication.php @@ -11,36 +11,30 @@ */ /** - * Includes TErrorHandler class + * Includes core interfaces essential for TApplication class */ -require_once(PRADO_DIR.'/Exceptions/TErrorHandler.php'); +require_once(PRADO_DIR.'/interfaces.php'); + /** - * Includes THttpRequest class + * Includes core classes essential for TApplication class */ +require_once(PRADO_DIR.'/TApplicationComponent.php'); +require_once(PRADO_DIR.'/TModule.php'); +require_once(PRADO_DIR.'/TService.php'); +require_once(PRADO_DIR.'/Exceptions/TErrorHandler.php'); +require_once(PRADO_DIR.'/Caching/TCache.php'); +require_once(PRADO_DIR.'/IO/TTextWriter.php'); +require_once(PRADO_DIR.'/Collections/TList.php'); +require_once(PRADO_DIR.'/Collections/TMap.php'); +require_once(PRADO_DIR.'/Xml/TXmlDocument.php'); +require_once(PRADO_DIR.'/Security/TAuthorizationRule.php'); +require_once(PRADO_DIR.'/Security/TSecurityManager.php'); +require_once(PRADO_DIR.'/Web/THttpUtility.php'); +require_once(PRADO_DIR.'/Web/Javascripts/TJavaScript.php'); require_once(PRADO_DIR.'/Web/THttpRequest.php'); -/** - * Includes THttpResponse class - */ require_once(PRADO_DIR.'/Web/THttpResponse.php'); -/** - * Includes THttpSession class - */ require_once(PRADO_DIR.'/Web/THttpSession.php'); -/** - * Includes TAuthorizationRule class - */ -require_once(PRADO_DIR.'/Security/TAuthorizationRule.php'); -/** - * Includes TSecurityManager class - */ -require_once(PRADO_DIR.'/Security/TSecurityManager.php'); -/** - * Includes TPageService class (default service) - */ require_once(PRADO_DIR.'/Web/Services/TPageService.php'); -/** - * Includes TAssetManager class - */ require_once(PRADO_DIR.'/Web/TAssetManager.php'); diff --git a/framework/TModule.php b/framework/TModule.php new file mode 100644 index 00000000..ad68ca9f --- /dev/null +++ b/framework/TModule.php @@ -0,0 +1,57 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System + */ + +/** + * TModule class. + * + * TModule implements the basic methods required by IModule and may be + * used as the basic class for application modules. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +abstract class TModule extends TApplicationComponent implements IModule +{ + /** + * @var string module id + */ + private $_id; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + } + + /** + * @return string id of this module + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of this module + */ + public function setID($value) + { + $this->_id=$value; + } +} + +?> \ No newline at end of file diff --git a/framework/TService.php b/framework/TService.php new file mode 100644 index 00000000..f4a6244d --- /dev/null +++ b/framework/TService.php @@ -0,0 +1,64 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System + */ + +/** + * TService class. + * + * TService implements the basic methods required by IService and may be + * used as the basic class for application services. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +abstract class TService extends TApplicationComponent implements IService +{ + /** + * @var string service id + */ + private $_id; + + /** + * Initializes the service and attaches {@link run} to the RunService event of application. + * This method is required by IService and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + } + + /** + * @return string id of this service + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of this service + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * Runs the service. + */ + public function run() + { + } +} + +?> \ No newline at end of file diff --git a/framework/Util/TDataFieldAccessor.php b/framework/Util/TDataFieldAccessor.php new file mode 100644 index 00000000..09512a28 --- /dev/null +++ b/framework/Util/TDataFieldAccessor.php @@ -0,0 +1,117 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Util + */ + +/** + * TDataFieldAccessor class + * + * TDataFieldAccessor is a utility class that provides access to a field of some data. + * The accessor attempts to obtain the field value in the following order: + * - If the data is an array, then the field is treated as an array index + * and the corresponding element value is returned; + * - If the data is a TMap or TList object, then the field is treated as a key + * into the map or list, and the corresponding value is returned. + * - If the data is an object, the field is treated as a property or subproperty + * defined with getter methods. For example, if the object has a method called + * getMyValue(), then field 'MyValue' will retrive the result of this method call. + * If getMyValue() returns an object which contains a method getMySubValue(), + * then field 'MyValue.MySubValue' will return that method call result. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +class TDataFieldAccessor +{ + /** + * Evaluates the data value at the specified field. + * - If the data is an array, then the field is treated as an array index + * and the corresponding element value is returned; + * - If the data is a TMap or TList object, then the field is treated as a key + * into the map or list, and the corresponding value is returned. + * - If the data is an object, the field is treated as a property or subproperty + * defined with getter methods. For example, if the object has a method called + * getMyValue(), then field 'MyValue' will retrive the result of this method call. + * If getMyValue() returns an object which contains a method getMySubValue(), + * then field 'MyValue.MySubValue' will return that method call result. + * @param mixed data containing the field value, can be an array, TMap, TList or object. + * @param mixed field value + * @return mixed value at the specified field + * @throw TInvalidDataValueException if field or data is invalid + */ + public static function getDataFieldValue($data,$field) + { + if(Prado::getApplication()->getMode()===TApplication::STATE_PERFORMANCE) + { + if(is_array($data) || ($data instanceof ArrayAccess)) + return $data[$field]; + else if(is_object($data)) + { + if(strpos($field,'.')===false) // simple field + { + if(property_exists($data,$field)) + return $data->{$field}; + else + return call_user_func(array($data,'get'.$field)); + } + else // field in the format of xxx.yyy.zzz + { + $object=$data; + foreach(explode('.',$field) as $f) + $object=call_user_func(array($object,'get'.$f)); + return $object; + } + } + else + throw new TInvalidDataValueException('datafieldaccessor_data_invalid',$field); + } + else + { + if(is_array($data) || ($data instanceof ArrayAccess)) + { + if(isset($data[$field]) || $data[$field]===null) + return $data[$field]; + else + throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field); + } + else if(is_object($data)) + { + if(strpos($field,'.')===false) // simple field + { + if(property_exists($data,$field)) + return $data->{$field}; + else if(is_callable(array($data,'get'.$field))) + return call_user_func(array($data,'get'.$field)); + else + throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field); + } + else // field in the format of xxx.yyy.zzz + { + $object=$data; + foreach(explode('.',$field) as $f) + { + $getter='get'.$f; + if(is_callable(array($object,$getter))) + $object=call_user_func(array($object,$getter)); + else + throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field); + } + return $object; + } + } + else + throw new TInvalidDataValueException('datafieldaccessor_data_invalid',$field); + } + } +} + +?> \ No newline at end of file diff --git a/framework/Util/TLogRouter.php b/framework/Util/TLogRouter.php new file mode 100644 index 00000000..bdad8649 --- /dev/null +++ b/framework/Util/TLogRouter.php @@ -0,0 +1,666 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Util + */ + +/** + * TLogRouter class. + * + * TLogRouter manages routes that record log messages in different media different ways. + * For example, a file log route {@link TFileLogRoute} records log messages + * in log files. An email log route {@link TEmailLogRoute} sends log messages + * to email addresses. + * + * Log routes may be configured in application or page folder configuration files + * or an external configuration file specified by {@link setConfigFile ConfigFile}. + * The format is as follows, + * + * <route class="TFileLogRoute" Categories="System.Web.UI" Levels="Warning" /> + * <route class="TEmailLogRoute" Categories="Application" Levels="Fatal" Emails="admin@pradosoft.com" /> + * + * You can specify multiple routes with different filtering conditions and different + * targets, even if the routes are of the same type. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +class TLogRouter extends TModule +{ + /** + * File extension of external configuration file + */ + const CONFIG_FILE_EXT='.xml'; + /** + * @var array list of routes available + */ + private $_routes=array(); + /** + * @var string external configuration file + */ + private $_configFile=null; + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. + */ + public function init($config) + { + if($this->_configFile!==null) + { + if(is_file($this->_configFile)) + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_configFile); + $this->loadConfig($dom); + } + else + throw new TConfigurationException('logrouter_configfile_invalid',$this->_configFile); + } + $this->loadConfig($config); + $this->getApplication()->attachEventHandler('OnEndRequest',array($this,'collectLogs')); + } + + /** + * Loads configuration from an XML element + * @param TXmlElement configuration node + * @throws TConfigurationException if log route class or type is not specified + */ + private function loadConfig($xml) + { + foreach($xml->getElementsByTagName('route') as $routeConfig) + { + $properties=$routeConfig->getAttributes(); + if(($class=$properties->remove('class'))===null) + throw new TConfigurationException('logrouter_routeclass_required'); + $route=Prado::createComponent($class); + if(!($route instanceof TLogRoute)) + throw new TConfigurationException('logrouter_routetype_invalid'); + foreach($properties as $name=>$value) + $route->setSubproperty($name,$value); + $this->_routes[]=$route; + $route->init($routeConfig); + } + } + + /** + * @return string external configuration file. Defaults to null. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * must be suffixed with '.xml'. + * @throws TInvalidDataValueException if the file is invalid. + */ + public function setConfigFile($value) + { + if(($this->_configFile=Prado::getPathOfNamespace($value,self::LOG_FILE_EXT))===null) + throw new TConfigurationException('logrouter_configfile_invalid',$value); + } + + /** + * Collects log messages from a logger. + * This method is an event handler to application's EndRequest event. + * @param mixed event parameter + */ + public function collectLogs($param) + { + $logger=Prado::getLogger(); + foreach($this->_routes as $route) + $route->collectLogs($logger); + } +} + +/** + * TLogRoute class. + * + * TLogRoute is the base class for all log route classes. + * A log route object retrieves log messages from a logger and sends it + * somewhere, such as files, emails. + * The messages being retrieved may be filtered first before being sent + * to the destination. The filters include log level filter and log category filter. + * + * To specify level filter, set {@link setLevels Levels} property, + * which takes a string of comma-separated desired level names (e.g. 'Error, Debug'). + * To specify category filter, set {@link setCategories Categories} property, + * which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO'). + * + * Level filter and category filter are combinational, i.e., only messages + * satisfying both filter conditions will they be returned. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +abstract class TLogRoute extends TApplicationComponent +{ + /** + * @var array lookup table for level names + */ + protected static $_levelNames=array( + TLogger::DEBUG=>'Debug', + TLogger::INFO=>'Info', + TLogger::NOTICE=>'Notice', + TLogger::WARNING=>'Warning', + TLogger::ERROR=>'Error', + TLogger::ALERT=>'Alert', + TLogger::FATAL=>'Fatal' + ); + /** + * @var array lookup table for level values + */ + protected static $_levelValues=array( + 'debug'=>TLogger::DEBUG, + 'info'=>TLogger::INFO, + 'notice'=>TLogger::NOTICE, + 'warning'=>TLogger::WARNING, + 'error'=>TLogger::ERROR, + 'alert'=>TLogger::ALERT, + 'fatal'=>TLogger::FATAL + ); + /** + * @var integer log level filter (bits) + */ + private $_levels=null; + /** + * @var array log category filter + */ + private $_categories=null; + + /** + * Initializes the route. + * @param TXmlElement configurations specified in {@link TLogRouter}. + */ + public function init($config) + { + } + + /** + * @return integer log level filter + */ + public function getLevels() + { + return $this->_levels; + } + + /** + * @param integer|string integer log level filter (in bits). If the value is + * a string, it is assumed to be comma-separated level names. Valid level names + * include 'Debug', 'Info', 'Notice', 'Warning', 'Error', 'Alert' and 'Fatal'. + */ + public function setLevels($levels) + { + if(is_integer($levels)) + $this->_levels=$levels; + else + { + $this->_levels=null; + $levels=strtolower($levels); + foreach(explode(',',$levels) as $level) + { + $level=trim($level); + if(isset(self::$_levelValues[$level])) + $this->_levels|=self::$_levelValues[$level]; + } + } + } + + /** + * @return array list of categories to be looked for + */ + public function getCategories() + { + return $this->_categories; + } + + /** + * @param array|string list of categories to be looked for. If the value is a string, + * it is assumed to be comma-separated category names. + */ + public function setCategories($categories) + { + if(is_array($categories)) + $this->_categories=$categories; + else + { + $this->_categories=null; + foreach(explode(',',$categories) as $category) + { + if(($category=trim($category))!=='') + $this->_categories[]=$category; + } + } + } + + /** + * @param integer level value + * @return string level name + */ + protected function getLevelName($level) + { + return isset(self::$_levelNames[$level])?self::$_levelNames[$level]:'Unknown'; + } + + /** + * @param string level name + * @return integer level value + */ + protected function getLevelValue($level) + { + return isset(self::$_levelValues[$level])?self::$_levelValues[$level]:0; + } + + /** + * Formats a log message given different fields. + * @param string message content + * @param integer message level + * @param string message category + * @param integer timestamp + * @return string formatted message + */ + protected function formatLogMessage($message,$level,$category,$time) + { + return @date('M d H:i:s',$time).' ['.$this->getLevelName($level).'] ['.$category.'] '.$message."\n"; + } + + /** + * Retrieves log messages from logger to log route specific destination. + * @param TLogger logger instance + */ + public function collectLogs(TLogger $logger) + { + $logs=$logger->getLogs($this->getLevels(),$this->getCategories()); + if(!empty($logs)) + $this->processLogs($logs); + } + + /** + * Processes log messages and sends them to specific destination. + * Derived child classes must implement this method. + * @param array list of messages. Each array element is a (formatted) message string. + */ + abstract protected function processLogs($logs); +} + +/** + * TFileLogRoute class. + * + * TFileLogRoute records log messages in files. + * The log files are stored under {@link setLogPath LogPath} and the file name + * is specified by {@link setLogFile LogFile}. If the size of the log file is + * greater than {@link setMaxFileSize MaxFileSize} (in kilo-bytes), a rotation + * is performed, which renames the current log file by suffixing the file name + * with '.1'. All existing log files are moved backwards one place, i.e., '.2' + * to '.3', '.1' to '.2'. The property {@link setMaxLogFiles MaxLogFiles} + * specifies how many files to be kept. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +class TFileLogRoute extends TLogRoute +{ + /** + * @var integer maximum log file size + */ + private $_maxFileSize=512; // in KB + /** + * @var integer number of log files used for rotation + */ + private $_maxLogFiles=2; + /** + * @var string directory storing log files + */ + private $_logPath=null; + /** + * @var string log file name + */ + private $_logFile='prado.log'; + + /** + * @return string directory storing log files. Defaults to application runtime path. + */ + public function getLogPath() + { + if($this->_logPath===null) + $this->_logPath=$this->getApplication()->getRuntimePath(); + return $this->_logPath; + } + + /** + * @param string directory (in namespace format) storing log files. + * @throws TConfigurationException if log path is invalid + */ + public function setLogPath($value) + { + if(($this->_logPath=Prado::getPathOfNamespace($value))===null || !is_dir($this->_logPath) || !is_writable($this->_logPath)) + throw new TConfigurationException('filelogroute_logpath_invalid',$value); + } + + /** + * @return string log file name. Defaults to 'prado.log'. + */ + public function getLogFile() + { + return $this->_logFile; + } + + /** + * @param string log file name + */ + public function setLogFile($value) + { + $this->_logFile=$value; + } + + /** + * @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB). + */ + public function getMaxFileSize() + { + return $this->_maxFileSize; + } + + /** + * @param integer maximum log file size in kilo-bytes (KB). + * @throws TInvalidDataValueException if the value is smaller than 1. + */ + public function setMaxFileSize($value) + { + $this->_maxFileSize=TPropertyValue::ensureInteger($value); + if($this->_maxFileSize<=0) + throw new TInvalidDataValueException('filelogroute_maxfilesize_invalid'); + } + + /** + * @return integer number of files used for rotation. Defaults to 2. + */ + public function getMaxLogFiles() + { + return $this->_maxLogFiles; + } + + /** + * @param integer number of files used for rotation. + */ + public function setMaxLogFiles($value) + { + $this->_maxLogFiles=TPropertyValue::ensureInteger($value); + if($this->_maxLogFiles<1) + throw new TInvalidDataValueException('filelogroute_maxlogfiles_invalid'); + } + + /** + * Saves log messages in files. + * @param array list of log messages + */ + protected function processLogs($logs) + { + $logFile=$this->getLogPath().'/'.$this->getLogFile(); + if(@filesize($logFile)>$this->_maxFileSize*1024) + $this->rotateFiles(); + foreach($logs as $log) + error_log($this->formatLogMessage($log[0],$log[1],$log[2],$log[3]),3,$logFile); + } + + /** + * Rotates log files. + */ + protected function rotateFiles() + { + $file=$this->getLogPath().'/'.$this->getLogFile(); + for($i=$this->_maxLogFiles;$i>0;--$i) + { + $rotateFile=$file.'.'.$i; + if(is_file($rotateFile)) + { + if($i===$this->_maxLogFiles) + unlink($rotateFile); + else + rename($rotateFile,$file.'.'.($i+1)); + } + } + if(is_file($file)) + rename($file,$file.'.1'); + } +} + +/** + * TEmailLogRoute class. + * + * TEmailLogRoute sends selected log messages to email addresses. + * The target email addresses may be specified via {@link setEmails Emails} property. + * Optionally, you may set the email {@link setSubject Subject} and the + * {@link setSentFrom SentFrom} address. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +class TEmailLogRoute extends TLogRoute +{ + /** + * Regex pattern for email address. + */ + const EMAIL_PATTERN='/^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/'; + /** + * Default email subject. + */ + const DEFAULT_SUBJECT='Prado Application Log'; + /** + * @var array list of destination email addresses. + */ + private $_emails=array(); + /** + * @var string email subject + */ + private $_subject=''; + /** + * @var string email sent from address + */ + private $_from=''; + + /** + * Initializes the route. + * @param TXmlElement configurations specified in {@link TLogRouter}. + * @throws TConfigurationException if {@link getSentFrom SentFrom} is empty and + * 'sendmail_from' in php.ini is also empty. + */ + public function init($config) + { + if($this->_from==='') + $this->_from=ini_get('sendmail_from'); + if($this->_from==='') + throw new TConfigurationException('emaillogroute_sentfrom_required'); + } + + /** + * Sends log messages to specified email addresses. + * @param array list of log messages + */ + protected function processLogs($logs) + { + $message=''; + foreach($logs as $log) + $message.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]); + $message=wordwrap($message,70); + foreach($this->_emails as $email) + mail($email,$this->getSubject(),$message,"From:{$this->_from}\r\n"); + + } + + /** + * @return array list of destination email addresses + */ + public function getEmails() + { + return $this->_emails; + } + + /** + * @return array|string list of destination email addresses. If the value is + * a string, it is assumed to be comma-separated email addresses. + */ + public function setEmails($emails) + { + if(is_array($emails)) + $this->_emails=$emails; + else + { + $this->_emails=array(); + foreach(explode(',',$emails) as $email) + { + $email=trim($email); + if(preg_match(self::EMAIL_PATTERN,$email)) + $this->_emails[]=$email; + } + } + } + + /** + * @return string email subject. Defaults to TEmailLogRoute::DEFAULT_SUBJECT + */ + public function getSubject() + { + if($this->_subject===null) + $this->_subject=self::DEFAULT_SUBJECT; + return $this->_subject; + } + + /** + * @param string email subject. + */ + public function setSubject($value) + { + $this->_subject=$value; + } + + /** + * @return string send from address of the email + */ + public function getSentFrom() + { + return $this->_from; + } + + /** + * @param string send from address of the email + */ + public function setSentFrom($value) + { + $this->_from=$value; + } +} + +/** + * TBrowserLogRoute class. + * + * TBrowserLogRoute prints selected log messages in the response. + * + * @author Xiang Wei Zhuo + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +class TBrowserLogRoute extends TLogRoute +{ + public function processLogs($logs) + { + if(empty($logs) || $this->getApplication()->getMode()==='Performance') return; + $first = $logs[0][3]; + $prev = $first; + $even = true; + $response = $this->getApplication()->getResponse(); + $response->write($this->renderHeader()); + foreach($logs as $log) + { + $timing['total'] = $log[3] - $first; + $timing['delta'] = $log[3] - $prev; + $timing['even'] = !($even = !$even); + $prev=$log[3]; + $response->write($this->renderMessage($log,$timing)); + } + $response->write($this->renderFooter()); + } + + protected function renderHeader() + { + $string = << + + + Application Log + + +   + CategoryMessageTime Spent (s)Cumulated Time Spent (s) + +EOD; + return $string; + } + + protected function renderMessage($log, $info) + { + $bgcolor = $info['even'] ? "#fff" : "#eee"; + $total = sprintf('%0.6f', $info['total']); + $delta = sprintf('%0.6f', $info['delta']); + $color = $this->getColorLevel($log[1]); + $msg = preg_replace('/\(line[^\)]+\)$/','',$log[0]); //remove line number info + $msg = THttpUtility::htmlEncode($msg); + $string = << +   + {$log[2]} + {$msg} + {$delta} + {$total} + +EOD; + return $string; + } + + protected function getColorLevel($level) + { + switch($level) + { + case TLogger::DEBUG: return 'green'; + case TLogger::INFO: return 'black'; + case TLogger::NOTICE: return '#3333FF'; + case TLogger::WARNING: return '#33FFFF'; + case TLogger::ERROR: return '#ff9933'; + case TLogger::ALERT: return '#ff00ff'; + case TLogger::FATAL: return 'red'; + } + return ''; + } + + protected function renderFooter() + { + $string = ""; + foreach(self::$_levelValues as $name => $level) + { + $string .= "getColorLevel($level); + $string .= ";margin: 0.5em;\">".strtoupper($name).""; + } + $string .= ""; + return $string; + } +} +?> \ No newline at end of file diff --git a/framework/Util/TLogger.php b/framework/Util/TLogger.php new file mode 100644 index 00000000..632c46f1 --- /dev/null +++ b/framework/Util/TLogger.php @@ -0,0 +1,130 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Util + */ + +/** + * TLogger class. + * + * TLogger records log messages in memory and implements the methods to + * retrieve the messages with filter conditions, including log levels and + * log categories. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +class TLogger extends TComponent +{ + /** + * Log levels. + */ + const DEBUG=0x01; + const INFO=0x02; + const NOTICE=0x04; + const WARNING=0x08; + const ERROR=0x10; + const ALERT=0x20; + const FATAL=0x40; + /** + * @var array log messages + */ + private $_logs=array(); + /** + * @var integer log levels (bits) to be filtered + */ + private $_levels; + /** + * @var array list of categories to be filtered + */ + private $_categories; + + /** + * Logs a message. + * Messages logged by this method may be retrieved via {@link getLogs}. + * @param string message to be logged + * @param integer level of the message. Valid values include + * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, + * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. + * @param string category of the message + */ + public function log($message,$level,$category='Uncategorized') + { + $this->_logs[]=array($message,$level,$category,microtime(true)); + } + + /** + * Retrieves log messages. + * Messages may be filtered by log levels and/or categories. + * A level filter is specified by an integer, whose bits indicate the levels interested. + * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels. + * A category filter is specified by concatenating interested category names + * with commas. A message whose category name starts with any filtering category + * will be returned. For example, a category filter 'System.Web, System.IO' + * will return messages under categories such as 'System.Web', 'System.IO', + * 'System.Web.UI', 'System.Web.UI.WebControls', etc. + * Level filter and category filter are combinational, i.e., only messages + * satisfying both filter conditions will they be returned. + * @param integer level filter + * @param string category filter + * @param array list of messages. Each array elements represents one message + * with the following structure: + * array( + * [0] => message + * [1] => level + * [2] => category + * [3] => timestamp); + */ + public function getLogs($levels=null,$categories=null) + { + $this->_levels=$levels; + $this->_categories=$categories; + if(empty($levels) && empty($categories)) + return $this->_logs; + else if(empty($levels)) + return array_values(array_filter(array_filter($this->_logs,array($this,'filterByCategories')))); + else if(empty($categories)) + return array_values(array_filter(array_filter($this->_logs,array($this,'filterByLevels')))); + else + { + $ret=array_values(array_filter(array_filter($this->_logs,array($this,'filterByLevels')))); + return array_values(array_filter(array_filter($ret,array($this,'filterByCategories')))); + } + } + + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + */ + private function filterByCategories($value) + { + foreach($this->_categories as $category) + { + if($value[2]===$category || strpos($value[2],$category.'.')===0) + return $value; + } + return false; + } + + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + */ + private function filterByLevels($value) + { + if($value[1] & $this->_levels) + return $value; + else + return false; + } +} + +?> \ No newline at end of file diff --git a/framework/Util/TSimpleDateFormatter.php b/framework/Util/TSimpleDateFormatter.php new file mode 100644 index 00000000..ce92a272 --- /dev/null +++ b/framework/Util/TSimpleDateFormatter.php @@ -0,0 +1,354 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Util + */ + +/** + * TSimpleDateFormatter class. + * + * Formats and parses dates using the SimpleDateFormat pattern. + * This pattern is compatible with the I18N and java's SimpleDateFormatter. + * + * Pattern | Description + * ---------------------------------------------------- + * d | Day of month 1 to 31, no padding + * dd | Day of monath 01 to 31, zero leading + * M | Month digit 1 to 12, no padding + * MM | Month digit 01 to 12, zero leading + * yy | 2 year digit, e.g., 96, 05 + * yyyy | 4 year digit, e.g., 2005 + * ---------------------------------------------------- + * + * + * Usage example, to format a date + * + * $formatter = new TSimpleDateFormatter("dd/MM/yyy"); + * echo $formatter->format(time()); + * + * + * To parse the date string into a date timestamp. + * + * $formatter = new TSimpleDateFormatter("d-M-yyy"); + * echo $formatter->parse("24-6-2005"); + * + * + * @author Wei Zhuo + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +class TSimpleDateFormatter +{ + /** + * Formatting pattern. + * @var string + */ + private $pattern; + + /** + * Charset, default is 'UTF-8' + * @var string + */ + private $charset = 'UTF-8'; + + /** + * Constructor, create a new date time formatter. + * @param string formatting pattern. + * @param string pattern and value charset + */ + public function __construct($pattern, $charset='UTF-8') + { + $this->setPattern($pattern); + $this->setCharset($charset); + } + + /** + * @return string formatting pattern. + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * @param string formatting pattern. + */ + public function setPattern($pattern) + { + $this->pattern = $pattern; + } + + /** + * @return string formatting charset. + */ + public function getCharset() + { + return $this->charset; + } + + /** + * @param string formatting charset. + */ + public function setCharset($charset) + { + $this->charset = $charset; + } + + /** + * Format the date according to the pattern. + * @param string|int the date to format, either integer or a string readable by strtotime. + * @return string formatted date. + */ + public function format($value) + { + $date = $this->getDate($value); + $bits['yyyy'] = $date['year']; + $bits['yy'] = substr("{$date['year']}", -2); + + $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT); + $bits['M'] = $date['mon']; + + $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT); + $bits['d'] = $date['mday']; + + return str_replace(array_keys($bits), $bits, $this->pattern); + } + + public function getMonthPattern() + { + if(is_int(strpos($this->pattern, 'MMMM'))) + return 'MMMM'; + if(is_int(strpos($this->pattern, 'MMM'))) + return 'MMM'; + if(is_int(strpos($this->pattern, 'MM'))) + return 'MM'; + if(is_int(strpos($this->pattern, 'M'))) + return 'M'; + return false; + } + + public function getDayPattern() + { + if(is_int(strpos($this->pattern, 'dd'))) + return 'dd'; + if(is_int(strpos($this->pattern, 'd'))) + return 'd'; + return false; + } + + public function getYearPattern() + { + if(is_int(strpos($this->pattern, 'yyyy'))) + return 'yyyy'; + if(is_int(strpos($this->pattern, 'yy'))) + return 'yy'; + return false; + } + + public function getDayMonthYearOrdering() + { + $ordering = array(); + if(is_int($day= strpos($this->pattern, 'd'))) + $ordering['day'] = $day; + if(is_int($month= strpos($this->pattern, 'M'))) + $ordering['month'] = $month; + if(is_int($year= strpos($this->pattern, 'yy'))) + $ordering['year'] = $year; + asort($ordering); + return array_keys($ordering); + } + + /** + * Gets the time stamp from string or integer. + * @param string|int date to parse + * @return int parsed date time stamp + */ + private function getDate($value) + { + if(is_int($value)) + return @getdate($value); + $date = @strtotime($value); + if($date < 0) + throw new TInvalidDataValueException('invalid_date', $value); + return @getdate($date); + } + + /** + * @return boolean true if the given value matches with the date pattern. + */ + public function isValidDate($value) + { + return !is_null($this->parse($value, false)); + } + + /** + * Parse the string according to the pattern. + * @param string date string to parse + * @return int date time stamp + * @throws TInvalidDataValueException if date string is malformed. + */ + public function parse($value,$defaultToCurrentTime=true) + { + if(!is_string($value)) + throw new TInvalidDataValueException('date_to_parse_must_be_string', $value); + + if(empty($this->pattern)) return time(); + + $date = $this->getDate(time()); + + if($this->length(trim($value)) < 1) + return $defaultToCurrentTime ? $date : null; + + $pattern = $this->pattern; + + $i_val = 0; + $i_format = 0; + $pattern_length = $this->length($pattern); + $c = ''; + $token=''; + $x=null; $y=null; + + + if($defaultToCurrentTime) + { + $year = "{$date['year']}"; + $month = $date['mon']; + $day = $date['mday']; + } + else + { + $year = null; + $month = null; + $day = null; + } + + while ($i_format < $pattern_length) + { + $c = $this->charAt($pattern,$i_format); + $token=''; + while ($this->charEqual($pattern, $i_format, $c) + && ($i_format < $pattern_length)) + { + $token .= $this->charAt($pattern, $i_format++); + } + + if ($token=='yyyy' || $token=='yy' || $token=='y') + { + if ($token=='yyyy') { $x=4;$y=4; } + if ($token=='yy') { $x=2;$y=2; } + if ($token=='y') { $x=2;$y=4; } + $year = $this->getInteger($value,$i_val,$x,$y); + if(is_null($year)) + throw new TInvalidDataValueException('Invalid year', $value); + $i_val += strlen($year); + if(strlen($year) == 2) + { + $iYear = intval($year); + if($iYear > 70) + $year = $iYear + 1900; + else + $year = $iYear + 2000; + } + $year = intval($year); + } + elseif($token=='MM' || $token=='M') + { + $month=$this->getInteger($value,$i_val, + $this->length($token),2); + $iMonth = intval($month); + if(is_null($month) || $iMonth < 1 || $iMonth > 12 ) + throw new TInvalidDataValueException('Invalid month', $value); + $i_val += strlen($month); + $month = $iMonth; + } + elseif ($token=='dd' || $token=='d') + { + $day = $this->getInteger($value,$i_val, + $this->length($token), 2); + $iDay = intval($day); + if(is_null($day) || $iDay < 1 || $iDay >31) + throw new TInvalidDataValueException('Invalid day', $value); + $i_val += strlen($day); + $day = $iDay; + } + else + { + if($this->substring($value, $i_val, $this->length($token)) != $token) + throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value); + else + $i_val += $this->length($token); + } + } + if ($i_val != $this->length($value)) + throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value); + + if(!$defaultToCurrentTime && (is_null($month) || is_null($day) || is_null($year))) + return null; + else + return $this->getDate(@mktime(0, 0, 0, $month, $day, $year)); + } + + /** + * Calculate the length of a string, may be consider iconv_strlen? + */ + private function length($string) + { + //use iconv_strlen or just strlen? + return strlen($string); + } + + /** + * Get the char at a position. + */ + private function charAt($string, $pos) + { + return $this->substring($string, $pos, 1); + } + + /** + * Gets a portion of a string, uses iconv_substr. + */ + private function substring($string, $start, $length) + { + return iconv_substr($string, $start, $length); + } + + /** + * Returns true if char at position equals a particular char. + */ + private function charEqual($string, $pos, $char) + { + return $this->charAt($string, $pos) == $char; + } + + /** + * Gets integer from part of a string, allows integers of any length. + * @param string string to retrieve the integer from. + * @param int starting position + * @param int minimum integer length + * @param int maximum integer length + * @return string integer portition of the string, null otherwise + */ + private function getInteger($str,$i,$minlength,$maxlength) + { + //match for digits backwards + for ($x = $maxlength; $x >= $minlength; $x--) + { + $token= $this->substring($str, $i,$x); + if ($this->length($token) < $minlength) + return null; + if (preg_match('/^\d+$/', $token)) + return $token; + } + return null; + } +} + +?> \ No newline at end of file diff --git a/framework/Util/TVarDumper.php b/framework/Util/TVarDumper.php new file mode 100644 index 00000000..0532a6ec --- /dev/null +++ b/framework/Util/TVarDumper.php @@ -0,0 +1,123 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Util + */ + +/** + * TVarDumper class. + * + * TVarDumper is intended to replace the buggy PHP function var_dump and print_r. + * It can correctly identify the recursively referenced objects in a complex + * object structure. It also has a recurisve depth control to avoid indefinite + * recursive display of some peculiar variables. + * + * TVarDumper can be used as follows, + * + * echo TVarDumper::dump($var); + * + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Util + * @since 3.0 + */ +class TVarDumper +{ + private static $_objects; + private static $_output; + private static $_depth; + + /** + * Converts a variable into a string representation. + * This method achieves the similar functionality as var_dump and print_r + * but is more robust when handling complex objects such as PRADO controls. + * @param mixed variable to be dumped + * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. + * @return string the string representation of the variable + */ + public static function dump($var,$depth=10) + { + self::$_output=''; + self::$_objects=array(); + self::$_depth=$depth; + self::dumpInternal($var,0); + return self::$_output; + } + + private static function dumpInternal($var,$level) + { + switch(gettype($var)) + { + case 'boolean': + self::$_output.=$var?'true':'false'; + break; + case 'integer': + self::$_output.="$var"; + break; + case 'double': + self::$_output.="$var"; + break; + case 'string': + self::$_output.="'$var'"; + break; + case 'resource': + self::$_output.='{resource}'; + break; + case 'NULL': + self::$_output.="null"; + break; + case 'unknown type': + self::$_output.='{unknown}'; + break; + case 'array': + if(self::$_depth<=$level) + self::$_output.='array(...)'; + else if(empty($var)) + self::$_output.='array()'; + else + { + $keys=array_keys($var); + $spaces=str_repeat(' ',$level*4); + self::$_output.="array\n".$spaces.'('; + foreach($keys as $key) + { + self::$_output.="\n".$spaces." [$key] => "; + self::$_output.=self::dumpInternal($var[$key],$level+1); + } + self::$_output.="\n".$spaces.')'; + } + break; + case 'object': + if(($id=array_search($var,self::$_objects,true))!==false) + self::$_output.=get_class($var).'#'.($id+1).'(...)'; + else if(self::$_depth<=$level) + self::$_output.=get_class($var).'(...)'; + else + { + $id=array_push(self::$_objects,$var); + $className=get_class($var); + $members=(array)$var; + $keys=array_keys($members); + $spaces=str_repeat(' ',$level*4); + self::$_output.="$className#$id\n".$spaces.'('; + foreach($keys as $key) + { + $keyDisplay=strtr(trim($key),array("\0"=>':')); + self::$_output.="\n".$spaces." [$keyDisplay] => "; + self::$_output.=self::dumpInternal($members[$key],$level+1); + } + self::$_output.="\n".$spaces.')'; + } + break; + } + } +} + +?> \ No newline at end of file diff --git a/framework/Web/TAssetManager.php b/framework/Web/TAssetManager.php index 267505c7..bfc5ef8b 100644 --- a/framework/Web/TAssetManager.php +++ b/framework/Web/TAssetManager.php @@ -280,7 +280,7 @@ class TAssetManager extends TModule throw new TIOException('assetmanager_tarfile_invalid',$path); else { - Prado::using('System.Data.TTarFileExtractor'); + Prado::using('System.IO.TTarFileExtractor'); $tar = new TTarFileExtractor($fullpath); return $tar->extract($destination); } diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php index dba58555..52ec9bb1 100644 --- a/framework/Web/UI/TControl.php +++ b/framework/Web/UI/TControl.php @@ -1865,6 +1865,47 @@ interface IBroadcastEventReceiver public function broadcastEventReceived($sender,$param); } +/** + * ITheme interface. + * + * This interface must be implemented by theme. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Web.UI + * @since 3.0 + */ +interface ITheme +{ + /** + * Applies this theme to the specified control. + * @param TControl the control to be applied with this theme + */ + public function applySkin($control); +} + +/** + * ITemplate interface + * + * ITemplate specifies the interface for classes encapsulating + * parsed template structures. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Web.UI + * @since 3.0 + */ +interface ITemplate +{ + /** + * Instantiates the template. + * Content in the template will be instantiated as components and text strings + * and passed to the specified parent control. + * @param TControl the parent control + */ + public function instantiateIn($parent); +} + /** * TBroadcastEventParameter class * diff --git a/framework/Web/UI/WebControls/TBaseDataList.php b/framework/Web/UI/WebControls/TBaseDataList.php index b7c79cbe..479fb2ef 100644 --- a/framework/Web/UI/WebControls/TBaseDataList.php +++ b/framework/Web/UI/WebControls/TBaseDataList.php @@ -11,9 +11,10 @@ */ /** - * Includes TDataBoundControl class + * Includes TDataBoundControl and TDataFieldAccessor classes */ Prado::using('System.Web.UI.WebControls.TDataBoundControl'); +Prado::using('System.Util.TDataFieldAccessor'); /** * TBaseDataList class diff --git a/framework/Web/UI/WebControls/TCompareValidator.php b/framework/Web/UI/WebControls/TCompareValidator.php index 811ca50a..1bf4c529 100644 --- a/framework/Web/UI/WebControls/TCompareValidator.php +++ b/framework/Web/UI/WebControls/TCompareValidator.php @@ -201,7 +201,7 @@ class TCompareValidator extends TBaseValidator $dateFormat = $this->getDateFormat(); if($dateFormat!=='') { - $formatter = Prado::createComponent('System.Data.TSimpleDateFormatter', $dateFormat); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $dateFormat); return array($formatter->parse($value1), $formatter->parse($value2)); } else diff --git a/framework/Web/UI/WebControls/TDataGridColumn.php b/framework/Web/UI/WebControls/TDataGridColumn.php index e8cc81aa..de9d9d5c 100644 --- a/framework/Web/UI/WebControls/TDataGridColumn.php +++ b/framework/Web/UI/WebControls/TDataGridColumn.php @@ -10,6 +10,11 @@ * @package System.Web.UI.WebControls */ +/** + * Includes TDataFieldAccessor class + */ +Prado::using('System.Util.TDataFieldAccessor'); + /** * TDataGridColumn class * diff --git a/framework/Web/UI/WebControls/TDataTypeValidator.php b/framework/Web/UI/WebControls/TDataTypeValidator.php index f3cf5a3c..81c23ce4 100644 --- a/framework/Web/UI/WebControls/TDataTypeValidator.php +++ b/framework/Web/UI/WebControls/TDataTypeValidator.php @@ -91,7 +91,7 @@ class TDataTypeValidator extends TBaseValidator $dateFormat = $this->getDateFormat(); if(strlen($dateFormat)) { - $formatter = Prado::createComponent('System.Data.TSimpleDateFormatter',$dateFormat); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$dateFormat); return $formatter->isValidDate($value); } else diff --git a/framework/Web/UI/WebControls/TDatePicker.php b/framework/Web/UI/WebControls/TDatePicker.php index 8d6d45e4..0d30636c 100644 --- a/framework/Web/UI/WebControls/TDatePicker.php +++ b/framework/Web/UI/WebControls/TDatePicker.php @@ -256,7 +256,7 @@ class TDatePicker extends TTextBox public function setDate($value) { $date = TPropertyValue::ensureInteger($value); - $formatter = Prado::createComponent('System.Data.TSimpleDateFormatter', + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $this->getDateFormat()); $this->setText($formatter->format($date)); } @@ -358,7 +358,7 @@ class TDatePicker extends TTextBox $date = @mktime(0, 0, 0, $month, $day, $year); $pattern = $this->getDateFormat(); $pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern); - $formatter = Prado::createComponent('System.Data.TSimpleDateFormatter', $pattern); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $pattern); return $formatter->format($date); } @@ -452,7 +452,7 @@ class TDatePicker extends TTextBox protected function hasDayPattern() { - $formatter = Prado::createComponent('System.Data.TSimpleDateFormatter', + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $this->getDateFormat()); return !is_null($formatter->getDayPattern()); } @@ -464,7 +464,7 @@ class TDatePicker extends TTextBox */ protected function renderCalendarSelections($writer, $date) { - $formatter = Prado::createComponent('System.Data.TSimpleDateFormatter', + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $this->getDateFormat()); foreach($formatter->getDayMonthYearOrdering() as $type) { @@ -485,7 +485,7 @@ class TDatePicker extends TTextBox { $pattern = $this->getDateFormat(); $pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern); - $formatter = Prado::createComponent('System.Data.TSimpleDateFormatter',$pattern); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$pattern); return $formatter->parse($this->getText()); } @@ -550,7 +550,7 @@ class TDatePicker extends TTextBox */ protected function getLocalizedMonthNames($info) { - $formatter = Prado::createComponent('System.Data.TSimpleDateFormatter', + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $this->getDateFormat()); switch($formatter->getMonthPattern()) { diff --git a/framework/Web/UI/WebControls/TListControl.php b/framework/Web/UI/WebControls/TListControl.php index 3d73556a..ccbce4d6 100644 --- a/framework/Web/UI/WebControls/TListControl.php +++ b/framework/Web/UI/WebControls/TListControl.php @@ -18,6 +18,10 @@ Prado::using('System.Web.UI.WebControls.TDataBoundControl'); * Includes TAttributeCollection class */ Prado::using('System.Collections.TAttributeCollection'); +/** + * Includes TDataFieldAccessor class + */ +Prado::using('System.Util.TDataFieldAccessor'); /** * TListControl class diff --git a/framework/Web/UI/WebControls/TRangeValidator.php b/framework/Web/UI/WebControls/TRangeValidator.php index 842797ba..7ded3e71 100644 --- a/framework/Web/UI/WebControls/TRangeValidator.php +++ b/framework/Web/UI/WebControls/TRangeValidator.php @@ -226,7 +226,7 @@ class TRangeValidator extends TBaseValidator $dateFormat = $this->getDateFormat(); if($dateFormat!=='') { - $formatter=Prado::createComponent('System.Data.TSimpleDateFormatter', $dateFormat); + $formatter=Prado::createComponent('System.Util.TSimpleDateFormatter', $dateFormat); $value = $formatter->parse($value, $dateFormat); if($minValue!=='') $valid=$valid && ($value>=$formatter->parse($minValue)); diff --git a/framework/Web/UI/WebControls/TWizard.php b/framework/Web/UI/WebControls/TWizard.php index 0b44872b..ef4691bd 100644 --- a/framework/Web/UI/WebControls/TWizard.php +++ b/framework/Web/UI/WebControls/TWizard.php @@ -16,6 +16,7 @@ Prado::using('System.Web.UI.WebControls.TButton'); Prado::using('System.Web.UI.WebControls.TLinkButton'); Prado::using('System.Web.UI.WebControls.TImageButton'); Prado::using('System.Web.UI.WebControls.TDataList'); +Prado::using('System.Collections.TStack'); /** * Class TWizard. diff --git a/framework/Xml/TXmlDocument.php b/framework/Xml/TXmlDocument.php new file mode 100644 index 00000000..fb73a9f5 --- /dev/null +++ b/framework/Xml/TXmlDocument.php @@ -0,0 +1,455 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Xml + */ + +/** + * TXmlElement class. + * + * TXmlElement represents an XML element node. + * You can obtain its tagname, attributes, text between the openning and closing + * tags via the TagName, Attributes, and Value properties, respectively. + * You can also retrieve its parent and child elements by Parent and Elements + * properties, respectively. + * + * TBD: xpath + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Xml + * @since 3.0 + */ +class TXmlElement extends TComponent +{ + /** + * @var TXmlElement parent of this element + */ + private $_parent=null; + /** + * @var string tagname of this element + */ + private $_tagName; + /** + * @var string text enclosed between openning and closing tags of this element + */ + private $_value; + /** + * @var TXmlElementList list of child elements of this element + */ + private $_elements=null; + /** + * @var TMap attributes of this element + */ + private $_attributes=null; + + /** + * Constructor. + * @param string tagname for this element + */ + public function __construct($tagName) + { + $this->setTagName($tagName); + } + + /** + * @return TXmlElement parent element of this element + */ + public function getParent() + { + return $this->_parent; + } + + /** + * @param TXmlElement parent element of this element + */ + public function setParent($parent) + { + $this->_parent=$parent; + } + + /** + * @return string tagname of this element + */ + public function getTagName() + { + return $this->_tagName; + } + + /** + * @param string tagname of this element + */ + public function setTagName($tagName) + { + $this->_tagName=$tagName; + } + + /** + * @return string text enclosed between opening and closing tag of this element + */ + public function getValue() + { + return $this->_value; + } + + /** + * @param string text enclosed between opening and closing tag of this element + */ + public function setValue($value) + { + $this->_value=$value; + } + + /** + * @return boolean true if this element has child elements + */ + public function getHasElement() + { + return $this->_elements!==null && $this->_elements->getCount()>0; + } + + /** + * @return boolean true if this element has attributes + */ + public function getHasAttribute() + { + return $this->_attributes!==null && $this->_attributes->getCount()>0; + } + + /** + * @return string the attribute specified by the name, null if no such attribute + */ + public function getAttribute($name) + { + if($this->_attributes!==null) + return $this->_attributes->itemAt($name); + else + return null; + } + + /** + * @return TXmlElementList list of child elements + */ + public function getElements() + { + if(!$this->_elements) + $this->_elements=new TXmlElementList($this); + return $this->_elements; + } + + /** + * @return TMap list of attributes + */ + public function getAttributes() + { + if(!$this->_attributes) + $this->_attributes=new TMap; + return $this->_attributes; + } + + /** + * @return TXmlElement the first child element that has the specified tagname, null if not found + */ + public function getElementByTagName($tagName) + { + if($this->_elements) + { + foreach($this->_elements as $element) + if($element->_tagName===$tagName) + return $element; + } + return null; + } + + /** + * @return TList list of all child elements that have the specified tagname + */ + public function getElementsByTagName($tagName) + { + $list=new TList; + if($this->_elements) + { + foreach($this->_elements as $element) + if($element->_tagName===$tagName) + $list->add($element); + } + return $list; + } + + /** + * @return string string representation of this element + */ + public function toString($indent=0) + { + $attr=''; + if($this->_attributes!==null) + { + foreach($this->_attributes as $name=>$value) + $attr.=" $name=\"$value\""; + } + $prefix=str_repeat(' ',$indent*4); + if($this->getHasElement()) + { + $str=$prefix."<{$this->_tagName}$attr>\n"; + foreach($this->getElements() as $element) + $str.=$element->toString($indent+1)."\n"; + $str.=$prefix."_tagName}>"; + return $str; + } + else if($this->getValue()!=='') + { + return $prefix."<{$this->_tagName}$attr>{$this->_value}_tagName}>"; + } + else + return $prefix."<{$this->_tagName}$attr />"; + } +} + +/** + * TXmlDocument class. + * + * TXmlDocument represents a DOM representation of an XML file. + * Besides all properties and methods inherited from {@link TXmlElement}, + * you can load an XML file or string by {@link loadFromFile} or {@link loadFromString}. + * You can also get the version and encoding of the XML document by + * the Version and Encoding properties. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Xml + * @since 3.0 + */ +class TXmlDocument extends TXmlElement +{ + /** + * @var string version of this XML document + */ + private $_version; + /** + * @var string encoding of this XML document + */ + private $_encoding; + + /** + * Constructor. + * @param string version of this XML document + * @param string encoding of this XML document + */ + public function __construct($version='1.0',$encoding='') + { + parent::__construct(''); + $this->setversion($version); + $this->setEncoding($encoding); + } + + /** + * @return string version of this XML document + */ + public function getVersion() + { + return $this->_version; + } + + /** + * @param string version of this XML document + */ + public function setVersion($version) + { + $this->_version=$version; + } + + /** + * @return string encoding of this XML document + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * @param string encoding of this XML document + */ + public function setEncoding($encoding) + { + $this->_encoding=$encoding; + } + + /** + * Loads and parses an XML document. + * @param string the XML file path + * @return boolean whether the XML file is parsed successfully + * @throws TIOException if the file fails to be opened. + */ + public function loadFromFile($file) + { + if(($str=@file_get_contents($file))!==false) + return $this->loadFromString($str); + else + throw new TIOException('xmldocument_file_read_failed',$file); + } + + /** + * Loads and parses an XML string. + * The version and encoding will be determined based on the parsing result. + * @param string the XML string + * @return boolean whether the XML string is parsed successfully + */ + public function loadFromString($string) + { + // TODO: since PHP 5.1, we can get parsing errors and throw them as exception + $doc=new DOMDocument(); + if($doc->loadXML($string)===false) + return false; + + $this->setEncoding($doc->encoding); + $this->setVersion($doc->version); + + $element=$doc->documentElement; + $this->setTagName($element->tagName); + $this->setValue($element->nodeValue); + $elements=$this->getElements(); + $attributes=$this->getAttributes(); + $elements->clear(); + $attributes->clear(); + foreach($element->attributes as $name=>$attr) + $attributes->add($name,$attr->value); + foreach($element->childNodes as $child) + { + if($child instanceof DOMElement) + $elements->add($this->buildElement($child)); + } + + return true; + } + + /** + * Saves this XML document as an XML file. + * @param string the name of the file to be stored with XML output + * @throws TIOException if the file cannot be written + */ + public function saveToFile($file) + { + if(($fw=fopen($file,'w'))!==false) + { + fwrite($fw,$this->saveToString()); + fclose($fw); + } + else + throw new TIOException('xmldocument_file_write_failed',$file); + } + + /** + * Saves this XML document as an XML string + * @return string the XML string of this XML document + */ + public function saveToString() + { + $version=empty($this->_version)?' version="1.0"':' version="'.$this->_version.'"'; + $encoding=empty($this->_encoding)?'':' encoding="'.$this->_encoding.'"'; + return "\n".$this->toString(0); + } + + /** + * Recursively converts DOM XML nodes into TXmlElement + * @param DOMXmlNode the node to be converted + * @return TXmlElement the converted TXmlElement + */ + private function buildElement($node) + { + $element=new TXmlElement($node->tagName); + $element->setValue($node->nodeValue); + foreach($node->attributes as $name=>$attr) + $element->getAttributes()->add($name,$attr->value); + foreach($node->childNodes as $child) + { + if($child instanceof DOMElement) + $element->getElements()->add($this->buildElement($child)); + } + return $element; + } +} + + +/** + * TXmlElement class. + * + * TXmlElement represents an XML element node. + * You can obtain its tagname, attributes, text between the openning and closing + * tags via the TagName, Attributes, and Value properties, respectively. + * You can also retrieve its parent and child elements by Parent and Elements + * properties, respectively. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Xml + * @since 3.0 + */ +class TXmlElementList extends TList +{ + /** + * @var TXmlElement owner of this list + */ + private $_o; + + /** + * Constructor. + * @param TXmlElement owner of this list + */ + public function __construct(TXmlElement $owner) + { + $this->_o=$owner; + } + + /** + * @return TXmlElement owner of this list + */ + protected function getOwner() + { + return $this->_o; + } + + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added TXmlElement object. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TXmlElement object. + */ + public function insertAt($index,$item) + { + if($item instanceof TXmlElement) + { + parent::insertAt($index,$item); + if($item->getParent()!==null) + $item->getParent()->getElements()->remove($item); + $item->setParent($this->_o); + } + else + throw new TInvalidDataTypeException('xmlelementlist_xmlelement_required'); + } + + /** + * Removes an item at the specified position. + * This overrides the parent implementation by performing additional + * cleanup work when removing a TXmlElement object. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + */ + public function removeAt($index) + { + $item=parent::removeAt($index); + if($item instanceof TXmlElement) + $item->setParent(null); + return $item; + } +} + +?> \ No newline at end of file diff --git a/framework/core.php b/framework/core.php deleted file mode 100644 index 6f7d081d..00000000 --- a/framework/core.php +++ /dev/null @@ -1,1094 +0,0 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Revision: $ $Date: $ - * @package System - */ - -/** - * The framework installation path. - */ -define('PRADO_DIR',dirname(__FILE__)); - -/** - * Includes TComponent definition - */ -require_once(PRADO_DIR.'/TComponent.php'); -/** - * Includes TApplicationComponent definition - */ -require_once(PRADO_DIR.'/TApplicationComponent.php'); -/** - * Includes exception definitions - */ -require_once(PRADO_DIR.'/Exceptions/TException.php'); -/** - * Includes TList definition - */ -require_once(PRADO_DIR.'/Collections/TList.php'); -/** - * Includes TMap definition - */ -require_once(PRADO_DIR.'/Collections/TMap.php'); -/** - * Includes TStack definition - */ -require_once(PRADO_DIR.'/Collections/TStack.php'); -/** - * Includes TXmlDocument, TXmlElement definition - */ -require_once(PRADO_DIR.'/Data/TXmlDocument.php'); -/** - * Includes THttpUtility definition - */ -require_once(PRADO_DIR.'/Web/THttpUtility.php'); -/** - * Includes TJavaScript definition - */ -require_once(PRADO_DIR.'/Web/Javascripts/TJavaScript.php'); -/** - * Includes TCache definition - */ -require_once(PRADO_DIR.'/Data/TCache.php'); -/** - * Includes TDataFieldAccessor definition - */ -require_once(PRADO_DIR.'/Data/TDataFieldAccessor.php'); -/** - * Includes TLogger definition - */ -require_once(PRADO_DIR.'/Log/TLogger.php'); - -/** - * IModule interface. - * - * This interface must be implemented by application modules. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -interface IModule -{ - /** - * Initializes the module. - * @param TXmlElement the configuration for the module - */ - public function init($config); - /** - * @return string ID of the module - */ - public function getID(); - /** - * @param string ID of the module - */ - public function setID($id); -} - -/** - * IService interface. - * - * This interface must be implemented by services. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -interface IService -{ - /** - * Initializes the service. - * @param TXmlElement the configuration for the service - */ - public function init($config); - /** - * @return string ID of the service - */ - public function getID(); - /** - * @param string ID of the service - */ - public function setID($id); - /** - * Runs the service. - */ - public function run(); -} - -/** - * ITextWriter interface. - * - * This interface must be implemented by writers. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -interface ITextWriter -{ - /** - * Writes a string. - * @param string string to be written - */ - public function write($str); - /** - * Flushes the content that has been written. - */ - public function flush(); -} - -/** - * ITheme interface. - * - * This interface must be implemented by theme. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -interface ITheme -{ - /** - * Applies this theme to the specified control. - * @param TControl the control to be applied with this theme - */ - public function applySkin($control); -} - -/** - * ITemplate interface - * - * ITemplate specifies the interface for classes encapsulating - * parsed template structures. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -interface ITemplate -{ - /** - * Instantiates the template. - * Content in the template will be instantiated as components and text strings - * and passed to the specified parent control. - * @param TControl the parent control - */ - public function instantiateIn($parent); -} - -/** - * IUser interface. - * - * This interface must be implemented by user objects. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -interface IUser -{ - /** - * @return string username - */ - public function getName(); - /** - * @param string username - */ - public function setName($value); - /** - * @return boolean if the user is a guest - */ - public function getIsGuest(); - /** - * @param boolean if the user is a guest - */ - public function setIsGuest($value); - /** - * @return array list of roles that the user is of - */ - public function getRoles(); - /** - * @return array|string list of roles that the user is of. If it is a string, roles are assumed by separated by comma - */ - public function setRoles($value); - /** - * @param string role to be tested - * @return boolean whether the user is of this role - */ - public function isInRole($role); - /** - * @return string user data that is serialized and will be stored in session - */ - public function saveToString(); - /** - * @param string user data that is serialized and restored from session - * @return IUser the user object - */ - public function loadFromString($string); -} - -/** - * IStatePersister class. - * - * This interface must be implemented by all state persister classes (such as - * {@link TPageStatePersister}, {@link TApplicationStatePersister}. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -interface IStatePersister -{ - /** - * Loads state from a persistent storage. - * @return mixed the state - */ - public function load(); - /** - * Saves state into a persistent storage. - * @param mixed the state to be saved - */ - public function save($state); -} - -/** - * TModule class. - * - * TModule implements the basic methods required by IModule and may be - * used as the basic class for application modules. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -abstract class TModule extends TApplicationComponent implements IModule -{ - /** - * @var string module id - */ - private $_id; - - /** - * Initializes the module. - * This method is required by IModule and is invoked by application. - * @param TXmlElement module configuration - */ - public function init($config) - { - } - - /** - * @return string id of this module - */ - public function getID() - { - return $this->_id; - } - - /** - * @param string id of this module - */ - public function setID($value) - { - $this->_id=$value; - } -} - -/** - * TService class. - * - * TService implements the basic methods required by IService and may be - * used as the basic class for application services. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -abstract class TService extends TApplicationComponent implements IService -{ - /** - * @var string service id - */ - private $_id; - - /** - * Initializes the service and attaches {@link run} to the RunService event of application. - * This method is required by IService and is invoked by application. - * @param TXmlElement module configuration - */ - public function init($config) - { - } - - /** - * @return string id of this service - */ - public function getID() - { - return $this->_id; - } - - /** - * @param string id of this service - */ - public function setID($value) - { - $this->_id=$value; - } - - /** - * Runs the service. - */ - public function run() - { - } -} - -/** - * TVarDumper class. - * - * TVarDumper is intended to replace the buggy PHP function var_dump and print_r. - * It can correctly identify the recursively referenced objects in a complex - * object structure. It also has a recurisve depth control to avoid indefinite - * recursive display of some peculiar variables. - * - * TVarDumper can be used as follows, - * - * echo TVarDumper::dump($var); - * - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -class TVarDumper -{ - private static $_objects; - private static $_output; - private static $_depth; - - /** - * Converts a variable into a string representation. - * This method achieves the similar functionality as var_dump and print_r - * but is more robust when handling complex objects such as PRADO controls. - * @param mixed variable to be dumped - * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. - * @return string the string representation of the variable - */ - public static function dump($var,$depth=10) - { - self::$_output=''; - self::$_objects=array(); - self::$_depth=$depth; - self::dumpInternal($var,0); - return self::$_output; - } - - private static function dumpInternal($var,$level) - { - switch(gettype($var)) - { - case 'boolean': - self::$_output.=$var?'true':'false'; - break; - case 'integer': - self::$_output.="$var"; - break; - case 'double': - self::$_output.="$var"; - break; - case 'string': - self::$_output.="'$var'"; - break; - case 'resource': - self::$_output.='{resource}'; - break; - case 'NULL': - self::$_output.="null"; - break; - case 'unknown type': - self::$_output.='{unknown}'; - break; - case 'array': - if(self::$_depth<=$level) - self::$_output.='array(...)'; - else if(empty($var)) - self::$_output.='array()'; - else - { - $keys=array_keys($var); - $spaces=str_repeat(' ',$level*4); - self::$_output.="array\n".$spaces.'('; - foreach($keys as $key) - { - self::$_output.="\n".$spaces." [$key] => "; - self::$_output.=self::dumpInternal($var[$key],$level+1); - } - self::$_output.="\n".$spaces.')'; - } - break; - case 'object': - if(($id=array_search($var,self::$_objects,true))!==false) - self::$_output.=get_class($var).'#'.($id+1).'(...)'; - else if(self::$_depth<=$level) - self::$_output.=get_class($var).'(...)'; - else - { - $id=array_push(self::$_objects,$var); - $className=get_class($var); - $members=(array)$var; - $keys=array_keys($members); - $spaces=str_repeat(' ',$level*4); - self::$_output.="$className#$id\n".$spaces.'('; - foreach($keys as $key) - { - $keyDisplay=strtr(trim($key),array("\0"=>':')); - self::$_output.="\n".$spaces." [$keyDisplay] => "; - self::$_output.=self::dumpInternal($members[$key],$level+1); - } - self::$_output.="\n".$spaces.')'; - } - break; - } - } -} - - -/** - * PradoBase class. - * - * PradoBase implements a few fundamental static methods. - * - * To use the static methods, Use Prado as the class name rather than PradoBase. - * PradoBase is meant to serve as the base class of Prado. The latter might be - * rewritten for customization. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -class PradoBase -{ - /** - * File extension for Prado class files. - */ - const CLASS_FILE_EXT='.php'; - /** - * @var array list of path aliases - */ - private static $_aliases=array('System'=>PRADO_DIR); - /** - * @var array list of namespaces currently in use - */ - private static $_usings=array(); - /** - * @var TApplication the application instance - */ - private static $_application=null; - /** - * @var TLogger logger instance - */ - private static $_logger=null; - - /** - * @return string the version of Prado framework - */ - public static function getVersion() - { - return '3.0RC1'; - } - - /** - * Initializes error handlers. - * This method set error and exception handlers to be functions - * defined in this class. - */ - public static function initErrorHandlers() - { - /** - * Sets error handler to be Prado::phpErrorHandler - */ - set_error_handler(array('PradoBase','phpErrorHandler'),error_reporting()); - /** - * Sets exception handler to be Prado::exceptionHandler - */ - set_exception_handler(array('PradoBase','exceptionHandler')); - } - - /** - * Class autoload loader. - * This method is provided to be invoked within an __auload() magic method. - * @param string class name - */ - public static function autoload($className) - { - include_once($className.self::CLASS_FILE_EXT); - if(!class_exists($className,false) && !interface_exists($className,false)) - self::fatalError("Class file for '$className' cannot be found."); - } - - /** - * @return string a string that can be displayed on your Web page showing powered-by-PRADO information - */ - public static function poweredByPrado() - { - return 'Powered by PRADO'; - } - - /** - * PHP error handler. - * This method should be registered as PHP error handler using - * {@link set_error_handler}. The method throws an exception that - * contains the error information. - * @param integer the level of the error raised - * @param string the error message - * @param string the filename that the error was raised in - * @param integer the line number the error was raised at - */ - public static function phpErrorHandler($errno,$errstr,$errfile,$errline) - { - if(error_reporting()!=0) - throw new TPhpErrorException($errno,$errstr,$errfile,$errline); - } - - /** - * Default exception handler. - * This method should be registered as default exception handler using - * {@link set_exception_handler}. The method tries to use the errorhandler - * module of the Prado application to handle the exception. - * If the application or the module does not exist, it simply echoes the - * exception. - * @param Exception exception that is not caught - */ - public static function exceptionHandler($exception) - { - if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null) - { - $errorHandler->handleError(null,$exception); - } - else - { - echo $exception; - } - exit(1); - } - - /** - * Stores the application instance in the class static member. - * This method helps implement a singleton pattern for TApplication. - * Repeated invocation of this method or the application constructor - * will cause the throw of an exception. - * This method should only be used by framework developers. - * @param TApplication the application instance - * @throws TInvalidOperationException if this method is invoked twice or more. - */ - public static function setApplication($application) - { - if(self::$_application!==null) - throw new TInvalidOperationException('prado_application_singleton_required'); - self::$_application=$application; - } - - /** - * @return TApplication the application singleton, null if the singleton has not be created yet. - */ - public static function getApplication() - { - return self::$_application; - } - - /** - * @return string the path of the framework - */ - public static function getFrameworkPath() - { - return PRADO_DIR; - } - - /** - * Serializes a data. - * The original PHP serialize function has a bug that may not serialize - * properly an object. - * @param mixed data to be serialized - * @return string the serialized data - */ - public static function serialize($data) - { - $arr[0]=$data; - return serialize($arr); - } - - /** - * Unserializes a data. - * The original PHP unserialize function has a bug that may not unserialize - * properly an object. - * @param string data to be unserialized - * @return mixed unserialized data, null if unserialize failed - */ - public static function unserialize($str) - { - $arr=unserialize($str); - return isset($arr[0])?$arr[0]:null; - } - - /** - * Creates a component with the specified type. - * A component type can be either the component class name - * or a namespace referring to the path of the component class file. - * For example, 'TButton', 'System.Web.UI.WebControls.TButton' are both - * valid component type. - * This method can also pass parameters to component constructors. - * All paramters passed to this method except the first one (the component type) - * will be supplied as component constructor paramters. - * @param string component type - * @return TComponent component instance of the specified type - * @throws TInvalidDataValueException if the component type is unknown - */ - public static function createComponent($type) - { - self::using($type); - if(($pos=strrpos($type,'.'))!==false) - $type=substr($type,$pos+1); - if(($n=func_num_args())>1) - { - $args=func_get_args(); - $s='$args[1]'; - for($i=2;$i<$n;++$i) - $s.=",\$args[$i]"; - eval("\$component=new $type($s);"); - return $component; - } - else - return new $type; - } - - /** - * Uses a namespace. - * A namespace ending with an asterisk '*' refers to a directory, otherwise it represents a PHP file. - * If the namespace corresponds to a directory, the directory will be appended - * to the include path. If the namespace corresponds to a file, it will be included (include_once). - * @param string namespace to be used - * @throws TInvalidDataValueException if the namespace is invalid - */ - public static function using($namespace) - { - if(isset(self::$_usings[$namespace]) || class_exists($namespace,false)) - return; - if(($pos=strrpos($namespace,'.'))===false) // a class name - { - try - { - include_once($namespace.self::CLASS_FILE_EXT); - } - catch(Exception $e) - { - if(!class_exists($namespace,false)) - throw new TInvalidOperationException('prado_component_unknown',$namespace); - else - throw $e; - } - } - else if(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null) - { - $className=substr($namespace,$pos+1); - if($className==='*') // a directory - { - if(is_dir($path)) - { - self::$_usings[$namespace]=$path; - set_include_path(get_include_path().PATH_SEPARATOR.$path); - } - else - throw new TInvalidDataValueException('prado_using_invalid',$namespace); - } - else // a file - { - if(is_file($path)) - { - self::$_usings[$namespace]=$path; - if(!class_exists($className,false)) - { - try - { - include_once($path); - } - catch(Exception $e) - { - if(!class_exists($className,false)) - throw new TInvalidOperationException('prado_component_unknown',$className); - else - throw $e; - } - } - } - else - throw new TInvalidDataValueException('prado_using_invalid',$namespace); - } - } - else - throw new TInvalidDataValueException('prado_using_invalid',$namespace); - } - - /** - * Translates a namespace into a file path. - * The first segment of the namespace is considered as a path alias - * which is replaced with the actual path. The rest segments are - * subdirectory names appended to the aliased path. - * If the namespace ends with an asterisk '*', it represents a directory; - * Otherwise it represents a file whose extension name is specified by the second parameter (defaults to empty). - * Note, this method does not ensure the existence of the resulting file path. - * @param string namespace - * @param string extension to be appended if the namespace refers to a file - * @return string file path corresponding to the namespace, null if namespace is invalid - */ - public static function getPathOfNamespace($namespace,$ext='') - { - if(isset(self::$_usings[$namespace])) - return self::$_usings[$namespace]; - else if(isset(self::$_aliases[$namespace])) - return self::$_aliases[$namespace]; - else - { - $segs=explode('.',$namespace); - $alias=array_shift($segs); - if(($file=array_pop($segs))!==null && ($root=self::getPathOfAlias($alias))!==null) - return rtrim($root.'/'.implode('/',$segs),'/').(($file==='*')?'':'/'.$file.$ext); - else - return null; - } - } - - /** - * @param string alias to the path - * @return string the path corresponding to the alias, null if alias not defined. - */ - public static function getPathOfAlias($alias) - { - return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null; - } - - /** - * @param string alias to the path - * @param string the path corresponding to the alias - * @throws TInvalidOperationException if the alias is already defined - * @throws TInvalidDataValueException if the path is not a valid file path - */ - public static function setPathOfAlias($alias,$path) - { - if(isset(self::$_aliases[$alias])) - throw new TInvalidOperationException('prado_alias_redefined',$alias); - else if(($rp=realpath($path))!==false && is_dir($rp)) - { - if(strpos($alias,'.')===false) - self::$_aliases[$alias]=$rp; - else - throw new TInvalidDataValueException('prado_aliasname_invalid',$alias); - } - else - throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path); - } - - /** - * Fatal error handler. - * This method displays an error message together with the current call stack. - * The application will exit after calling this method. - * @param string error message - */ - public static function fatalError($msg) - { - echo '

Fatal Error

'; - echo '

'.$msg.'

'; - if(!function_exists('debug_backtrace')) - return; - echo '

Debug Backtrace

'; - echo '
';
-		$index=-1;
-		foreach(debug_backtrace() as $t)
-		{
-			$index++;
-			if($index==0)  // hide the backtrace of this function
-				continue;
-			echo '#'.$index.' ';
-			if(isset($t['file']))
-				echo basename($t['file']) . ':' . $t['line'];
-			else
-			   echo '';
-			echo ' -- ';
-			if(isset($t['class']))
-				echo $t['class'] . $t['type'];
-			echo $t['function'];
-			if(isset($t['args']) && sizeof($t['args']) > 0)
-				echo '(...)';
-			else
-				echo '()';
-			echo "\n";
-		}
-		echo '
'; - exit(1); - } - - /** - * Returns a list of user preferred languages. - * The languages are returned as an array. Each array element - * represents a single language preference. The languages are ordered - * according to user preferences. The first language is the most preferred. - * @return array list of user preferred languages. - */ - public static function getUserLanguages() - { - static $languages=null; - if($languages===null) - { - if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) - $languages[0]='en'; - else - { - $languages=array(); - foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language) - { - $array=split(';q=',trim($language)); - $languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0; - } - arsort($languages); - $languages=array_keys($languages); - if(empty($languages)) - $languages[0]='en'; - } - } - return $languages; - } - - /** - * Returns the most preferred language by the client user. - * @return string the most preferred language by the client user, defaults to English. - */ - public static function getPreferredLanguage() - { - static $language=null; - if($language===null) - { - $langs=Prado::getUserLanguages(); - $lang=explode('-',$langs[0]); - if(empty($lang[0]) || !ctype_alpha($lang[0])) - $language='en'; - else - $language=$lang[0]; - } - return $language; - } - - /** - * Writes a log message. - * This method wraps {@link log()} by checking the application mode. - * When the application is in Debug mode, debug backtrace information is appended - * to the message and the message is logged at DEBUG level. - * When the application is in Performance mode, this method does nothing. - * Otherwise, the message is logged at INFO level. - * @param string message to be logged - * @param string category of the message - * @see log, getLogger - */ - public static function trace($msg,$category='Uncategorized') - { - if(self::$_application && self::$_application->getMode()===TApplication::STATE_PERFORMANCE) - return; - if(!self::$_application || self::$_application->getMode()===TApplication::STATE_DEBUG) - { - $trace=debug_backtrace(); - if(isset($trace[0]['file']) && isset($trace[0]['line'])) - $msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})"; - $level=TLogger::DEBUG; - } - else - $level=TLogger::INFO; - self::log($msg,$level,$category); - } - - /** - * Logs a message. - * Messages logged by this method may be retrieved via {@link TLogger::getLogs} - * and may be recorded in different media, such as file, email, database, using - * {@link TLogRouter}. - * @param string message to be logged - * @param integer level of the message. Valid values include - * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, - * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. - * @param string category of the message - */ - public static function log($msg,$level=TLogger::INFO,$category='Uncategorized') - { - if(self::$_logger===null) - self::$_logger=new TLogger; - self::$_logger->log($msg,$level,$category); - } - - /** - * @return TLogger message logger - */ - public static function getLogger() - { - if(self::$_logger===null) - self::$_logger=new TLogger; - return self::$_logger; - } - - /** - * Converts a variable into a string representation. - * This method achieves the similar functionality as var_dump and print_r - * but is more robust when handling complex objects such as PRADO controls. - * @param mixed variable to be dumped - * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. - * @return string the string representation of the variable - */ - public static function varDump($var,$depth=10) - { - return TVarDumper::dump($var,$depth); - } -} - -/** - * TTextWriter class. - * - * TTextWriter implements a memory-based text writer. - * Content written by TTextWriter are stored in memory - * and can be obtained by calling {@link flush()}. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ -class TTextWriter extends TComponent implements ITextWriter -{ - private $_str=''; - - /** - * Flushes the content that has been written. - * @return string the content being flushed - */ - public function flush() - { - $str=$this->_str; - $this->_str=''; - return $str; - } - - /** - * Writes a string. - * @param string string to be written - */ - public function write($str) - { - $this->_str.=$str; - } - - /** - * Writers a string and terminates it with a newline. - * @param string content to be written - * @see write - */ - public function writeLine($str='') - { - $this->write($str."\n"); - } -} - -if(version_compare(phpversion(),'5.1.0','>=')) -{ - /** - * TReflectionClass class. - * This class is written to cope with the incompatibility between different PHP versions. - * It is equivalent to ReflectionClass if PHP version >= 5.1.0 - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ - class TReflectionClass extends ReflectionClass - { - } -} -else // PHP < 5.1.0 -{ - /** - * TReflectionClass class. - * This class is written to cope with the incompatibility between different PHP versions. - * It mainly provides a way to detect if a method exists for a given class name. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System - * @since 3.0 - */ - class TReflectionClass extends ReflectionClass - { - /** - * @param string method name - * @return boolean whether the method exists - */ - public function hasMethod($method) - { - try - { - return $this->getMethod($method)!==null; - } - catch(Exception $e) - { - return false; - } - } - - /** - * @param string property name - * @return boolean whether the property exists - */ - public function hasProperty($property) - { - try - { - return $this->getProperty($property)!==null; - } - catch(Exception $e) - { - return false; - } - } - } - - if(!function_exists('property_exists')) - { - /** - * Detects whether an object contains the specified member variable. - * @param object - * @param string member variable (property) name - * @return boolean - */ - function property_exists($object, $property) - { - if(is_object($object)) - return array_key_exists($property, get_object_vars($object)); - else - return false; - } - } -} - -?> \ No newline at end of file diff --git a/framework/interfaces.php b/framework/interfaces.php new file mode 100644 index 00000000..f63da347 --- /dev/null +++ b/framework/interfaces.php @@ -0,0 +1,172 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System + */ + +/** + * IModule interface. + * + * This interface must be implemented by application modules. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +interface IModule +{ + /** + * Initializes the module. + * @param TXmlElement the configuration for the module + */ + public function init($config); + /** + * @return string ID of the module + */ + public function getID(); + /** + * @param string ID of the module + */ + public function setID($id); +} + +/** + * IService interface. + * + * This interface must be implemented by services. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +interface IService +{ + /** + * Initializes the service. + * @param TXmlElement the configuration for the service + */ + public function init($config); + /** + * @return string ID of the service + */ + public function getID(); + /** + * @param string ID of the service + */ + public function setID($id); + /** + * Runs the service. + */ + public function run(); +} + +/** + * ITextWriter interface. + * + * This interface must be implemented by writers. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +interface ITextWriter +{ + /** + * Writes a string. + * @param string string to be written + */ + public function write($str); + /** + * Flushes the content that has been written. + */ + public function flush(); +} + + +/** + * IUser interface. + * + * This interface must be implemented by user objects. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +interface IUser +{ + /** + * @return string username + */ + public function getName(); + /** + * @param string username + */ + public function setName($value); + /** + * @return boolean if the user is a guest + */ + public function getIsGuest(); + /** + * @param boolean if the user is a guest + */ + public function setIsGuest($value); + /** + * @return array list of roles that the user is of + */ + public function getRoles(); + /** + * @return array|string list of roles that the user is of. If it is a string, roles are assumed by separated by comma + */ + public function setRoles($value); + /** + * @param string role to be tested + * @return boolean whether the user is of this role + */ + public function isInRole($role); + /** + * @return string user data that is serialized and will be stored in session + */ + public function saveToString(); + /** + * @param string user data that is serialized and restored from session + * @return IUser the user object + */ + public function loadFromString($string); +} + +/** + * IStatePersister class. + * + * This interface must be implemented by all state persister classes (such as + * {@link TPageStatePersister}, {@link TApplicationStatePersister}. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +interface IStatePersister +{ + /** + * Loads state from a persistent storage. + * @return mixed the state + */ + public function load(); + /** + * Saves state into a persistent storage. + * @param mixed the state to be saved + */ + public function save($state); +} + +?> \ No newline at end of file diff --git a/framework/prado.php b/framework/prado.php index 59268105..d55898b4 100644 --- a/framework/prado.php +++ b/framework/prado.php @@ -4,8 +4,10 @@ * * This file is intended to be included in the entry script of Prado applications. * It defines Prado class by extending PradoBase, a static class providing globally - * available functionalities to Prado applications. It also sets PHP error and - * exception handler functions, and provides a __autoload function which automatically + * available functionalities that enable PRADO component model and error handling mechanism. + * + * By including this file, the PHP error and exception handlers are set as + * PRADO handlers, and an __autoload function is provided that automatiically * loads a class file if the class is not defined. * * @author Qiang Xue @@ -17,9 +19,9 @@ */ /** - * Includes the Prado core header file + * Includes the PradoBase class file */ -require_once(dirname(__FILE__).'/core.php'); +require_once(dirname(__FILE__).'/PradoBase.php'); /** * Defines Prado class if not defined. -- cgit v1.2.3