From e8b0fd13270ca7d678be42a04f063e3963e13e3f Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 19 May 2012 09:46:32 -0400 Subject: [PATCH] ... --- framework/YiiBase.php | 2 +- framework/base/ActionFilter.php | 12 +- framework/base/Application.php | 31 ++- framework/base/Controller.php | 47 +++- framework/base/RenderEvent.php | 39 +++ framework/base/Theme.php | 49 ++-- framework/base/ThemeManager.php | 94 ------- framework/base/View.php | 6 +- framework/base/Widget.php | 5 - framework/caching/ApcCache.php | 109 ++++++++ framework/caching/Cache.php | 399 +++++++++++++++++++++++++++++ framework/caching/ChainedDependency.php | 98 +++++++ framework/caching/DbCache.php | 314 +++++++++++++++++++++++ framework/caching/DbDependency.php | 112 ++++++++ framework/caching/Dependency.php | 113 ++++++++ framework/caching/DirectoryDependency.php | 134 ++++++++++ framework/caching/DummyCache.php | 164 ++++++++++++ framework/caching/EAcceleratorCache.php | 107 ++++++++ framework/caching/ExpressionDependency.php | 53 ++++ framework/caching/FileCache.php | 222 ++++++++++++++++ framework/caching/FileDependency.php | 53 ++++ framework/caching/MemCache.php | 282 ++++++++++++++++++++ framework/caching/WinCache.php | 109 ++++++++ framework/caching/XCache.php | 104 ++++++++ framework/caching/ZendDataCache.php | 99 +++++++ 25 files changed, 2603 insertions(+), 154 deletions(-) create mode 100644 framework/base/RenderEvent.php delete mode 100644 framework/base/ThemeManager.php create mode 100644 framework/caching/ApcCache.php create mode 100644 framework/caching/Cache.php create mode 100644 framework/caching/ChainedDependency.php create mode 100644 framework/caching/DbCache.php create mode 100644 framework/caching/DbDependency.php create mode 100644 framework/caching/Dependency.php create mode 100644 framework/caching/DirectoryDependency.php create mode 100644 framework/caching/DummyCache.php create mode 100644 framework/caching/EAcceleratorCache.php create mode 100644 framework/caching/ExpressionDependency.php create mode 100644 framework/caching/FileCache.php create mode 100644 framework/caching/FileDependency.php create mode 100644 framework/caching/MemCache.php create mode 100644 framework/caching/WinCache.php create mode 100644 framework/caching/XCache.php create mode 100644 framework/caching/ZendDataCache.php diff --git a/framework/YiiBase.php b/framework/YiiBase.php index 6e1e1a3..277be2c 100644 --- a/framework/YiiBase.php +++ b/framework/YiiBase.php @@ -191,7 +191,7 @@ class YiiBase { if (isset(self::$aliases[$alias])) { return self::$aliases[$alias]; - } elseif ($alias[0] !== '@') { // not an alias + } elseif ($alias === '' || $alias[0] !== '@') { // not an alias return $alias; } elseif (($pos = strpos($alias, '/')) !== false) { $rootAlias = substr($alias, 0, $pos); diff --git a/framework/base/ActionFilter.php b/framework/base/ActionFilter.php index 0da475a..d572a61 100644 --- a/framework/base/ActionFilter.php +++ b/framework/base/ActionFilter.php @@ -57,27 +57,27 @@ class ActionFilter extends Behavior $this->owner->getEventHandlers('afterAction')->insertAt(0, array($this, 'handleEvent')); } - public function authorize(ActionEvent $event) + public function authorize($event) { } - public function beforeAction(ActionEvent $event) + public function beforeAction($event) { } - public function beforeRender(ActionEvent $event) + public function beforeRender($event) { } - public function afterRender(ActionEvent $event) + public function afterRender($event) { } - public function afterAction(ActionEvent $event) + public function afterAction($event) { } - public function handleEvent(ActionEvent $event) + public function handleEvent($event) { if ($this->applyTo($event->action)) { $this->{$event->name}($event); diff --git a/framework/base/Application.php b/framework/base/Application.php index c80fcaa..dac3f10 100644 --- a/framework/base/Application.php +++ b/framework/base/Application.php @@ -118,9 +118,7 @@ class Application extends Module \Yii::$application = $this; $this->id = $id; $this->setBasePath($basePath); - \Yii::$aliases['@application'] = $this->getBasePath(); - \Yii::$aliases['@entry'] = dirname($_SERVER['SCRIPT_FILENAME']); - \Yii::$aliases['@www'] = ''; + $this->registerDefaultAliases(); $this->registerCoreComponents(); } @@ -217,7 +215,7 @@ class Application extends Module */ public function getRuntimePath() { - if ($this->_runtimePath === null) { + if ($this->_runtimePath !== null) { $this->setRuntimePath($this->getBasePath() . DIRECTORY_SEPARATOR . 'runtime'); } return $this->_runtimePath; @@ -230,10 +228,12 @@ class Application extends Module */ public function setRuntimePath($path) { - if (!is_dir($path) || !is_writable($path)) { + $p = \Yii::getAlias($path); + if ($p === false || !is_dir($p) || !is_writable($path)) { throw new Exception("Application runtime path \"$path\" is invalid. Please make sure it is a directory writable by the Web server process."); + } else { + $this->_runtimePath = $p; } - $this->_runtimePath = $path; } /** @@ -329,6 +329,15 @@ class Application extends Module } /** + * Returns the application theme. + * @return Theme the theme that this application is currently using. + */ + public function getTheme() + { + return $this->getComponent('theme'); + } + + /** * Returns the security manager component. * @return SecurityManager the security manager application component. */ @@ -374,6 +383,16 @@ class Application extends Module } /** + * Sets default path aliases. + */ + public function registerDefaultAliases() + { + \Yii::$aliases['@application'] = $this->getBasePath(); + \Yii::$aliases['@entry'] = dirname($_SERVER['SCRIPT_FILENAME']); + \Yii::$aliases['@www'] = ''; + } + + /** * Registers the core application components. * @see setComponents */ diff --git a/framework/base/Controller.php b/framework/base/Controller.php index e3df100..ab0fb95 100644 --- a/framework/base/Controller.php +++ b/framework/base/Controller.php @@ -246,7 +246,7 @@ class Controller extends Component implements Initable * @param Action $action the action to be executed. * @return boolean whether the action is allowed to be executed. */ - public function authorize(Action $action) + public function authorize($action) { $event = new ActionEvent($action); $this->trigger(__METHOD__, $event); @@ -259,7 +259,7 @@ class Controller extends Component implements Initable * @param Action $action the action to be executed. * @return boolean whether the action should continue to be executed. */ - public function beforeAction(Action $action) + public function beforeAction($action) { $event = new ActionEvent($action); $this->trigger(__METHOD__, $event); @@ -271,31 +271,54 @@ class Controller extends Component implements Initable * You may override this method to do some postprocessing for the action. * @param Action $action the action just executed. */ - public function afterAction(Action $action) + public function afterAction($action) { - $event = new ActionEvent($action); - $this->trigger(__METHOD__, $event); + $this->trigger(__METHOD__, new ActionEvent($action)); } /** * This method is invoked right before an action renders its result using [[render()]]. - * @param Action $action the action to be executed. + * @param string $view the view to be rendered * @return boolean whether the action should continue to render. */ - public function beforeRender(Action $action) + public function beforeRender($view) { - $event = new ActionEvent($action); + $event = new RenderEvent($view); $this->trigger(__METHOD__, $event); return $event->isValid; } /** * This method is invoked right after an action renders its result using [[render()]]. - * @param Action $action the action just executed. + * @param string $view the view just rendered */ - public function afterRender(Action $action) + public function afterRender($view) { - $event = new ActionEvent($action); - $this->trigger(__METHOD__, $event); + $this->trigger(__METHOD__, new RenderEvent($view)); + } + + public function render($view, $params = array()) + { + if ($this->beforeRender($view)) { + $v = $this->createView(); + $v->render($view, $params); + $this->afterRender($view); + } + } + + public function renderText($text) + { + + } + + public function renderPartial($view, $params = array()) + { + + + } + + public function createView() + { + return new View; } } diff --git a/framework/base/RenderEvent.php b/framework/base/RenderEvent.php new file mode 100644 index 0000000..114675b --- /dev/null +++ b/framework/base/RenderEvent.php @@ -0,0 +1,39 @@ + + * @since 2.0 + */ +class RenderEvent extends Event +{ + /** + * @var Action the action currently being executed + */ + public $action; + /** + * @var boolean whether the action is in valid state and its life cycle should proceed. + */ + public $isValid = true; + + /** + * Constructor. + * @param Action $action the action associated with this action event. + */ + public function __construct(Action $action) + { + $this->action = $action; + } +} diff --git a/framework/base/Theme.php b/framework/base/Theme.php index 30a4623..a266d70 100644 --- a/framework/base/Theme.php +++ b/framework/base/Theme.php @@ -17,45 +17,42 @@ namespace yii\base; */ class Theme extends ApplicationComponent { - private $_name; - private $_basePath; - private $_baseUrl; + public $basePath; + public $baseUrl; - /** - * Constructor. - * @param string $name name of the theme - * @param string $basePath base theme path - * @param string $baseUrl base theme URL - */ - public function __construct($name, $basePath, $baseUrl) + public function init() { - $this->_name = $name; - $this->_baseUrl = $baseUrl; - $this->_basePath = $basePath; + if ($this->basePath !== null) { + $this->basePath = \Yii::getAlias($this->basePath); + } else { + throw new Exception("Theme.basePath must be set."); + } + if ($this->baseUrl !== null) { + $this->baseUrl = \Yii::getAlias($this->baseUrl); + } else { + throw new Exception("Theme.baseUrl must be set."); + } } /** - * @return string theme name + * @param Controller $controller + * @return string */ - public function getName() + public function getViewPath($controller = null) { - return $this->_name; + $path = $this->basePath . DIRECTORY_SEPARATOR . 'views'; + return $controller === null ? $path : $path . DIRECTORY_SEPARATOR . $controller->id; } - /** - * @return string the relative URL to the theme folder (without ending slash) - */ - public function getBaseUrl() + public function getLayoutPath($module = null) { - return $this->_baseUrl; + $path = $this->getViewPath($module); + return $controller === null ? $path : $path . DIRECTORY_SEPARATOR . $controller->id; } - /** - * @return string the file path to the theme folder - */ - public function getBasePath() + public function getWidgetViewPath($widget) { - return $this->_basePath; + } /** diff --git a/framework/base/ThemeManager.php b/framework/base/ThemeManager.php deleted file mode 100644 index 2259749..0000000 --- a/framework/base/ThemeManager.php +++ /dev/null @@ -1,94 +0,0 @@ - - * @since 2.0 - */ -class ThemeManager extends ApplicationComponent -{ - /** - * default themes base path - */ - const DEFAULT_BASEPATH = 'themes'; - - /** - * @var string the name of the theme class for representing a theme. - * Defaults to {@link Theme}. This can also be a class name in dot syntax. - */ - public $themeClass = 'Theme'; - /** - * @var string the base path containing all themes. Defaults to '@entry/themes'. - */ - public $basePath = '@entry/themes'; - /** - * @var string the base URL for all themes. Defaults to "@www/themes". - */ - public $baseUrl = '@www/themes'; - - - /** - * @param string $name name of the theme to be retrieved - * @return Theme the theme retrieved. Null if the theme does not exist. - */ - public function getTheme($name) - { - $themePath = $this->getBasePath() . DIRECTORY_SEPARATOR . $name; - if (is_dir($themePath)) { - $class = Yii::import($this->themeClass, true); - return new $class($name, $themePath, $this->getBaseUrl() . '/' . $name); - } else { - return null; - } - } - - /** - * @return array list of available theme names - */ - public function getThemeNames() - { - static $themes; - if ($themes === null) { - $themes = array(); - $basePath = $this->getBasePath(); - $folder = @opendir($basePath); - while (($file = @readdir($folder)) !== false) { - if ($file !== '.' && $file !== '..' && $file !== '.svn' && $file !== '.gitignore' && is_dir($basePath . DIRECTORY_SEPARATOR . $file)) { - $themes[] = $file; - } - } - closedir($folder); - sort($themes); - } - return $themes; - } -} diff --git a/framework/base/View.php b/framework/base/View.php index c1b4bf6..46ef9d4 100644 --- a/framework/base/View.php +++ b/framework/base/View.php @@ -25,7 +25,7 @@ class View extends Component * @var string|array the base path where the view file should be looked for using the specified view name. * This can be either a string representing a single base path, or an array representing multiple base paths. * If the latter, the view file will be looked for in the given base paths in the order they are specified. - * This property must be set before calling [[render()]]. + * Path aliases can be used. This property must be set before calling [[render()]]. */ public $basePath; /** @@ -250,8 +250,6 @@ class View extends Component */ public function findViewFile($view) { - $view = ltrim($view, '/'); - if (($extension = FileHelper::getExtension($view)) === '') { $view .= '.php'; } @@ -260,7 +258,7 @@ class View extends Component } elseif (!empty($this->basePath)) { $basePaths = is_array($this->basePath) ? $this->basePath : array($this->basePath); foreach ($basePaths as $basePath) { - $file = $basePath . DIRECTORY_SEPARATOR . $view; + $file = \Yii::getAlias($basePath . DIRECTORY_SEPARATOR . $view); if (is_file($file)) { break; } diff --git a/framework/base/Widget.php b/framework/base/Widget.php index 4f1a92e..f2ec630 100644 --- a/framework/base/Widget.php +++ b/framework/base/Widget.php @@ -18,11 +18,6 @@ namespace yii\base; class Widget extends Component implements Initable { /** - * @var mixed the name of the skin to be used by this widget. Defaults to 'default'. - * If this is set as false, no skin will be applied to this widget. - */ - public $skin = 'default'; - /** * @var Widget|Controller the owner/creator of this widget. It could be either a widget or a controller. */ public $owner; diff --git a/framework/caching/ApcCache.php b/framework/caching/ApcCache.php new file mode 100644 index 0000000..430269f --- /dev/null +++ b/framework/caching/ApcCache.php @@ -0,0 +1,109 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CApcCache provides APC caching in terms of an application component. + * + * The caching is based on {@link http://www.php.net/apc APC}. + * To use this application component, the APC PHP extension must be loaded. + * + * See {@link CCache} manual for common cache operations that are supported by CApcCache. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching + * @since 1.0 + */ +class CApcCache extends CCache +{ + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of memcache. + * @throws CException if APC cache extension is not loaded or is disabled. + */ + public function init() + { + parent::init(); + if(!extension_loaded('apc')) + throw new CException(Yii::t('yii','CApcCache requires PHP apc extension to be loaded.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return apc_fetch($key); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * @param array $keys a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + */ + protected function getValues($keys) + { + return apc_fetch($keys); + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return apc_store($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return apc_add($key,$value,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return apc_delete($key); + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + * @since 1.1.5 + */ + protected function flushValues() + { + return apc_clear_cache('user'); + } +} diff --git a/framework/caching/Cache.php b/framework/caching/Cache.php new file mode 100644 index 0000000..8de8add --- /dev/null +++ b/framework/caching/Cache.php @@ -0,0 +1,399 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCache is the base class for cache classes with different cache storage implementation. + * + * A data item can be stored in cache by calling {@link set} and be retrieved back + * later by {@link get}. In both operations, a key identifying the data item is required. + * An expiration time and/or a dependency can also be specified when calling {@link set}. + * If the data item expires or the dependency changes, calling {@link get} will not + * return back the data item. + * + * Note, by definition, cache does not ensure the existence of a value + * even if it does not expire. Cache is not meant to be a persistent storage. + * + * CCache implements the interface {@link ICache} with the following methods: + * + * + * Child classes must implement the following methods: + * + * + * CCache also implements ArrayAccess so that it can be used like an array. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching + * @since 1.0 + */ +abstract class CCache extends CApplicationComponent implements ICache, ArrayAccess +{ + /** + * @var string a string prefixed to every cache key so that it is unique. Defaults to null which means + * to use the {@link CApplication::getId() application ID}. If different applications need to access the same + * pool of cached data, the same prefix should be set for each of the applications explicitly. + */ + public $keyPrefix; + + /** + * @var boolean whether to md5-hash the cache key for normalization purposes. Defaults to true. Setting this property to false makes sure the cache + * key will not be tampered when calling the relevant methods {@link get()}, {@link set()}, {@link add()} and {@link delete()}. This is useful if a Yii + * application as well as an external application need to access the same cache pool (also see description of {@link keyPrefix} regarding this use case). + * However, without normalization you should make sure the affected cache backend does support the structure (charset, length, etc.) of all the provided + * cache keys, otherwise there might be unexpected behavior. + * @since 1.1.11 + **/ + public $hashKey=true; + + /** + * @var boolean whether to automatically serialize/unserialize the cache values. Defaults to true. Setting this property to false makes sure the cache + * value will not be tampered when calling the methods {@link set()} and {@link add()}. This is useful in case you want to store data which simply + * does not require serialization (e.g. integers, strings or raw binary data). Thus there might be a small increase in performance and a smaller overall + * cache size. Take in mind that you will be unable to store PHP structures like arrays or objects, you would have to serialize and unserialize them manually. + * Another negative side effect is that providing a dependency via {@link set()} or {@link add()} will have no effect since dependencies rely on serialization. + * Since all the relevant core application components rely on dependency support, you should be very careful disabling this feature. Usually you want to + * configure a dedicated cache component for the sole purpose of storing raw unserialized data, the main cache component should always support serialization. + * @since 1.1.11 + **/ + public $autoSerialize=true; + + /** + * @var boolean wether to make use of the {@link http://pecl.php.net/package/igbinary igbinary} serializer for cache entry serialization. Defaults to false. + * NOTE: If this is set to true while the igbinary extension has not been loaded, cache serialization will silently fall back to PHP's default + * serializer. Since the two serialization formats are incompatible, caches should be purged before switching this on to prevent errors. + * @since 1.1.11 + */ + public $useIgbinarySerializer=false; + + /** + * Initializes the application component. + * This method overrides the parent implementation by setting default cache key prefix. + */ + public function init() + { + parent::init(); + if($this->keyPrefix===null) + $this->keyPrefix=Yii::app()->getId(); + $this->useIgbinarySerializer=$this->useIgbinarySerializer&&extension_loaded('igbinary'); + } + + /** + * @param string $key a key identifying a value to be cached + * @return sring a key generated from the provided key which ensures the uniqueness across applications + */ + protected function generateUniqueKey($key) + { + return $this->hashKey ? md5($this->keyPrefix.$key) : $this->keyPrefix.$key; + } + + /** + * Retrieves a value from cache with a specified key. + * @param string $id a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache, expired or the dependency has changed. + */ + public function get($id) + { + if(($value=$this->getValue($this->generateUniqueKey($id)))!==false) + { + $data=$this->autoSerialize ? $this->unserializeValue($value) : $value; + if(!$this->autoSerialize || (is_array($data) && (!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged()))) + { + Yii::trace('Serving "'.$id.'" from cache','system.caching.'.get_class($this)); + return $this->autoSerialize ? $data[0] : $data; + } + } + return false; + } + + /** + * Retrieves multiple values from cache with the specified keys. + * Some caches (such as memcache, apc) allow retrieving multiple cached values at one time, + * which may improve the performance since it reduces the communication cost. + * In case a cache doesn't support this feature natively, it will be simulated by this method. + * @param array $ids list of keys identifying the cached values + * @return array list of cached values corresponding to the specified keys. The array + * is returned in terms of (key,value) pairs. + * If a value is not cached or expired, the corresponding array value will be false. + */ + public function mget($ids) + { + $uniqueIDs=array(); + $results=array(); + foreach($ids as $id) + { + $uniqueIDs[$id]=$this->generateUniqueKey($id); + $results[$id]=false; + } + $values=$this->getValues($uniqueIDs); + foreach($uniqueIDs as $id=>$uniqueID) + { + if(!isset($values[$uniqueID])) + continue; + $data=$this->autoSerialize ? $this->unserializeValue($values[$uniqueID]) : $values[$uniqueID]; + if(!$this->autoSerialize || (is_array($data) && (!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged()))) + { + Yii::trace('Serving "'.$id.'" from cache','system.caching.'.get_class($this)); + $results[$id]=$this->autoSerialize ? $data[0] : $data; + } + } + return $results; + } + + /** + * 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 $id the key identifying the value to be cached + * @param mixed $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0,$dependency=null) + { + Yii::trace('Saving "'.$id.'" to cache','system.caching.'.get_class($this)); + if($dependency!==null && $this->autoSerialize) + $dependency->evaluateDependency(); + $data=$this->autoSerialize ? $this->serializeValue(array($value,$dependency)) : $value; + return $this->setValue($this->generateUniqueKey($id),$data,$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 $id the key identifying the value to be cached + * @param mixed $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0,$dependency=null) + { + Yii::trace('Adding "'.$id.'" to cache','system.caching.'.get_class($this)); + if($dependency!==null && $this->autoSerialize) + $dependency->evaluateDependency(); + $data=$this->autoSerialize ? $this->serializeValue(array($value,$dependency)) : $value; + return $this->addValue($this->generateUniqueKey($id),$data,$expire); + } + + /** + * Deletes a value with the specified key from cache + * @param string $id the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($id) + { + Yii::trace('Deleting "'.$id.'" from cache','system.caching.'.get_class($this)); + return $this->deleteValue($this->generateUniqueKey($id)); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + * @return boolean whether the flush operation was successful. + */ + public function flush() + { + Yii::trace('Flushing cache','system.caching.'.get_class($this)); + return $this->flushValues(); + } + + /** + * Retrieves a value from cache with a specified key. + * This method should be implemented by child classes to retrieve the data + * from specific cache storage. The uniqueness and dependency are handled + * in {@link get()} already. So only the implementation of data retrieval + * is needed. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + * @throws CException if this method is not overridden by child classes + */ + protected function getValue($key) + { + throw new CException(Yii::t('yii','{className} does not support get() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * The default implementation simply calls {@link getValue} multiple + * times to retrieve the cached values one by one. + * If the underlying cache storage supports multiget, this method should + * be overridden to exploit that feature. + * @param array $keys a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + */ + protected function getValues($keys) + { + $results=array(); + foreach($keys as $key) + $results[$key]=$this->getValue($key); + return $results; + } + + /** + * Stores a value identified by a key in cache. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link set()} already. So only the implementation of data storage + * is needed. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + * @throws CException if this method is not overridden by child classes + */ + protected function setValue($key,$value,$expire) + { + throw new CException(Yii::t('yii','{className} does not support set() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link add()} already. So only the implementation of data storage + * is needed. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + * @throws CException if this method is not overridden by child classes + */ + protected function addValue($key,$value,$expire) + { + throw new CException(Yii::t('yii','{className} does not support add() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Deletes a value with the specified key from cache + * This method should be implemented by child classes to delete the data from actual cache storage. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + * @throws CException if this method is not overridden by child classes + */ + protected function deleteValue($key) + { + throw new CException(Yii::t('yii','{className} does not support delete() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Deletes all values from cache. + * Child classes may implement this method to realize the flush operation. + * @return boolean whether the flush operation was successful. + * @throws CException if this method is not overridden by child classes + * @since 1.1.5 + */ + protected function flushValues() + { + throw new CException(Yii::t('yii','{className} does not support flushValues() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Serializes the value before it will be stored in the actual cache backend. + * This method will be called if {@link autoSerialize} is set to true. Child classes may override this method to change + * the way the value is being serialized. The default implementation simply makes use of the PHP serialize() function + * unless {@link useIgbinarySerializer} is set to true and the igbinary extension is installed. + * Make sure to override {@link unserializeValue()} as well if you want to change the serialization process. + * @param mixed $value the unserialized representation of the value + * @return string the serialized representation of the value + * @since 1.1.11 + **/ + protected function serializeValue($value) + { + if($this->useIgbinarySerializer) + return igbinary_serialize($value); + return serialize($value); + } + + /** + * Unserializes the value after it was retrieved from the actual cache backend. + * This method will be called if {@link autoSerialize} is set to true. Child classes may override this method to change + * the way the value is being unserialized. The default implementation simply makes use of the PHP unserialize() function + * unless {@link useIgbinarySerializer} is set to true and the igbinary extension is installed. + * Make sure to override {@link serializeValue()} as well if you want to change the serialization process. + * @param string $value the serialized representation of the value + * @return mixed the unserialized representation of the value + * @since 1.1.11 + **/ + protected function unserializeValue($value) + { + if($this->useIgbinarySerializer) + return igbinary_unserialize($value); + return unserialize($value); + } + + /** + * Returns whether there is a cache entry with a specified key. + * This method is required by the interface ArrayAccess. + * @param string $id a key identifying the cached value + * @return boolean + */ + public function offsetExists($id) + { + return $this->get($id)!==false; + } + + /** + * Retrieves the value from cache with a specified key. + * This method is required by the interface ArrayAccess. + * @param string $id a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function offsetGet($id) + { + return $this->get($id); + } + + /** + * Stores the value identified by a key into cache. + * If the cache already contains such a key, the existing value will be + * replaced with the new ones. To add expiration and dependencies, use the set() method. + * This method is required by the interface ArrayAccess. + * @param string $id the key identifying the value to be cached + * @param mixed $value the value to be cached + */ + public function offsetSet($id, $value) + { + $this->set($id, $value); + } + + /** + * Deletes the value with the specified key from cache + * This method is required by the interface ArrayAccess. + * @param string $id the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function offsetUnset($id) + { + $this->delete($id); + } +} \ No newline at end of file diff --git a/framework/caching/ChainedDependency.php b/framework/caching/ChainedDependency.php new file mode 100644 index 0000000..4719542 --- /dev/null +++ b/framework/caching/ChainedDependency.php @@ -0,0 +1,98 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CChainedCacheDependency represents a list of cache dependencies. + * + * If any of the dependencies reports a dependency change, CChainedCacheDependency + * will return true for the checking. + * + * To add dependencies to CChainedCacheDependency, use {@link getDependencies Dependencies} + * which gives a {@link CTypedList} instance and can be used like an array + * (see {@link CList} for more details}). + * + * @property CTypedList $dependencies List of dependency objects. + * @property boolean $hasChanged Whether the dependency is changed or not. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching.dependencies + * @since 1.0 + */ +class CChainedCacheDependency extends CComponent implements ICacheDependency +{ + private $_dependencies=null; + + /** + * Constructor. + * @param array $dependencies the dependencies to be added to this chain. + * @since 1.1.4 + */ + public function __construct($dependencies=array()) + { + if(!empty($dependencies)) + $this->setDependencies($dependencies); + } + + /** + * @return CTypedList list of dependency objects + */ + public function getDependencies() + { + if($this->_dependencies===null) + $this->_dependencies=new CTypedList('ICacheDependency'); + return $this->_dependencies; + } + + /** + * @param array $values list of dependency objects or configurations to be added to this chain. + * If a depedency is specified as a configuration, it must be an array that can be recognized + * by {@link YiiBase::createComponent}. + */ + public function setDependencies($values) + { + $dependencies=$this->getDependencies(); + foreach($values as $value) + { + if(is_array($value)) + $value=Yii::createComponent($value); + $dependencies->add($value); + } + } + + /** + * Evaluates the dependency by generating and saving the data related with dependency. + */ + public function evaluateDependency() + { + if($this->_dependencies!==null) + { + foreach($this->_dependencies as $dependency) + $dependency->evaluateDependency(); + } + } + + /** + * Performs the actual dependency checking. + * This method returns true if any of the dependency objects + * reports a dependency change. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + if($this->_dependencies!==null) + { + foreach($this->_dependencies as $dependency) + if($dependency->getHasChanged()) + return true; + } + return false; + } +} diff --git a/framework/caching/DbCache.php b/framework/caching/DbCache.php new file mode 100644 index 0000000..ea886ee --- /dev/null +++ b/framework/caching/DbCache.php @@ -0,0 +1,314 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbCache implements a cache application component by storing cached data in a database. + * + * CDbCache stores cache data in a DB table named {@link cacheTableName}. + * If the table does not exist, it will be automatically created. + * By setting {@link autoCreateCacheTable} to false, you can also manually create the DB table. + * + * CDbCache relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to access database. + * By default, it will use a SQLite3 database under the application runtime directory. + * You can also specify {@link connectionID} so that it makes use of + * a DB application component to access database. + * + * See {@link CCache} manual for common cache operations that are supported by CDbCache. + * + * @property integer $gCProbability The probability (parts per million) that garbage collection (GC) should be performed + * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. + * @property CDbConnection $dbConnection The DB connection instance. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching + * @since 1.0 + */ +class CDbCache extends CCache +{ + /** + * @var string the ID of the {@link CDbConnection} application component. If not set, + * a SQLite3 database will be automatically created and used. The SQLite database file + * is protected/runtime/cache-YiiVersion.db. + */ + public $connectionID; + /** + * @var string name of the DB table to store cache content. Defaults to 'YiiCache'. + * Note, if {@link autoCreateCacheTable} is false and you want to create the DB table + * manually by yourself, you need to make sure the DB table is of the following structure: + *
+	 * (id CHAR(128) PRIMARY KEY, expire INTEGER, value BLOB)
+	 * 
+ * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable + * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.) + * @see autoCreateCacheTable + */ + public $cacheTableName='YiiCache'; + /** + * @var boolean whether the cache DB table should be created automatically if it does not exist. Defaults to true. + * If you already have the table created, it is recommended you set this property to be false to improve performance. + * @see cacheTableName + */ + public $autoCreateCacheTable=true; + /** + * @var CDbConnection the DB connection instance + */ + private $_db; + private $_gcProbability=100; + private $_gced=false; + + /** + * Initializes this application component. + * + * This method is required by the {@link IApplicationComponent} interface. + * It ensures the existence of the cache DB table. + * It also removes expired data items from the cache. + */ + public function init() + { + parent::init(); + + $db=$this->getDbConnection(); + $db->setActive(true); + if($this->autoCreateCacheTable) + { + $sql="DELETE FROM {$this->cacheTableName} WHERE expire>0 AND expire<".time(); + try + { + $db->createCommand($sql)->execute(); + } + catch(Exception $e) + { + $this->createCacheTable($db,$this->cacheTableName); + } + } + } + + /** + * @return integer the probability (parts per million) that garbage collection (GC) should be performed + * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. + */ + public function getGCProbability() + { + return $this->_gcProbability; + } + + /** + * @param integer $value the probability (parts per million) that garbage collection (GC) should be performed + * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. + * This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all. + */ + public function setGCProbability($value) + { + $value=(int)$value; + if($value<0) + $value=0; + if($value>1000000) + $value=1000000; + $this->_gcProbability=$value; + } + + /** + * Creates the cache DB table. + * @param CDbConnection $db the database connection + * @param string $tableName the name of the table to be created + */ + protected function createCacheTable($db,$tableName) + { + $driver=$db->getDriverName(); + if($driver==='mysql') + $blob='LONGBLOB'; + else if($driver==='pgsql') + $blob='BYTEA'; + else + $blob='BLOB'; + $sql=<<createCommand($sql)->execute(); + } + + /** + * @return CDbConnection the DB connection instance + * @throws CException if {@link connectionID} does not point to a valid application component. + */ + public function getDbConnection() + { + if($this->_db!==null) + return $this->_db; + else if(($id=$this->connectionID)!==null) + { + if(($this->_db=Yii::app()->getComponent($id)) instanceof CDbConnection) + return $this->_db; + else + throw new CException(Yii::t('yii','CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.', + array('{id}'=>$id))); + } + else + { + $dbFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'cache-'.Yii::getVersion().'.db'; + return $this->_db=new CDbConnection('sqlite:'.$dbFile); + } + } + + /** + * Sets the DB connection used by the cache component. + * @param CDbConnection $value the DB connection instance + * @since 1.1.5 + */ + public function setDbConnection($value) + { + $this->_db=$value; + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $time=time(); + $sql="SELECT value FROM {$this->cacheTableName} WHERE id='$key' AND (expire=0 OR expire>$time)"; + $db=$this->getDbConnection(); + if($db->queryCachingDuration>0) + { + $duration=$db->queryCachingDuration; + $db->queryCachingDuration=0; + $result=$db->createCommand($sql)->queryScalar(); + $db->queryCachingDuration=$duration; + return $result; + } + else + return $db->createCommand($sql)->queryScalar(); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * @param array $keys a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + */ + protected function getValues($keys) + { + if(empty($keys)) + return array(); + + $ids=implode("','",$keys); + $time=time(); + $sql="SELECT id, value FROM {$this->cacheTableName} WHERE id IN ('$ids') AND (expire=0 OR expire>$time)"; + + $db=$this->getDbConnection(); + if($db->queryCachingDuration>0) + { + $duration=$db->queryCachingDuration; + $db->queryCachingDuration=0; + $rows=$db->createCommand($sql)->queryAll(); + $db->queryCachingDuration=$duration; + } + else + $rows=$db->createCommand($sql)->queryAll(); + + $results=array(); + foreach($keys as $key) + $results[$key]=false; + foreach($rows as $row) + $results[$row['id']]=$row['value']; + return $results; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + $this->deleteValue($key); + return $this->addValue($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + if(!$this->_gced && mt_rand(0,1000000)<$this->_gcProbability) + { + $this->gc(); + $this->_gced=true; + } + + if($expire>0) + $expire+=time(); + else + $expire=0; + $sql="INSERT INTO {$this->cacheTableName} (id,expire,value) VALUES ('$key',$expire,:value)"; + try + { + $command=$this->getDbConnection()->createCommand($sql); + $command->bindValue(':value',$value,PDO::PARAM_LOB); + $command->execute(); + return true; + } + catch(Exception $e) + { + return false; + } + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + $sql="DELETE FROM {$this->cacheTableName} WHERE id='$key'"; + $this->getDbConnection()->createCommand($sql)->execute(); + return true; + } + + /** + * Removes the expired data values. + */ + protected function gc() + { + $this->getDbConnection()->createCommand("DELETE FROM {$this->cacheTableName} WHERE expire>0 AND expire<".time())->execute(); + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + * @since 1.1.5 + */ + protected function flushValues() + { + $this->getDbConnection()->createCommand("DELETE FROM {$this->cacheTableName}")->execute(); + return true; + } +} diff --git a/framework/caching/DbDependency.php b/framework/caching/DbDependency.php new file mode 100644 index 0000000..6c92ee9 --- /dev/null +++ b/framework/caching/DbDependency.php @@ -0,0 +1,112 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbCacheDependency represents a dependency based on the query result of a SQL statement. + * + * If the query result (a scalar) changes, the dependency is considered as changed. + * To specify the SQL statement, set {@link sql} property. + * The {@link connectionID} property specifies the ID of a {@link CDbConnection} application + * component. It is this DB connection that is used to perform the query. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching.dependencies + * @since 1.0 + */ +class CDbCacheDependency extends CCacheDependency +{ + /** + * @var string the ID of a {@link CDbConnection} application component. Defaults to 'db'. + */ + public $connectionID='db'; + /** + * @var string the SQL statement whose result is used to determine if the dependency has been changed. + * Note, the SQL statement should return back a single value. + */ + public $sql; + /** + * @var array parameters (name=>value) to be bound to the SQL statement specified by {@link sql}. + * @since 1.1.4 + */ + public $params; + + private $_db; + + /** + * Constructor. + * @param string $sql the SQL statement whose result is used to determine if the dependency has been changed. + */ + public function __construct($sql=null) + { + $this->sql=$sql; + } + + /** + * PHP sleep magic method. + * This method ensures that the database instance is set null because it contains resource handles. + * @return array + */ + public function __sleep() + { + $this->_db=null; + return array_keys((array)$this); + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the value of the global state. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + if($this->sql!==null) + { + $db=$this->getDbConnection(); + $command=$db->createCommand($this->sql); + if(is_array($this->params)) + { + foreach($this->params as $name=>$value) + $command->bindValue($name,$value); + } + if($db->queryCachingDuration>0) + { + // temporarily disable and re-enable query caching + $duration=$db->queryCachingDuration; + $db->queryCachingDuration=0; + $result=$command->queryRow(); + $db->queryCachingDuration=$duration; + } + else + $result=$command->queryRow(); + return $result; + } + else + throw new CException(Yii::t('yii','CDbCacheDependency.sql cannot be empty.')); + } + + /** + * @return CDbConnection the DB connection instance + * @throws CException if {@link connectionID} does not point to a valid application component. + */ + protected function getDbConnection() + { + if($this->_db!==null) + return $this->_db; + else + { + if(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection) + return $this->_db; + else + throw new CException(Yii::t('yii','CDbCacheDependency.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.', + array('{id}'=>$this->connectionID))); + } + } +} diff --git a/framework/caching/Dependency.php b/framework/caching/Dependency.php new file mode 100644 index 0000000..a131926 --- /dev/null +++ b/framework/caching/Dependency.php @@ -0,0 +1,113 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCacheDependency is the base class for cache dependency classes. + * + * CCacheDependency implements the {@link ICacheDependency} interface. + * Child classes should override its {@link generateDependentData} for + * actual dependency checking. + * + * @property boolean $hasChanged Whether the dependency has changed. + * @property mixed $dependentData The data used to determine if dependency has been changed. + * This data is available after {@link evaluateDependency} is called. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching.dependencies + * @since 1.0 + */ +class CCacheDependency extends CComponent implements ICacheDependency +{ + /** + * @var boolean Whether this dependency is reusable or not. + * If set to true, dependent data for this cache dependency will only be generated once per request. + * You can then use the same cache dependency for multiple separate cache calls on the same page + * without the overhead of re-evaluating the dependency each time. + * Defaults to false; + * @since 1.1.11 + */ + public $reuseDependentData=false; + + /** + * @var array cached data for reusable dependencies. + * @since 1.1.11 + */ + private static $_reusableData=array(); + + private $_hash; + private $_data; + + /** + * Evaluates the dependency by generating and saving the data related with dependency. + * This method is invoked by cache before writing data into it. + */ + public function evaluateDependency() + { + if ($this->reuseDependentData) + { + $hash=$this->getHash(); + if (!isset(self::$_reusableData[$hash]['dependentData'])) + self::$_reusableData[$hash]['dependentData']=$this->generateDependentData(); + $this->_data=self::$_reusableData[$hash]['dependentData']; + } + else + $this->_data=$this->generateDependentData(); + } + + /** + * @return boolean whether the dependency has changed. + */ + public function getHasChanged() + { + if ($this->reuseDependentData) + { + $hash=$this->getHash(); + if (!isset(self::$_reusableData[$hash]['hasChanged'])) + { + if (!isset(self::$_reusableData[$hash]['dependentData'])) + self::$_reusableData[$hash]['dependentData']=$this->generateDependentData(); + self::$_reusableData[$hash]['hasChanged']=self::$_reusableData[$hash]['dependentData']!=$this->_data; + } + return self::$_reusableData[$hash]['hasChanged']; + } + else + return $this->generateDependentData()!=$this->_data; + } + + /** + * @return mixed the data used to determine if dependency has been changed. + * This data is available after {@link evaluateDependency} is called. + */ + public function getDependentData() + { + return $this->_data; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * Derived classes should override this method to generate actual dependent data. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + return null; + } + /** + * Generates a unique hash that identifies this cache dependency. + * @return string the hash for this cache dependency + */ + private function getHash() + { + if($this->_hash===null) + $this->_hash=sha1(serialize($this)); + return $this->_hash; + } +} \ No newline at end of file diff --git a/framework/caching/DirectoryDependency.php b/framework/caching/DirectoryDependency.php new file mode 100644 index 0000000..3aaa6b9 --- /dev/null +++ b/framework/caching/DirectoryDependency.php @@ -0,0 +1,134 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDirectoryCacheDependency represents a dependency based on change of a directory. + * + * CDirectoryCacheDependency performs dependency checking based on the + * modification time of the files contained in the specified directory. + * The directory being checked is specified via {@link directory}. + * + * By default, all files under the specified directory and subdirectories + * will be checked. If the last modification time of any of them is changed + * or if different number of files are contained in a directory, the dependency + * is reported as changed. By specifying {@link recursiveLevel}, + * one can limit the checking to a certain depth of the directory. + * + * Note, dependency checking for a directory is expensive because it involves + * accessing modification time of multiple files under the directory. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching.dependencies + * @since 1.0 + */ +class CDirectoryCacheDependency extends CCacheDependency +{ + /** + * @var string the directory whose change is used to determine if the dependency has been changed. + * If any of the files under the directory is changed, the dependency is considered as changed. + */ + public $directory; + /** + * @var integer the depth of the subdirectories to be recursively checked. + * If the value is less than 0, it means unlimited depth. + * If the value is 0, it means checking the files directly under the specified directory. + */ + public $recursiveLevel=-1; + /** + * @var string the regular expression matching valid file/directory names. + * Only the matching files or directories will be checked for changes. + * Defaults to null, meaning all files/directories will qualify. + */ + public $namePattern; + + /** + * Constructor. + * @param string $directory the directory to be checked + */ + public function __construct($directory=null) + { + $this->directory=$directory; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the modification timestamps for files under the directory. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + if($this->directory!==null) + return $this->generateTimestamps($this->directory); + else + throw new CException(Yii::t('yii','CDirectoryCacheDependency.directory cannot be empty.')); + } + + /** + * Determines the last modification time for files under the directory. + * This method may go recursively into subdirectories if {@link recursiveLevel} is not 0. + * @param string $directory the directory name + * @param integer $level level of the recursion + * @return array list of file modification time indexed by the file path + */ + protected function generateTimestamps($directory,$level=0) + { + if(($dir=@opendir($directory))===false) + throw new CException(Yii::t('yii','"{path}" is not a valid directory.', + array('{path}'=>$directory))); + $timestamps=array(); + while(($file=readdir($dir))!==false) + { + $path=$directory.DIRECTORY_SEPARATOR.$file; + if($file==='.' || $file==='..') + continue; + if($this->namePattern!==null && !preg_match($this->namePattern,$file)) + continue; + if(is_file($path)) + { + if($this->validateFile($path)) + $timestamps[$path]=filemtime($path); + } + else + { + if(($this->recursiveLevel<0 || $level<$this->recursiveLevel) && $this->validateDirectory($path)) + $timestamps=array_merge($timestamps, $this->generateTimestamps($path,$level+1)); + } + } + closedir($dir); + return $timestamps; + } + + /** + * Checks to see if the file should be checked for dependency. + * This method is invoked when dependency of the whole directory is being checked. + * By default, it always returns true, meaning the file should be checked. + * You may override this method to check only certain files. + * @param string $fileName the name of the file that may be checked for dependency. + * @return boolean whether this file should be checked. + */ + protected function validateFile($fileName) + { + return true; + } + + /** + * Checks to see if the specified subdirectory should be checked for dependency. + * This method is invoked when dependency of the whole directory is being checked. + * By default, it always returns true, meaning the subdirectory should be checked. + * You may override this method to check only certain subdirectories. + * @param string $directory the name of the subdirectory that may be checked for dependency. + * @return boolean whether this subdirectory should be checked. + */ + protected function validateDirectory($directory) + { + return true; + } +} diff --git a/framework/caching/DummyCache.php b/framework/caching/DummyCache.php new file mode 100644 index 0000000..f78bcf3 --- /dev/null +++ b/framework/caching/DummyCache.php @@ -0,0 +1,164 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDummyCache is a placeholder cache component. + * + * CDummyCache does not cache anything. It is provided so that one can always configure + * a 'cache' application component and he does not need to check if Yii::app()->cache is null or not. + * By replacing CDummyCache with some other cache component, one can quickly switch from + * non-caching mode to caching mode. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching + * @since 1.0 + */ +class CDummyCache extends CApplicationComponent implements ICache, ArrayAccess +{ + /** + * @var string a string prefixed to every cache key so that it is unique. Defaults to {@link CApplication::getId() application ID}. + */ + public $keyPrefix; + + /** + * Initializes the application component. + * This method overrides the parent implementation by setting default cache key prefix. + */ + public function init() + { + parent::init(); + if($this->keyPrefix===null) + $this->keyPrefix=Yii::app()->getId(); + } + + /** + * Retrieves a value from cache with a specified key. + * @param string $id a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache, expired or the dependency has changed. + */ + public function get($id) + { + return false; + } + + /** + * Retrieves multiple values from cache with the specified keys. + * Some caches (such as memcache, apc) allow retrieving multiple cached values at one time, + * which may improve the performance since it reduces the communication cost. + * In case a cache doesn't support this feature natively, it will be simulated by this method. + * @param array $ids list of keys identifying the cached values + * @return array list of cached values corresponding to the specified keys. The array + * is returned in terms of (key,value) pairs. + * If a value is not cached or expired, the corresponding array value will be false. + */ + public function mget($ids) + { + $results=array(); + foreach($ids as $id) + $results[$id]=false; + return $results; + } + + /** + * 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 $id the key identifying the value to be cached + * @param mixed $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0,$dependency=null) + { + return true; + } + + /** + * 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 $id the key identifying the value to be cached + * @param mixed $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0,$dependency=null) + { + return true; + } + + /** + * Deletes a value with the specified key from cache + * @param string $id the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($id) + { + return true; + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + * @return boolean whether the flush operation was successful. + * @throws CException if this method is not overridden by child classes + */ + public function flush() + { + return true; + } + + /** + * Returns whether there is a cache entry with a specified key. + * This method is required by the interface ArrayAccess. + * @param string $id a key identifying the cached value + * @return boolean + */ + public function offsetExists($id) + { + return false; + } + + /** + * Retrieves the value from cache with a specified key. + * This method is required by the interface ArrayAccess. + * @param string $id a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function offsetGet($id) + { + return false; + } + + /** + * Stores the value identified by a key into cache. + * If the cache already contains such a key, the existing value will be + * replaced with the new ones. To add expiration and dependencies, use the set() method. + * This method is required by the interface ArrayAccess. + * @param string $id the key identifying the value to be cached + * @param mixed $value the value to be cached + */ + public function offsetSet($id, $value) + { + } + + /** + * Deletes the value with the specified key from cache + * This method is required by the interface ArrayAccess. + * @param string $id the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function offsetUnset($id) + { + } +} diff --git a/framework/caching/EAcceleratorCache.php b/framework/caching/EAcceleratorCache.php new file mode 100644 index 0000000..99be8be --- /dev/null +++ b/framework/caching/EAcceleratorCache.php @@ -0,0 +1,107 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CEAcceleratorCache implements a cache application module based on {@link http://eaccelerator.net/ eaccelerator}. + * + * To use this application component, the eAccelerator PHP extension must be loaded. + * + * See {@link CCache} manual for common cache operations that are supported by CEAccelerator. + * + * Please note that as of v0.9.6, eAccelerator no longer supports data caching. + * This means if you still want to use this component, your eAccelerator should be of 0.9.5.x or lower version. + * + * @author Steffen Dietz + * @version $Id$ + * @package system.caching + */ +class CEAcceleratorCache extends CCache +{ + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of eAccelerator. + * @throws CException if eAccelerator extension is not loaded, is disabled or the cache functions are not compiled in. + */ + public function init() + { + parent::init(); + if(!function_exists('eaccelerator_get')) + throw new CException(Yii::t('yii','CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $result = eaccelerator_get($key); + return $result !== NULL ? $result : false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return eaccelerator_put($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return (NULL === eaccelerator_get($key)) ? $this->setValue($key,$value,$expire) : false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return eaccelerator_rm($key); + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + * @since 1.1.5 + */ + protected function flushValues() + { + // first, remove expired content from cache + eaccelerator_gc(); + // now, remove leftover cache-keys + $keys = eaccelerator_list_keys(); + foreach($keys as $key) + $this->deleteValue(substr($key['name'], 1)); + return true; + } +} diff --git a/framework/caching/ExpressionDependency.php b/framework/caching/ExpressionDependency.php new file mode 100644 index 0000000..9771b68 --- /dev/null +++ b/framework/caching/ExpressionDependency.php @@ -0,0 +1,53 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CExpressionDependency represents a dependency based on the result of a PHP expression. + * + * CExpressionDependency performs dependency checking based on the + * result of a PHP {@link expression}. + * The dependency is reported as unchanged if and only if the result is + * the same as the one evaluated when storing the data to cache. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching.dependencies + * @since 1.0 + */ +class CExpressionDependency extends CCacheDependency +{ + /** + * @var string the PHP expression whose result is used to determine the dependency. + * The expression can also be a valid PHP callback, + * including class method name (array(ClassName/Object, MethodName)), + * or anonymous function (PHP 5.3.0+). The function/method will be passed with a + * parameter which is the dependency object itself. + */ + public $expression; + + /** + * Constructor. + * @param string $expression the PHP expression whose result is used to determine the dependency. + */ + public function __construct($expression='true') + { + $this->expression=$expression; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the result of the PHP expression. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + return $this->evaluateExpression($this->expression); + } +} diff --git a/framework/caching/FileCache.php b/framework/caching/FileCache.php new file mode 100644 index 0000000..19af687 --- /dev/null +++ b/framework/caching/FileCache.php @@ -0,0 +1,222 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFileCache provides a file-based caching mechanism. + * + * For each data value being cached, CFileCache will use store it in a separate file + * under {@link cachePath} which defaults to 'protected/runtime/cache'. + * CFileCache will perform garbage collection automatically to remove expired cache files. + * + * See {@link CCache} manual for common cache operations that are supported by CFileCache. + * + * @property integer $gCProbability The probability (parts per million) that garbage collection (GC) should be performed + * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching + */ +class CFileCache extends CCache +{ + /** + * @var string the directory to store cache files. Defaults to null, meaning + * using 'protected/runtime/cache' as the directory. + */ + public $cachePath; + /** + * @var string cache file suffix. Defaults to '.bin'. + */ + public $cacheFileSuffix='.bin'; + /** + * @var integer the level of sub-directories to store cache files. Defaults to 0, + * meaning no sub-directories. If the system has huge number of cache files (e.g. 10K+), + * you may want to set this value to be 1 or 2 so that the file system is not over burdened. + * The value of this property should not exceed 16 (less than 3 is recommended). + */ + public $directoryLevel=0; + + private $_gcProbability=100; + private $_gced=false; + + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of memcache. + * @throws CException if APC cache extension is not loaded or is disabled. + */ + public function init() + { + parent::init(); + if($this->cachePath===null) + $this->cachePath=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'cache'; + if(!is_dir($this->cachePath)) + mkdir($this->cachePath,0777,true); + } + + /** + * @return integer the probability (parts per million) that garbage collection (GC) should be performed + * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. + */ + public function getGCProbability() + { + return $this->_gcProbability; + } + + /** + * @param integer $value the probability (parts per million) that garbage collection (GC) should be performed + * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. + * This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all. + */ + public function setGCProbability($value) + { + $value=(int)$value; + if($value<0) + $value=0; + if($value>1000000) + $value=1000000; + $this->_gcProbability=$value; + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + * @since 1.1.5 + */ + protected function flushValues() + { + $this->gc(false); + return true; + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $cacheFile=$this->getCacheFile($key); + if(($time=@filemtime($cacheFile))>time()) + return @file_get_contents($cacheFile); + else if($time>0) + @unlink($cacheFile); + return false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + if(!$this->_gced && mt_rand(0,1000000)<$this->_gcProbability) + { + $this->gc(); + $this->_gced=true; + } + + if($expire<=0) + $expire=31536000; // 1 year + $expire+=time(); + + $cacheFile=$this->getCacheFile($key); + if($this->directoryLevel>0) + @mkdir(dirname($cacheFile),0777,true); + if(@file_put_contents($cacheFile,$value,LOCK_EX)!==false) + { + @chmod($cacheFile,0777); + return @touch($cacheFile,$expire); + } + else + return false; + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + $cacheFile=$this->getCacheFile($key); + if(@filemtime($cacheFile)>time()) + return false; + return $this->setValue($key,$value,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + $cacheFile=$this->getCacheFile($key); + return @unlink($cacheFile); + } + + /** + * Returns the cache file path given the cache key. + * @param string $key cache key + * @return string the cache file path + */ + protected function getCacheFile($key) + { + if($this->directoryLevel>0) + { + $base=$this->cachePath; + for($i=0;$i<$this->directoryLevel;++$i) + { + if(($prefix=substr($key,$i+$i,2))!==false) + $base.=DIRECTORY_SEPARATOR.$prefix; + } + return $base.DIRECTORY_SEPARATOR.$key.$this->cacheFileSuffix; + } + else + return $this->cachePath.DIRECTORY_SEPARATOR.$key.$this->cacheFileSuffix; + } + + /** + * Removes expired cache files. + * @param boolean $expiredOnly whether to removed expired cache files only. If true, all cache files under {@link cachePath} will be removed. + * @param string $path the path to clean with. If null, it will be {@link cachePath}. + */ + public function gc($expiredOnly=true,$path=null) + { + if($path===null) + $path=$this->cachePath; + if(($handle=opendir($path))===false) + return; + while(($file=readdir($handle))!==false) + { + if($file[0]==='.') + continue; + $fullPath=$path.DIRECTORY_SEPARATOR.$file; + if(is_dir($fullPath)) + $this->gc($expiredOnly,$fullPath); + else if($expiredOnly && @filemtime($fullPath) + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFileCacheDependency represents a dependency based on a file's last modification time. + * + * CFileCacheDependency performs dependency checking based on the + * last modification time of the file specified via {@link fileName}. + * The dependency is reported as unchanged if and only if the file's + * last modification time remains unchanged. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching.dependencies + * @since 1.0 + */ +class CFileCacheDependency extends CCacheDependency +{ + /** + * @var string the name of the file whose last modification time is used to + * check if the dependency has been changed. + */ + public $fileName; + + /** + * Constructor. + * @param string $fileName name of the file whose change is to be checked. + */ + public function __construct($fileName=null) + { + $this->fileName=$fileName; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the file's last modification time. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + if($this->fileName!==null) + return @filemtime($this->fileName); + else + throw new CException(Yii::t('yii','CFileCacheDependency.fileName cannot be empty.')); + } +} diff --git a/framework/caching/MemCache.php b/framework/caching/MemCache.php new file mode 100644 index 0000000..0e1eb12 --- /dev/null +++ b/framework/caching/MemCache.php @@ -0,0 +1,282 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMemCache implements a cache application component based on {@link http://memcached.org/ memcached}. + * + * CMemCache can be configured with a list of memcache servers by settings + * its {@link setServers servers} property. By default, CMemCache assumes + * there is a memcache server running on localhost at port 11211. + * + * See {@link CCache} manual for common cache operations that are supported by CMemCache. + * + * 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 CMemCache as the cache application component, configure the application as follows, + *
+ * array(
+ *     'components'=>array(
+ *         'cache'=>array(
+ *             'class'=>'CMemCache',
+ *             'servers'=>array(
+ *                 array(
+ *                     'host'=>'server1',
+ *                     'port'=>11211,
+ *                     'weight'=>60,
+ *                 ),
+ *                 array(
+ *                     'host'=>'server2',
+ *                     'port'=>11211,
+ *                     'weight'=>40,
+ *                 ),
+ *             ),
+ *         ),
+ *     ),
+ * )
+ * 
+ * In the above, two memcache servers are used: server1 and server2. + * You can configure more properties of every server, including: + * host, port, persistent, weight, timeout, retryInterval, status. + * See {@link http://www.php.net/manual/en/function.memcache-addserver.php} + * for more details. + * + * CMemCache can also be used with {@link http://pecl.php.net/package/memcached memcached}. + * To do so, set {@link useMemcached} to be true. + * + * @property mixed $memCache The memcache instance (or memcached if {@link useMemcached} is true) used by this component. + * @property array $servers List of memcache server configurations. Each element is a {@link CMemCacheServerConfiguration}. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching + * @since 1.0 + */ +class CMemCache extends CCache +{ + /** + * @var boolean whether to use memcached or memcache as the underlying caching extension. + * If true {@link http://pecl.php.net/package/memcached memcached} will be used. + * If false {@link http://pecl.php.net/package/memcache memcache}. will be used. + * Defaults to false. + */ + public $useMemcached=false; + /** + * @var Memcache the Memcache instance + */ + private $_cache=null; + /** + * @var array list of memcache server configurations + */ + private $_servers=array(); + + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It creates the memcache instance and adds memcache servers. + * @throws CException if memcache extension is not loaded + */ + public function init() + { + parent::init(); + $servers=$this->getServers(); + $cache=$this->getMemCache(); + if(count($servers)) + { + foreach($servers as $server) + { + if($this->useMemcached) + $cache->addServer($server->host,$server->port,$server->weight); + else + $cache->addServer($server->host,$server->port,$server->persistent,$server->weight,$server->timeout,$server->status); + } + } + else + $cache->addServer('localhost',11211); + } + + /** + * @throws CException if extension isn't loaded + * @return Memcache|Memcached the memcache instance (or memcached if {@link useMemcached} is true) used by this component. + */ + public function getMemCache() + { + if($this->_cache!==null) + return $this->_cache; + else + { + $extension=$this->useMemcached ? 'memcached' : 'memcache'; + if(!extension_loaded($extension)) + throw new CException(Yii::t('yii',"CMemCache requires PHP $extension extension to be loaded.")); + return $this->_cache=$this->useMemcached ? new Memcached : new Memcache; + } + } + + /** + * @return array list of memcache server configurations. Each element is a {@link CMemCacheServerConfiguration}. + */ + public function getServers() + { + return $this->_servers; + } + + /** + * @param array $config list of memcache server configurations. Each element must be an array + * with the following keys: host, port, persistent, weight, timeout, retryInterval, status. + * @see http://www.php.net/manual/en/function.Memcache-addServer.php + */ + public function setServers($config) + { + foreach($config as $c) + $this->_servers[]=new CMemCacheServerConfiguration($c); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return $this->_cache->get($key); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * @param array $keys a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + */ + protected function getValues($keys) + { + return $this->useMemcached ? $this->_cache->getMulti($keys) : $this->_cache->get($keys); + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + if($expire>0) + $expire+=time(); + else + $expire=0; + + return $this->useMemcached ? $this->_cache->set($key,$value,$expire) : $this->_cache->set($key,$value,0,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + if($expire>0) + $expire+=time(); + else + $expire=0; + + return $this->useMemcached ? $this->_cache->add($key,$value,$expire) : $this->_cache->add($key,$value,0,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return $this->_cache->delete($key, 0); + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + * @since 1.1.5 + */ + protected function flushValues() + { + return $this->_cache->flush(); + } +} + +/** + * CMemCacheServerConfiguration represents the configuration data for a single memcache server. + * + * See {@link http://www.php.net/manual/en/function.Memcache-addServer.php} + * for detailed explanation of each configuration property. + * + * @author Qiang Xue + * @version $Id$ + * @package system.caching + * @since 1.0 + */ +class CMemCacheServerConfiguration extends CComponent +{ + /** + * @var string memcache server hostname or IP address + */ + public $host; + /** + * @var integer memcache server port + */ + public $port=11211; + /** + * @var boolean whether to use a persistent connection + */ + public $persistent=true; + /** + * @var integer probability of using this server among all servers. + */ + public $weight=1; + /** + * @var integer value in seconds which will be used for connecting to the server + */ + public $timeout=15; + /** + * @var integer how often a failed server will be retried (in seconds) + */ + public $retryInterval=15; + /** + * @var boolean if the server should be flagged as online upon a failure + */ + public $status=true; + + /** + * Constructor. + * @param array $config list of memcache server configurations. + * @throws CException if the configuration is not an array + */ + public function __construct($config) + { + if(is_array($config)) + { + foreach($config as $key=>$value) + $this->$key=$value; + if($this->host===null) + throw new CException(Yii::t('yii','CMemCache server configuration must have "host" value.')); + } + else + throw new CException(Yii::t('yii','CMemCache server configuration must be an array.')); + } +} \ No newline at end of file diff --git a/framework/caching/WinCache.php b/framework/caching/WinCache.php new file mode 100644 index 0000000..8def58d --- /dev/null +++ b/framework/caching/WinCache.php @@ -0,0 +1,109 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWinCache implements a cache application component based on {@link http://www.iis.net/expand/wincacheforphp WinCache}. + * + * To use this application component, the WinCache PHP extension must be loaded. + * + * See {@link CCache} manual for common cache operations that are supported by CWinCache. + * + * @author Alexander Makarov + * @version $Id$ + * @package system.caching + * @since 1.1.2 + */ +class CWinCache extends CCache { + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of WinCache extension and WinCache user cache. + * @throws CException if WinCache extension is not loaded or user cache is disabled + */ + public function init() + { + parent::init(); + if(!extension_loaded('wincache')) + throw new CException(Yii::t('yii', 'CWinCache requires PHP wincache extension to be loaded.')); + if(!ini_get('wincache.ucenabled')) + throw new CException(Yii::t('yii', 'CWinCache user cache is disabled. Please set wincache.ucenabled to On in your php.ini.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return wincache_ucache_get($key); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * @param array $keys a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + */ + protected function getValues($keys) + { + return wincache_ucache_get($keys); + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return wincache_ucache_set($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return wincache_ucache_add($key,$value,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return wincache_ucache_delete($key); + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + * @since 1.1.5 + */ + protected function flushValues() + { + return wincache_ucache_clear(); + } +} \ No newline at end of file diff --git a/framework/caching/XCache.php b/framework/caching/XCache.php new file mode 100644 index 0000000..62cc608 --- /dev/null +++ b/framework/caching/XCache.php @@ -0,0 +1,104 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CXCache implements a cache application module based on {@link http://xcache.lighttpd.net/ xcache}. + * + * To use this application component, the XCache PHP extension must be loaded. + * Flush functionality will only work correctly if "xcache.admin.enable_auth" is set to "Off" in php.ini. + * + * See {@link CCache} manual for common cache operations that are supported by CXCache. + * + * @author Wei Zhuo + * @version $Id$ + * @package system.caching + */ +class CXCache extends CCache +{ + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of memcache. + * @throws CException if memcache extension is not loaded or is disabled. + */ + public function init() + { + parent::init(); + if(!function_exists('xcache_isset')) + throw new CException(Yii::t('yii','CXCache requires PHP XCache extension to be loaded.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return xcache_isset($key) ? xcache_get($key) : false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return xcache_set($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return !xcache_isset($key) ? $this->setValue($key,$value,$expire) : false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return xcache_unset($key); + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + * @since 1.1.5 + */ + protected function flushValues() + { + for($i=0, $max=xcache_count(XC_TYPE_VAR); $i<$max; $i++) + { + if(xcache_clear_cache(XC_TYPE_VAR, $i)===false) + return false; + } + return true; + } +} + diff --git a/framework/caching/ZendDataCache.php b/framework/caching/ZendDataCache.php new file mode 100644 index 0000000..9804717 --- /dev/null +++ b/framework/caching/ZendDataCache.php @@ -0,0 +1,99 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CZendDataCache implements a cache application module based on the Zend Data Cache + * delivered with {@link http://www.zend.com/en/products/server/ ZendServer}. + * + * To use this application component, the Zend Data Cache PHP extension must be loaded. + * + * See {@link CCache} manual for common cache operations that are supported by CZendDataCache. + * + * @author Steffen Dietz + * @version $Id$ + * @package system.caching + */ +class CZendDataCache extends CCache +{ + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of Zend Data Cache. + * @throws CException if Zend Data Cache extension is not loaded. + */ + public function init() + { + parent::init(); + if(!function_exists('zend_shm_cache_store')) + throw new CException(Yii::t('yii','CZendDataCache requires PHP Zend Data Cache extension to be loaded.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $result = zend_shm_cache_fetch($key); + return $result !== NULL ? $result : false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return zend_shm_cache_store($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return (NULL === zend_shm_cache_fetch($key)) ? $this->setValue($key,$value,$expire) : false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return zend_shm_cache_delete($key); + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + * @since 1.1.5 + */ + protected function flushValues() + { + return zend_shm_cache_clear(); + } +}