Skip to content

Commit

Permalink
Remove dependency on Configure from Cache.
Browse files Browse the repository at this point in the history
Not using Configure to setup caching has a couple benefits.

* Using Configure creates magic at a distance. Setting things in one
  class creates effects in another.
* Cache engines are always configured the same way.
* Dropped cache engines don't magically re-create themselves.
* It is still simple to create cache adapters using configuration files, or
  inject instances directly.

With this change we still lazily load cache adapters once they are
used. Add a CacheRegistry to make Cache similar to log and other registy
based classes in CakePHP.
  • Loading branch information
markstory committed Aug 14, 2013
1 parent d6c030d commit 1c3d82e
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 188 deletions.
38 changes: 23 additions & 15 deletions App/Config/cache.php
Expand Up @@ -14,7 +14,7 @@
*/
namespace App\Config;

use Cake\Core\Configure;
use Cake\Cache\Cache;

/**
* Turn off all caching application-wide.
Expand All @@ -33,6 +33,16 @@
*/
//Configure::write('Cache.check', true);

/**
* Enable cache view prefixes.
*
* If set it will be prepended to the cache name for view file caching. This is
* helpful if you deploy the same application via multiple subdomains and languages,
* for instance. Each version can then have its own view cache namespace.
* Note: The final cache file name will then be `prefix_cachefilename`.
*/
//Configure::write('Cache.viewPrefix', 'prefix');

