PageRenderTime 45ms CodeModel.GetById 35ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

/system/classes/filecache.php

https://github.com/HabariMag/habarimag-old
PHP | 332 lines | 207 code | 43 blank | 82 comment | 39 complexity | 50794351d6bf82efbe40e5c0e025332a MD5 | raw file
  1<?php
  2/**
  3 * @package Habari
  4 *
  5 */
  6
  7/**
  8 * Contains the FileCache class
  9 *
 10 * Stores cache data in local files
 11 */
 12class FileCache extends Cache
 13{
 14	protected $cache_location;
 15	protected $enabled = false;
 16	protected $cache_files = array();
 17	protected $cache_data = array();
 18	protected $index_file;
 19
 20	/**
 21	 * Constructor for FileCache
 22	 *
 23	 * Sets up paths etc. and reads cache index, if it exists.
 24	 */
 25	public function __construct()
 26	{
 27		if ( !defined( 'FILE_CACHE_LOCATION' ) ) {
 28			define( 'FILE_CACHE_LOCATION', HABARI_PATH . '/user/cache/' );
 29		}
 30		$this->cache_location = FILE_CACHE_LOCATION;
 31		$this->index_file = $this->cache_location . md5( 'index' . Options::get( 'GUID' ) ) . '.data';
 32		$this->enabled = is_writeable( $this->cache_location );
 33		if ( $this->enabled ) {
 34			if ( file_exists( $this->index_file ) ) {
 35				$this->cache_files = unserialize( file_get_contents( $this->index_file ) );
 36			}
 37		}
 38		else {
 39			Session::error( sprintf( _t( "The cache directory '%s' is not writable - the cache is disabled. The user, or group, which your web server is running as, needs to have read, write, and execute permissions on this directory." ), $this->cache_location ), 'filecache' );
 40			EventLog::log( sprintf( _t( "The cache directory '%s' is not writable - the cache is disabled." ), $this->cache_location ), 'notice', 'cache', 'habari' );
 41		}
 42	}
 43
 44	/**
 45	 * Is record with $name in the cache?
 46	 *
 47	 * @param string $name name of the cached item
 48	 * @return boolean true if item is cached, false if not
 49	 */
 50	protected function _has( $name, $group )
 51	{
 52		if ( !$this->enabled ) {
 53			return false;
 54		}
 55		$hash = $this->get_name_hash( $name );
 56		$ghash = $this->get_group_hash( $group );
 57
 58		return isset( $this->cache_files[$ghash][$hash] ) && ( $this->cache_files[$ghash][$hash]['keep'] || $this->cache_files[$ghash][$hash]['expires'] > time() ) && file_exists( $this->cache_files[$ghash][$hash]['file'] );
 59	}
 60
 61	/**
 62	 * Is group in the cache?
 63	 *
 64	 * @param string $name name of the cached item
 65	 * @return boolean true if item is cached, false if not
 66	 */
 67	protected function _has_group( $group )
 68	{
 69		if ( !$this->enabled ) {
 70			return false;
 71		}
 72		$ghash = $this->get_group_hash( $group );
 73
 74		$valid = true;
 75		$now = time();
 76		foreach ( $this->cache_files[$ghash] as $hash => $record ) {
 77			if ( ! file_exists( $record['file'] ) || $record['expires'] <= $now ) {
 78				$valid = false;
 79				break;
 80			}
 81		}
 82
 83		return ( isset( $this->cache_files[$ghash] ) && count( $this->cache_files[$ghash] ) > 1 ) && $valid;
 84	}
 85
 86	/**
 87	 * Returns the group from the cache.
 88	 *
 89	 * @param string $name The name of the cached item
 90	 * @return mixed The group or array() if it doesn't exist in cache
 91	 */
 92	protected function _get_group( $group )
 93	{
 94		if ( !$this->enabled ) {
 95			return null;
 96		}
 97		$ghash = $this->get_group_hash( $group );
 98
 99		if ( !isset( $this->cache_data[$group] ) ) {
100			$this->cache_data[$group] = array();
101			if ( isset( $this->cache_files[$ghash] ) ) {
102				foreach ( $this->cache_files[$ghash] as $hash => $record ) {
103					$this->cache_data[$group][$record['name']] = unserialize(
104						file_get_contents( $record['file'] )
105					);
106				}
107			}
108		}
109		return $this->cache_data[$group];
110	}
111
112	/**
113	 * Returns the named value from the cache.
114	 *
115	 * @param string $name The name of the cached item
116	 * @return mixed The item value or null if it doesn't exist in cache
117	 */
118	protected function _get( $name, $group )
119	{
120		if ( !$this->enabled ) {
121			return null;
122		}
123		$hash = $this->get_name_hash( $name );
124		$ghash = $this->get_group_hash( $group );
125
126		if ( !isset( $this->cache_data[$group][$name] ) ) {
127			$this->cache_data[$group][$name] = null;
128			if ( isset( $this->cache_files[$ghash][$hash] ) && ($this->cache_files[$ghash][$hash]['keep'] || $this->cache_files[$ghash][$hash]['expires'] > time()) && file_exists( $this->cache_files[$ghash][$hash]['file'] ) ) {
129				$this->cache_data[$group][$name] = unserialize( file_get_contents( $this->cache_files[$ghash][$hash]['file'] ) );
130			}
131		}
132		return $this->cache_data[$group][$name];
133	}
134
135	protected function _set( $name, $value, $expiry, $group, $keep )
136	{
137		if ( !$this->enabled ) {
138			return null;
139		}
140
141		Plugins::act( 'cache_set_before', $name, $group, $value, $expiry );
142
143		$hash = $this->get_name_hash( $name );
144		$ghash = $this->get_group_hash( $group );
145
146		if ( !isset( $this->cache_data[$group] ) ) {
147			// prime our cache so the local version is up-to-date and complete
148			$this->_get_group( $group );
149		}
150		$this->cache_data[$group][$name] = $value;
151
152		file_put_contents( $this->cache_location . $ghash . $hash, serialize( $value ) );
153		$this->cache_files[$ghash][$hash] = array( 'file' => $this->cache_location . $ghash . $hash, 'expires' => time() + $expiry, 'name' => $name, 'keep' => $keep );
154		$this->clear_expired();
155		file_put_contents( $this->index_file, serialize( $this->cache_files ) );
156
157		Plugins::act( 'cache_set_after', $name, $group, $value, $expiry );
158
159		return true;
160	}
161
162	/**
163	 * Expires the named value from the cache.
164	 *
165	 * @param string $name The name of the cached item
166	 * @param string $group The name of the cache group
167	 * @param string $match_mode (optional) how to match bucket names ('strict', 'regex', 'glob') (default 'strict')
168	 */
169	protected function _expire( $name, $group, $match_mode = 'strict' )
170	{
171		if ( !$this->enabled ) {
172			return null;
173		}
174		
175		// prime the variable cache.
176		// dirty, dirty hack. we should *never* load all the data in, especially when we only care about the expirey
177		// alas, this crappy code requires it
178		$this->_get_group($group);
179		
180		$keys = array();
181		switch ( strtolower( $match_mode ) ) {
182			case 'glob':
183				if ( array_key_exists( $group, $this->cache_data ) ) {
184					$keys = preg_grep( Utils::glob_to_regex( $name ), array_keys( $this->cache_data[$group] ) );
185				}
186				break;
187			case 'regex':
188				if ( array_key_exists( $group, $this->cache_data ) ) {
189					$keys = preg_grep( $name, array_keys( $this->cache_data[$group] ) );
190				}
191				break;
192			case 'strict':
193			default:
194				$keys = array( $name );
195				break;
196		}
197
198		$ghash = $this->get_group_hash( $group );
199		foreach ( $keys as $key ) {
200			Plugins::act( 'cache_expire_before', $name, $group );
201
202			$hash = $this->get_name_hash( $key );
203
204			if ( isset( $this->cache_files[$ghash][$hash] ) && file_exists( $this->cache_files[$ghash][$hash]['file'] ) ) {
205				unlink( $this->cache_files[$ghash][$hash]['file'] );
206				unset( $this->cache_files[$ghash][$hash] );
207			}
208
209			Plugins::act( 'cache_expire_after', $name, $group );
210		}
211
212		$this->clear_expired();
213		file_put_contents( $this->index_file, serialize( $this->cache_files ) );
214	}
215
216
217	/**
218	 * Return whether a named cache value has expired
219	 *
220	 * @param string $name The name of the cached item
221	 * @param string $group The group of the cached item
222	 * @return boolean true if the stored value has expired
223	 */
224	protected function _expired( $name, $group )
225	{
226		if ( !$this->enabled ) {
227			return null;
228		}
229		$hash = $this->get_name_hash( $name );
230		$ghash = $this->get_group_hash( $group );
231
232		// Do not check cached data, since we can return (and cache in this object) data if the cache is set to 'keep'
233		if ( isset( $this->cache_files[$ghash][$hash] ) && $this->cache_files[$ghash][$hash]['expires'] > time() && file_exists( $this->cache_files[$ghash][$hash]['file'] ) ) {
234			return false;
235		}
236		else {
237			return true;
238		}
239	}
240
241	/**
242	 * Extend the expiration of the named cached value.
243	 *
244	 * @param string $name The name of the cached item
245	 * @param integer $expiry The duration in seconds to extend the cache expiration by
246	 */
247	protected function _extend( $name, $expiry, $group )
248	{
249		if ( !$this->enabled ) {
250			return null;
251		}
252
253		Plugins::act( 'cache_extend_before', $name, $group, $expiry );
254
255		$hash = $this->get_name_hash( $name );
256		$ghash = $this->get_group_hash( $group );
257
258		if ( isset( $this->cache_files[$ghash][$hash] ) ) {
259			$this->cache_files[$ghash][$hash]['expires'] = time() + $expiry;
260			$this->clear_expired();
261			file_put_contents( $this->index_file, serialize( $this->cache_files ) );
262		}
263
264		Plugins::act( 'cache_extend_after', $name, $group, $expiry );
265	}
266
267	/**
268	 * Remove all cache files
269	 */
270	protected function _purge()
271	{
272		Plugins::act( 'cache_purge_before' );
273
274		$glob = Utils::glob( FILE_CACHE_LOCATION . '*.data' );
275		foreach ( $glob as $file ) {
276			unlink( $file );
277		}
278		$glob = Utils::glob( FILE_CACHE_LOCATION . '*.cache' );
279		foreach ( $glob as $file ) {
280			unlink( $file );
281		}
282
283		Plugins::act( 'cache_purge_after' );
284	}
285
286	/**
287	 * Get the unique hash for a given key.
288	 *
289	 * @param string $name The name of the cached item.
290	 */
291	private function get_name_hash( $name )
292	{
293		return md5( $name . Options::get( 'GUID' ) ) . '.cache';
294	}
295
296	/**
297	 * Get the unique hash for a given key.
298	 *
299	 * @param string $name The name of the cached group.
300	 */
301	private function get_group_hash( $group )
302	{
303		return md5( $group . Options::get( 'GUID' ) ) . '.';
304	}
305
306	/**
307	 * Check whether a given record is still fresh (e.g. has not expired).
308	 */
309	private function record_fresh( $record )
310	{
311		if ( $record['expires'] > time() || $record['keep'] ) {
312			return true;
313		}
314		elseif ( file_exists( $record['file'] ) ) {
315			unlink( $record['file'] );
316		}
317		return false;
318	}
319
320	/**
321	 * Purge expired items from the cache.
322	 */
323	private function clear_expired()
324	{
325		foreach ( $this->cache_files as $ghash => $records ) {
326			$this->cache_files[$ghash] = array_filter( $records, array( $this, 'record_fresh' ) );
327		}
328	}
329
330}
331
332?>