diff options
Diffstat (limited to 'engine/lib/cache.php')
| -rw-r--r-- | engine/lib/cache.php | 858 |
1 files changed, 436 insertions, 422 deletions
diff --git a/engine/lib/cache.php b/engine/lib/cache.php index 47319e708..3116c1a9b 100644 --- a/engine/lib/cache.php +++ b/engine/lib/cache.php @@ -1,439 +1,453 @@ <?php - /** - * Elgg cache - * Cache file interface for caching data. - * - * @package Elgg - * @subpackage API - * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2 - * @author Curverider Ltd <info@elgg.com> - * @copyright Curverider Ltd 2008-2009 - * @link http://elgg.org/ - */ +/** + * Elgg cache + * Cache file interface for caching data. + * + * @package Elgg.Core + * @subpackage Cache + */ + +/* Filepath Cache */ + +/** + * Returns an ElggCache object suitable for caching system information + * + * @todo Can this be done in a cleaner way? + * @todo Swap to memcache etc? + * + * @return ElggFileCache + */ +function elgg_get_system_cache() { + global $CONFIG; /** - * ElggCache The elgg cache superclass. - * This defines the interface for a cache (wherever that cache is stored). - * - * @author Curverider Ltd <info@elgg.com> - * @package Elgg - * @subpackage API + * A default filestore cache using the dataroot. */ - abstract class ElggCache implements - ArrayAccess // Override for array access - { - /** - * Variables for the cache object. - * - * @var array - */ - private $variables; - - /** - * Set the constructor. - */ - function __construct() { $this->variables = array(); } - - /** - * Set a cache variable. - * - * @param string $variable - * @param string $value - */ - public function set_variable($variable, $value) - { - if (!is_array($this->variables)) - $this->variables = array(); - - $this->variables[$variable] = $value; - } - - /** - * Get variables for this cache. - * - * @param string $variable - * @return mixed The variable or null; - */ - public function get_variable($variable) - { - if (isset($this->variables[$variable])) - return $this->variables[$variable]; - - return null; - } - - /** - * Class member get overloading, returning key using $this->load defaults. - * - * @param string $key - * @return mixed - */ - function __get($key) { return $this->load($key); } - - /** - * Class member set overloading, setting a key using $this->save defaults. - * - * @param string $key - * @param mixed $value - * @return mixed - */ - function __set($key, $value) { return $this->save($key, $value); } - - /** - * Supporting isset, using $this->load() with default values. - * - * @param string $key The name of the attribute or metadata. - * @return bool - */ - function __isset($key) { return (bool)$this->load($key); } - - /** - * Supporting unsetting of magic attributes. - * - * @param string $key The name of the attribute or metadata. - */ - function __unset($key) { return $this->delete($key); } - - /** - * Save data in a cache. - * - * @param string $key - * @param string $data - * @return bool - */ - abstract public function save($key, $data); - - /** - * Load data from the cache using a given key. - * - * @param string $key - * @param int $offset - * @param int $limit - * @return mixed The stored data or false. - */ - abstract public function load($key, $offset = 0, $limit = null); - - /** - * Invalidate a key - * - * @param string $key - * @return bool - */ - abstract public function delete($key); - - /** - * Clear out all the contents of the cache. - * - */ - abstract public function clear(); - - /** - * Add a key only if it doesn't already exist. - * Implemented simply here, if you extend this class and your caching engine provides a better way then - * override this accordingly. - * - * @param string $key - * @param string $data - * @return bool - */ - public function add($key, $data) - { - if (!isset($this[$key])) - return $this->save($key, $data); - - return false; - } - - // ARRAY ACCESS INTERFACE ////////////////////////////////////////////////////////// - function offsetSet($key, $value) - { - $this->save($key, $value); - } - - function offsetGet($key) - { - return $this->load($key); - } - - function offsetUnset($key) - { - if ( isset($this->key) ) { - unset($this->key); - } - } - - function offsetExists($offset) - { - return isset($this->$offset); - } + static $FILE_PATH_CACHE; + + if (!$FILE_PATH_CACHE) { + $FILE_PATH_CACHE = new ElggFileCache($CONFIG->dataroot . 'system_cache/'); } - - /** - * Shared memory cache description. - * Extends ElggCache with functions useful to shared memory style caches (static variables, memcache etc) - */ - abstract class ElggSharedMemoryCache extends ElggCache - { - /** - * Namespace variable used to keep various bits of the cache - * separate. - * - * @var string - */ - private $namespace; - - /** - * Set the namespace of this cache. - * This is useful for cache types (like memcache or static variables) where there is one large - * flat area of memory shared across all instances of the cache. - * - * @param string $namespace - */ - public function setNamespace($namespace = "default") { $this->namespace = $namespace; } - /** - * Get the namespace currently defined. - * - * @return string - */ - public function getNamespace() { return $this->namespace; } + + return $FILE_PATH_CACHE; +} + +/** + * Reset the system cache by deleting the caches + * + * @return void + */ +function elgg_reset_system_cache() { + $cache = elgg_get_system_cache(); + $cache->clear(); +} + +/** + * Saves a system cache. + * + * @param string $type The type or identifier of the cache + * @param string $data The data to be saved + * @return bool + */ +function elgg_save_system_cache($type, $data) { + global $CONFIG; + + if ($CONFIG->system_cache_enabled) { + $cache = elgg_get_system_cache(); + return $cache->save($type, $data); } - - /** - * ElggStaticVariableCache - * Dummy cache which stores values in a static array. Using this makes future replacements to other caching back - * ends (eg memcache) much easier. - * - * @author Curverider Ltd <info@elgg.com> - * @package Elgg - * @subpackage API - */ - class ElggStaticVariableCache extends ElggSharedMemoryCache - { - /** - * The cache. - * - * @var unknown_type - */ - private static $__cache; - - /** - * Create the variable cache. - * - * This function creates a variable cache in a static variable in memory, optionally with a given namespace (to avoid overlap). - * - * @param string $namespace The namespace for this cache to write to - note, namespaces of the same name are shared! - */ - function __construct($namespace = 'default') - { - $this->setNamespace($namespace); - $this->clear(); - } - - public function save($key, $data) - { - $namespace = $this->getNamespace(); - - ElggStaticVariableCache::$__cache[$namespace][$key] = $data; - - return true; - } - - public function load($key, $offset = 0, $limit = null) - { - $namespace = $this->getNamespace(); - - if (isset(ElggStaticVariableCache::$__cache[$namespace][$key])) - return ElggStaticVariableCache::$__cache[$namespace][$key]; - - return false; - } - - public function delete($key) - { - $namespace = $this->getNamespace(); - - unset(ElggStaticVariableCache::$__cache[$namespace][$key]); - - return true; - } - - public function clear() - { - $namespace = $this->getNamespace(); - - if (!isset(ElggStaticVariableCache::$__cache)) - ElggStaticVariableCache::$__cache = array(); - - //if (!isset(ElggStaticVariableCache::$__cache[$namespace])) - ElggStaticVariableCache::$__cache[$namespace] = array(); + + return false; +} + +/** + * Retrieve the contents of a system cache. + * + * @param string $type The type of cache to load + * @return string + */ +function elgg_load_system_cache($type) { + global $CONFIG; + + if ($CONFIG->system_cache_enabled) { + $cache = elgg_get_system_cache(); + $cached_data = $cache->load($type); + + if ($cached_data) { + return $cached_data; } } + + return NULL; +} + +/** + * Enables the system disk cache. + * + * Uses the 'system_cache_enabled' datalist with a boolean value. + * Resets the system cache. + * + * @return void + */ +function elgg_enable_system_cache() { + global $CONFIG; + + datalist_set('system_cache_enabled', 1); + $CONFIG->system_cache_enabled = 1; + elgg_reset_system_cache(); +} + +/** + * Disables the system disk cache. + * + * Uses the 'system_cache_enabled' datalist with a boolean value. + * Resets the system cache. + * + * @return void + */ +function elgg_disable_system_cache() { + global $CONFIG; + + datalist_set('system_cache_enabled', 0); + $CONFIG->system_cache_enabled = 0; + elgg_reset_system_cache(); +} + +/** @todo deprecate in Elgg 1.9 **/ + +/** + * @access private + */ +function elgg_get_filepath_cache() { + return elgg_get_system_cache(); +} +/** + * @access private + */ +function elgg_filepath_cache_reset() { + elgg_reset_system_cache(); +} +/** + * @access private + */ +function elgg_filepath_cache_save($type, $data) { + return elgg_save_system_cache($type, $data); +} +/** + * @access private + */ +function elgg_filepath_cache_load($type) { + return elgg_load_system_cache($type); +} +/** + * @access private + */ +function elgg_enable_filepath_cache() { + elgg_enable_system_cache(); +} +/** + * @access private + */ +function elgg_disable_filepath_cache() { + elgg_disable_system_cache(); +} + +/* Simplecache */ + +/** + * Registers a view to simple cache. + * + * Simple cache is a caching mechanism that saves the output of + * views and its extensions into a file. If the view is called + * by the {@link simplecache/view.php} file, the Elgg framework will + * not be loaded and the contents of the view will returned + * from file. + * + * @warning Simple cached views must take no parameters and return + * the same content no matter who is logged in. + * + * @example + * $blog_js = elgg_get_simplecache_url('js', 'blog/save_draft'); + * elgg_register_simplecache_view('js/blog/save_draft'); + * elgg_register_js('elgg.blog', $blog_js); + * elgg_load_js('elgg.blog'); + * + * @param string $viewname View name + * + * @return void + * @link http://docs.elgg.org/Views/Simplecache + * @see elgg_regenerate_simplecache() + * @since 1.8.0 + */ +function elgg_register_simplecache_view($viewname) { + global $CONFIG; + + if (!isset($CONFIG->views)) { + $CONFIG->views = new stdClass; + } + + if (!isset($CONFIG->views->simplecache)) { + $CONFIG->views->simplecache = array(); + } + + $CONFIG->views->simplecache[] = $viewname; +} + +/** + * Get the URL for the cached file + * + * @warning You must register the view with elgg_register_simplecache_view() + * for caching to work. See elgg_register_simplecache_view() for a full example. + * + * @param string $type The file type: css or js + * @param string $view The view name + * @return string + * @since 1.8.0 + */ +function elgg_get_simplecache_url($type, $view) { + global $CONFIG; + $lastcache = (int)$CONFIG->lastcache; + $viewtype = elgg_get_viewtype(); + elgg_register_simplecache_view("$type/$view");// see #5302 + if (elgg_is_simplecache_enabled()) { + $url = elgg_get_site_url() . "cache/$type/$viewtype/$view.$lastcache.$type"; + } else { + $url = elgg_get_site_url() . "$type/$view.$lastcache.$type"; + $elements = array("view" => $viewtype); + $url = elgg_http_add_url_query_elements($url, $elements); + } - /** - * ElggFileCache - * Store cached data in a file store. - * - * @author Curverider Ltd <info@elgg.com> - * @package Elgg - * @subpackage API - */ - class ElggFileCache extends ElggCache - { - /** - * Set the Elgg cache. - * - * @param string $cache_path The cache path. - * @param int $max_age Maximum age in seconds, 0 if no limit. - * @param int $max_size Maximum size of cache in seconds, 0 if no limit. - */ - function __construct($cache_path, $max_age = 0, $max_size = 0) - { - $this->set_variable("cache_path", $cache_path); - $this->set_variable("max_age", $max_age); - $this->set_variable("max_size", $max_size); - - if ($cache_path=="") throw new ConfigurationException(elgg_echo('ConfigurationException:NoCachePath')); - } - - /** - * Create and return a handle to a file. - * - * @param string $filename - * @param string $rw - */ - protected function create_file($filename, $rw = "rb") - { - // Create a filename matrix - $matrix = ""; - $depth = strlen($filename); - if ($depth > 5) $depth = 5; - - // for ($n = 0; $n < $depth; $n++) - // $matrix .= $filename[$n] . "/"; - - // Create full path - $path = $this->get_variable("cache_path") . $matrix; - - // if (!mkdir($path, 0700, true)) throw new IOException("Could not make $path"); - - // Open the file - if ((!file_exists($path . $filename)) && ($rw=="rb")) return false; - - return fopen($path . $filename, $rw); - } - - /** - * Create a sanitised filename for the file. - * - * @param string $filename - */ - protected function sanitise_filename($filename) - { - // TODO : Writeme - - return $filename; - } - - /** - * Save a key - * - * @param string $key - * @param string $data - * @return boolean - */ - public function save($key, $data) - { - $f = $this->create_file($this->sanitise_filename($key), "wb"); - if ($f) - { - $result = fwrite($f, $data); - fclose($f); - - return $result; + return $url; +} + +/** + * Regenerates the simple cache. + * + * @warning This does not invalidate the cache, but actively rebuilds it. + * + * @param string $viewtype Optional viewtype to regenerate. Defaults to all valid viewtypes. + * + * @return void + * @see elgg_register_simplecache_view() + * @since 1.8.0 + */ +function elgg_regenerate_simplecache($viewtype = NULL) { + global $CONFIG; + + if (!isset($CONFIG->views->simplecache) || !is_array($CONFIG->views->simplecache)) { + return; + } + + $lastcached = time(); + + // @todo elgg_view() checks if the page set is done (isset($CONFIG->pagesetupdone)) and + // triggers an event if it's not. Calling elgg_view() here breaks submenus + // (at least) because the page setup hook is called before any + // contexts can be correctly set (since this is called before page_handler()). + // To avoid this, lie about $CONFIG->pagehandlerdone to force + // the trigger correctly when the first view is actually being output. + $CONFIG->pagesetupdone = TRUE; + + if (!file_exists($CONFIG->dataroot . 'views_simplecache')) { + mkdir($CONFIG->dataroot . 'views_simplecache'); + } + + if (isset($viewtype)) { + $viewtypes = array($viewtype); + } else { + $viewtypes = $CONFIG->view_types; + } + + $original_viewtype = elgg_get_viewtype(); + + // disable error reporting so we don't cache problems + $old_debug = elgg_get_config('debug'); + elgg_set_config('debug', null); + + foreach ($viewtypes as $viewtype) { + elgg_set_viewtype($viewtype); + foreach ($CONFIG->views->simplecache as $view) { + $viewcontents = elgg_view($view); + $viewname = md5(elgg_get_viewtype() . $view); + if ($handle = fopen($CONFIG->dataroot . 'views_simplecache/' . $viewname, 'w')) { + fwrite($handle, $viewcontents); + fclose($handle); } - - return false; } - - /** - * Load a key - * - * @param string $key - * @param int $offset - * @param int $limit - * @return string - */ - public function load($key, $offset = 0, $limit = null) - { - $f = $this->create_file($this->sanitise_filename($key)); - if ($f) - { - //fseek($f, $offset); - if (!$limit) $limit = -1; - $data = stream_get_contents($f, $limit, $offset); - - fclose($f); - - return $data; + + datalist_set("simplecache_lastupdate_$viewtype", $lastcached); + datalist_set("simplecache_lastcached_$viewtype", $lastcached); + } + + elgg_set_config('debug', $old_debug); + elgg_set_viewtype($original_viewtype); + + // needs to be set for links in html head + $CONFIG->lastcache = $lastcached; + + unset($CONFIG->pagesetupdone); +} + +/** + * Is simple cache enabled + * + * @return bool + * @since 1.8.0 + */ +function elgg_is_simplecache_enabled() { + if (elgg_get_config('simplecache_enabled')) { + return true; + } + + return false; +} + +/** + * Enables the simple cache. + * + * @access private + * @see elgg_register_simplecache_view() + * @return void + * @since 1.8.0 + */ +function elgg_enable_simplecache() { + global $CONFIG; + + datalist_set('simplecache_enabled', 1); + $CONFIG->simplecache_enabled = 1; + elgg_regenerate_simplecache(); +} + +/** + * Disables the simple cache. + * + * @warning Simplecache is also purged when disabled. + * + * @access private + * @see elgg_register_simplecache_view() + * @return void + * @since 1.8.0 + */ +function elgg_disable_simplecache() { + global $CONFIG; + if ($CONFIG->simplecache_enabled) { + datalist_set('simplecache_enabled', 0); + $CONFIG->simplecache_enabled = 0; + + // purge simple cache + if ($handle = opendir($CONFIG->dataroot . 'views_simplecache')) { + while (false !== ($file = readdir($handle))) { + if ($file != "." && $file != "..") { + unlink($CONFIG->dataroot . 'views_simplecache/' . $file); + } } - - return false; + closedir($handle); } - - /** - * Invalidate a given key. - * - * @param string $key - * @return bool - */ - public function delete($key) - { - $dir = $this->get_variable("cache_path"); - - return unlink($dir.$key); + } +} + +/** + * Deletes all cached views in the simplecache and sets the lastcache and + * lastupdate time to 0 for every valid viewtype. + * + * @return bool + * @since 1.7.4 + */ +function elgg_invalidate_simplecache() { + global $CONFIG; + + if (!isset($CONFIG->views->simplecache) || !is_array($CONFIG->views->simplecache)) { + return false; + } + + $handle = opendir($CONFIG->dataroot . 'views_simplecache'); + + if (!$handle) { + return false; + } + + // remove files. + $return = true; + while (false !== ($file = readdir($handle))) { + if ($file != "." && $file != "..") { + $return &= unlink($CONFIG->dataroot . 'views_simplecache/' . $file); } - - public function clear() - { - // TODO : writeme + } + closedir($handle); + + // reset cache times + $viewtypes = $CONFIG->view_types; + + if (!is_array($viewtypes)) { + return false; + } + + foreach ($viewtypes as $viewtype) { + $return &= datalist_set("simplecache_lastupdate_$viewtype", 0); + $return &= datalist_set("simplecache_lastcached_$viewtype", 0); + } + + return $return; +} + +/** + * @see elgg_reset_system_cache() + * @access private + */ +function _elgg_load_cache() { + global $CONFIG; + + $CONFIG->system_cache_loaded = false; + + $CONFIG->views = new stdClass(); + $data = elgg_load_system_cache('view_locations'); + if (!is_string($data)) { + return; + } + $CONFIG->views->locations = unserialize($data); + + $data = elgg_load_system_cache('view_types'); + if (!is_string($data)) { + return; + } + $CONFIG->view_types = unserialize($data); + + $CONFIG->system_cache_loaded = true; +} + +/** + * @access private + */ +function _elgg_cache_init() { + global $CONFIG; + + $viewtype = elgg_get_viewtype(); + + // Regenerate the simple cache if expired. + // Don't do it on upgrade because upgrade does it itself. + // @todo - move into function and perhaps run off init system event + if (!defined('UPGRADING')) { + $lastupdate = datalist_get("simplecache_lastupdate_$viewtype"); + $lastcached = datalist_get("simplecache_lastcached_$viewtype"); + if ($lastupdate == 0 || $lastcached < $lastupdate) { + elgg_regenerate_simplecache($viewtype); + $lastcached = datalist_get("simplecache_lastcached_$viewtype"); } - - public function __destruct() - { - // TODO: Check size and age, clean up accordingly - $size = 0; - $dir = $this->get_variable("cache_path"); - - // Short circuit if both size and age are unlimited - if (($this->get_variable("max_age")==0) && ($this->get_variable("max_size")==0)) - return; - - $exclude = array(".",".."); - - $files = scandir($dir); - if (!$files) throw new IOException(sprintf(elgg_echo('IOException:NotDirectory'), $dir)); - - // Perform cleanup - foreach ($files as $f) - { - if (!in_array($f, $exclude)) - { - $stat = stat($dir.$f); - - // Add size - $size .= $stat['size']; - - // Is this older than my maximum date? - if (($this->get_variable("max_age")>0) && (time() - $stat['mtime'] > $this->get_variable("max_age"))) - unlink($dir.$f); - - - - // TODO: Size - - } - } + $CONFIG->lastcache = $lastcached; + } + + // cache system data if enabled and not loaded + if ($CONFIG->system_cache_enabled && !$CONFIG->system_cache_loaded) { + elgg_save_system_cache('view_locations', serialize($CONFIG->views->locations)); + elgg_save_system_cache('view_types', serialize($CONFIG->view_types)); + } + + if ($CONFIG->system_cache_enabled && !$CONFIG->i18n_loaded_from_cache) { + reload_all_translations(); + foreach ($CONFIG->translations as $lang => $map) { + elgg_save_system_cache("$lang.lang", serialize($map)); } } - -?>
\ No newline at end of file +} + +elgg_register_event_handler('ready', 'system', '_elgg_cache_init'); |