// In development mode, caches should expire quickly.
$duration = '+999 days';
if (Configure::read('debug') >= 1) {
Expand All @@ -48,29 +58,33 @@
// Prefix each application on the same server with a different string, to avoid Memcache and APC conflicts.
$prefix = 'myapp_';


$cacheConfigs = [];

/**
* Configure the cache used for general framework caching. Path information,
* object listings, and translation cache files are stored with this configuration.
*/
Configure::write('Cache._cake_core_', [
$cacheConfigs['_cake_core_'] = [
'engine' => $engine,
'prefix' => $prefix . 'cake_core_',
'path' => CACHE . 'persistent' . DS,
'serialize' => ($engine === 'File'),
'duration' => $duration
]);
];


/**
* Configure the cache for model and datasource caches. This cache configuration
* is used to store schema descriptions, and table listings in connections.
*/
Configure::write('Cache._cake_model_', [
$cacheConfigs['_cake_model_'] = [
'engine' => $engine,
'prefix' => $prefix . 'cake_model_',
'path' => CACHE . 'models' . DS,
'serialize' => ($engine === 'File'),
'duration' => $duration
]);
];

/**
* Cache Engine Configuration
Expand Down Expand Up @@ -145,14 +159,8 @@
* 'persistent' => true, // [optional] set this to false for non-persistent connections
* ));
*/
Configure::write('Cache.default', array('engine' => 'File'));
$cacheConfigs['default'] = [
'engine' => 'File'
];

/**
* Enable cache view prefixes.
*
* If set it will be prepended to the cache name for view file caching. This is
* helpful if you deploy the same application via multiple subdomains and languages,
* for instance. Each version can then have its own view cache namespace.
* Note: The final cache file name will then be `prefix_cachefilename`.
*/
//Configure::write('Cache.viewPrefix', 'prefix');
Cache::config($cacheConfigs);
124 changes: 61 additions & 63 deletions lib/Cake/Cache/Cache.php
Expand Up @@ -32,7 +32,7 @@
* A sample configuration would be:
*
* {{{
* Configure::write('Cache.shared', array(
* Cache::config('shared', array(
* 'engine' => 'Cake\Cache\Engine\ApcEngine',
* 'prefix' => 'my_app_'
* ));
Expand Down Expand Up @@ -83,7 +83,18 @@
class Cache {

/**
* Cache configuration stack
* Configuraiton backup.
*
* Keeps the permanent/default settings for each cache engine.
* These settings are used to reset the engines after temporary modification.
*
* @var array
*/
protected static $_restore = array();

/**
* Cache configuration.
*
* Keeps the permanent/default settings for each cache engine.
* These settings are used to reset the engines after temporary modification.
*
Expand All @@ -106,22 +117,32 @@ class Cache {
protected static $_reset = false;

/**
* Engine instances keyed by configuration name.
* Cache Registry used for creating and using cache adapters.
*
* @var array
* @var Cake\Cache\CacheRegistry
*/
protected static $_engines = array();
protected static $_registry;

/**
* Deprecated method. Will be removed in 3.0.0
* This method can be used to define cache adapters for an application
* during the bootstrapping process. You can use this method to add new cache adapters
* at runtime as well. New cache configurations will be constructed upon the next write.
*
* To change an adapter's configuration at runtime, first drop the adapter and then
* reconfigure it.
*
* @deprecated
* Adapters will not be constructed until the first operation is done.
*
* @param string|array $key The name of the cache config, or an array of multiple configs.
* @param array $config An array of name => config data for adapter.
* @return void
*/
public static function config($name = null, $settings = array()) {
trigger_error(
__d('cake_dev', 'You must use Configure::write() to define cache configuration. Or use engine() to inject new adapter.'),
E_USER_WARNING
);
public static function config($key, $config = null) {
if ($config !== null && is_string($key)) {
static::$_config[$key] = $config;
return;
}
static::$_config = array_merge(static::$_config, $key);
}

/**
Expand All @@ -132,50 +153,35 @@ public static function config($name = null, $settings = array()) {
* @throws Cake\Error\Exception
*/
protected static function _buildEngine($name) {
$config = Configure::read('Cache.' . $name);
if (empty($config['engine'])) {
return false;
}
$cacheClass = App::classname($config['engine'], 'Cache/Engine', 'Engine');
if (!$cacheClass) {
throw new Error\Exception(
__d('cake_dev', 'Cache engine %s is not available.', $name)
);
}
if (!is_subclass_of($cacheClass, 'Cake\Cache\CacheEngine')) {
throw new Error\Exception(__d('cake_dev', 'Cache engines must use Cake\Cache\CacheEngine as a base class.'));
}
$engine = new $cacheClass();
if (!$engine->init($config)) {
throw new Error\Exception(
__d('cake_dev', 'Cache engine %s is not properly configured.', $name)
);
if (empty(static::$_registry)) {
static::$_registry = new CacheRegistry();
}
if ($engine->settings['probability'] && time() % $engine->settings['probability'] === 0) {
$engine->gc();
if (empty(static::$_config[$name]['engine'])) {
return false;
}
static::$_engines[$name] = $engine;
$config = static::$_config[$name];
$config['className'] = $config['engine'];

static::$_registry->load($name, $config);

if (!empty($config['groups'])) {
foreach ($config['groups'] as $group) {
static::$_groups[$group][] = $name;
sort(static::$_groups[$group]);
static::$_groups[$group] = array_unique(static::$_groups[$group]);
sort(static::$_groups[$group]);
}
}

return true;
}

/**
* Returns an array containing the connected Cache engines.
* This will not return engines that are configured, but have not
* been used yet.
* Returns an array containing the configured Cache engines.
*
* @return array Array of connected Cache config names.
* @return array Array of configured Cache config names.
*/
public static function configured() {
return array_keys(static::$_engines);
return array_keys(static::$_config);
}

/**
Expand All @@ -188,47 +194,39 @@ public static function configured() {
* @return boolean success of the removal, returns false when the config does not exist.
*/
public static function drop($config) {
if (isset(static::$_engines[$config])) {
unset(static::$_engines[$config]);
unset(static::$_config[$config]);
return true;
if (!isset(static::$_registry->{$config})) {
return false;
}
return false;
static::$_registry->unload($config);
unset(static::$_config[$config], static::$_restore[$config]);
return true;
}

/**
* Fetch the engine attached to a specific configuration name.
* If the engine does not exist, configuration data will be read from
* `Configure`.
*
* If the cache engine & configuration are missing an error will be
* triggered.
*
* @param string $config The configuration name you want an engine.
* @param Cake\Cache\CacheEngine $engine An engine instance if you are manually
* injecting a cache engine.
* @param string $config The configuration name you want an engine for.
* @return Cake\Cache\Engine
*/
public static function engine($config, CacheEngine $engine = null) {
public static function engine($config) {
if (Configure::read('Cache.disable')) {
return false;
}
if (isset(static::$_engines[$config])) {
return static::$_engines[$config];
if (isset(static::$_registry->{$config})) {
return static::$_registry->{$config};
}
if (!$engine && !static::_buildEngine($config, $engine)) {
if (!static::_buildEngine($config)) {
$message = __d(
'cake_dev',
'The "%s" cache configuration does not exist, nor could configuration be found at "Cache.%s".',
$config,
'The "%s" cache configuration does not exist.',
$config
);
trigger_error($message, E_USER_WARNING);
}
if ($engine) {
static::$_engines[$config] = $engine;
}
return static::$_engines[$config];
return static::$_registry->{$config};
}

/**
Expand Down Expand Up @@ -269,8 +267,8 @@ public static function set($settings = array(), $value = null, $config = 'defaul
return false;
}

if (empty(static::$_config[$config])) {
static::$_config[$config] = $engine->settings();
if (empty(static::$_restore[$config])) {
static::$_restore[$config] = $engine->settings();
}

if (!empty($settings)) {
Expand All @@ -292,11 +290,11 @@ public static function set($settings = array(), $value = null, $config = 'defaul
* @return void
*/
protected static function _modifySettings($engine, $config, $settings) {
$restore = static::$_config[$config];
$restore = static::$_restore[$config];
if (empty($settings)) {
static::$_reset = false;
$settings = $restore;
unset(static::$_config[$config]);
unset(static::$_restore[$config]);
} else {
$settings = array_merge($restore, $settings);
if (isset($settings['duration']) && !is_numeric($settings['duration'])) {
Expand Down

0 comments on commit 1c3d82e

Please sign in to comment.