mirror of
https://github.com/processwire/processwire.git
synced 2025-08-12 09:44:38 +02:00
Additional improvements to WireCache, WireCacheInterface and WireCacheDatabase
This commit is contained in:
@@ -1662,14 +1662,15 @@ $config->modals = array(
|
|||||||
* This is an optimization that can reduce some database overhead.
|
* This is an optimization that can reduce some database overhead.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
|
* @deprecated No longer in use as of 3.0.218
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
$config->preloadCacheNames = array(
|
$config->preloadCacheNames = array(
|
||||||
'Modules.info',
|
//'Modules.info',
|
||||||
//'ModulesVerbose.info',
|
//'ModulesVerbose.info',
|
||||||
'ModulesVersions.info',
|
//'ModulesVersions.info',
|
||||||
'Modules.wire/modules/',
|
//'Modules.wire/modules/',
|
||||||
'Modules.site/modules/',
|
//'Modules.site/modules/',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -723,29 +723,43 @@ interface InputfieldHasSelectableOptions {
|
|||||||
/**
|
/**
|
||||||
* Interface for WireCache handler classes
|
* Interface for WireCache handler classes
|
||||||
*
|
*
|
||||||
|
* For example implementations of this interface see
|
||||||
|
* WireCacheDatabase (core) and WireCacheFilesystem (module)
|
||||||
|
*
|
||||||
* @since 3.0.218
|
* @since 3.0.218
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
interface WireCacheInterface {
|
interface WireCacheInterface {
|
||||||
/**
|
|
||||||
* Get single cache
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string|array|null|false $expire
|
|
||||||
* @return string|false
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function get($name, $expire);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get multiple caches
|
* Find caches by names and/or expirations and return requested values
|
||||||
*
|
*
|
||||||
* @param array $names
|
* ~~~~~
|
||||||
* @param string|array|null|false $expire
|
* // Default options
|
||||||
* @return array
|
* $defaults = [
|
||||||
|
* 'names' => [],
|
||||||
|
* 'expires' => [],
|
||||||
|
* 'expiresMode' => 'OR',
|
||||||
|
* 'get' => [ 'name', 'expires', 'data' ],
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* // Example options
|
||||||
|
* $options['names'] = [ 'my-cache', 'your-cache', 'hello-*' ];
|
||||||
|
* $options['expires'] => [
|
||||||
|
* '<= ' . WireCache::expiresNever,
|
||||||
|
* '>= ' . date('Y-m-d H:i:s')
|
||||||
|
* ];
|
||||||
|
* ~~~~~
|
||||||
|
*
|
||||||
|
* @param array $options
|
||||||
|
* - `get` (array): Properties to get in return value, one or more of [ `name`, `expires`, `data`, `size` ] (default=all)
|
||||||
|
* - `names` (array): Names of caches to find (OR condition), optionally appended with wildcard `*`.
|
||||||
|
* - `expires` (array): Expirations of caches to match in ISO-8601 date format, prefixed with operator and space (see expiresMode mode below).
|
||||||
|
* - `expiresMode` (string): Whether it should match any one condition 'OR', or all conditions 'AND' (default='OR')
|
||||||
|
* @return array Returns array of associative arrays, each containing requested properties
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function getMultiple(array $names, $expire);
|
public function find(array $options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a cache
|
* Save a cache
|
||||||
@@ -759,7 +773,7 @@ interface WireCacheInterface {
|
|||||||
public function save($name, $data, $expire);
|
public function save($name, $data, $expire);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete cache
|
* Delete cache by name
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @return bool
|
* @return bool
|
||||||
@@ -768,7 +782,7 @@ interface WireCacheInterface {
|
|||||||
public function delete($name);
|
public function delete($name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all caches
|
* Delete all caches (except those reserved by the system)
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*
|
*
|
||||||
@@ -776,34 +790,10 @@ interface WireCacheInterface {
|
|||||||
public function deleteAll();
|
public function deleteAll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expire all caches
|
* Expire all caches (except those that should never expire)
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function expireAll();
|
public function expireAll();
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache maintenance / remove expired caches
|
|
||||||
*
|
|
||||||
* Called as part of a regular maintenance routine and after page/template save/deletion.
|
|
||||||
*
|
|
||||||
* @param Template|Page|null|bool Item to run maintenance for or, if not specified, general maintenance is performed.
|
|
||||||
* General maintenance only runs once per request. Specify boolean true to force general maintenance to run.
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function maintenance($obj = null);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get info about caches
|
|
||||||
*
|
|
||||||
* @param array $options
|
|
||||||
* - `verbose` (bool): Return verbose details? (default=true)
|
|
||||||
* - `names` (array): Names of caches to return info for, or omit for all (default=[])
|
|
||||||
* - `exclude` (array): Name prefixes of caches to exclude from return value (default=[])
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getInfo(array $options = array());
|
|
||||||
}
|
}
|
||||||
|
@@ -546,11 +546,7 @@ class ProcessWire extends Wire {
|
|||||||
throw new WireDatabaseException($e->getMessage());
|
throw new WireDatabaseException($e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var WireCache $cache */
|
$this->wire('cache', new WireCache(), true);
|
||||||
$cache = $this->wire('cache', new WireCache(), true);
|
|
||||||
$cacheNames = $config->preloadCacheNames;
|
|
||||||
if($database->getEngine() === 'innodb') $cacheNames[] = 'InnoDB.stopwords';
|
|
||||||
$cache->preload($cacheNames, WireCache::expireIgnore);
|
|
||||||
|
|
||||||
$modules = null;
|
$modules = null;
|
||||||
try {
|
try {
|
||||||
|
@@ -26,6 +26,12 @@
|
|||||||
|
|
||||||
class WireCache extends Wire {
|
class WireCache extends Wire {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default cache class
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const defaultCacheClass = 'WireCacheDatabase';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expiration constants that may be supplied to WireCache::save $seconds argument.
|
* Expiration constants that may be supplied to WireCache::save $seconds argument.
|
||||||
*
|
*
|
||||||
@@ -134,6 +140,22 @@ class WireCache extends Wire {
|
|||||||
*/
|
*/
|
||||||
protected $preloading = false;
|
protected $preloading = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memory cache used by the maintenancePage method
|
||||||
|
*
|
||||||
|
* @var array|null Once determined becomes array of cache names => Selectors objects
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected $cacheNameSelectors = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not it's worthwhile to attempt Page or Template maintenance after saves
|
||||||
|
*
|
||||||
|
* @var null|bool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected $usePageTemplateMaintenance = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var WireCacheInterface
|
* @var WireCacheInterface
|
||||||
*
|
*
|
||||||
@@ -147,8 +169,7 @@ class WireCache extends Wire {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected function cacher() {
|
protected function cacher() {
|
||||||
$class = __NAMESPACE__ . "\\WireCacheDatabase";
|
$class = __NAMESPACE__ . "\\" . self::defaultCacheClass;
|
||||||
// $class = __NAMESPACE__ . "\\WireCacheFilesystem";
|
|
||||||
if($this->cacher === null) {
|
if($this->cacher === null) {
|
||||||
$this->cacher = new $class();
|
$this->cacher = new $class();
|
||||||
$this->wire($this->cacher);
|
$this->wire($this->cacher);
|
||||||
@@ -165,6 +186,7 @@ class WireCache extends Wire {
|
|||||||
*
|
*
|
||||||
* @param array $names
|
* @param array $names
|
||||||
* @param int|string|null $expire
|
* @param int|string|null $expire
|
||||||
|
* @deprecated
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function preload(array $names, $expire = null) {
|
public function preload(array $names, $expire = null) {
|
||||||
@@ -180,6 +202,7 @@ class WireCache extends Wire {
|
|||||||
*
|
*
|
||||||
* @param object|string $ns
|
* @param object|string $ns
|
||||||
* @param int|string|null $expire
|
* @param int|string|null $expire
|
||||||
|
* @deprecated
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function preloadFor($ns, $expire = null) {
|
public function preloadFor($ns, $expire = null) {
|
||||||
@@ -221,7 +244,7 @@ class WireCache extends Wire {
|
|||||||
* - If given an array of names, multiple caches will be returned, indexed by cache name.
|
* - If given an array of names, multiple caches will be returned, indexed by cache name.
|
||||||
* - If given a cache name with an asterisk in it, it will return an array of all matching caches.
|
* - If given a cache name with an asterisk in it, it will return an array of all matching caches.
|
||||||
* @param int|string|null|false $expire Optionally specify max age (in seconds) OR oldest date string, or false to ignore.
|
* @param int|string|null|false $expire Optionally specify max age (in seconds) OR oldest date string, or false to ignore.
|
||||||
* - If cache exists and is older, then blank returned. You may omit this to divert to whatever expiration
|
* - If cache exists and is older, then null returned. You may omit this to divert to whatever expiration
|
||||||
* was specified at save() time. Note: The $expire and $func arguments may optionally be reversed.
|
* was specified at save() time. Note: The $expire and $func arguments may optionally be reversed.
|
||||||
* - If using a $func, the behavior of $expire becomes the same as that of save().
|
* - If using a $func, the behavior of $expire becomes the same as that of save().
|
||||||
* @param callable $func Optionally provide a function/closure that generates the cache value and it
|
* @param callable $func Optionally provide a function/closure that generates the cache value and it
|
||||||
@@ -230,26 +253,55 @@ class WireCache extends Wire {
|
|||||||
* @return string|array|PageArray|mixed|null Returns null if cache doesn’t exist and no generation function provided.
|
* @return string|array|PageArray|mixed|null Returns null if cache doesn’t exist and no generation function provided.
|
||||||
* @throws WireException if given invalid arguments
|
* @throws WireException if given invalid arguments
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function get($name, $expire = null, $func = null) {
|
public function get($name, $expire = null, $func = null) {
|
||||||
|
|
||||||
$values = array();
|
$values = array();
|
||||||
$expireNow = $expire === self::expireNow;
|
|
||||||
$getMultiple = is_array($name); // retrieving multiple caches at once?
|
$getMultiple = is_array($name); // retrieving multiple caches at once?
|
||||||
|
$expireNow = false;
|
||||||
|
|
||||||
|
$expireIgnores = array(
|
||||||
|
self::expireIgnore, self::expireReserved, self::expireNever,
|
||||||
|
self::expireSave, self::expireSelector
|
||||||
|
);
|
||||||
|
|
||||||
if($expire !== null && $expire !== self::expireIgnore) {
|
if($expire !== null && $expire !== self::expireIgnore) {
|
||||||
if(!is_int($expire) && !is_string($expire) && is_callable($expire)) {
|
if(!is_int($expire) && !is_string($expire) && is_callable($expire) && !$expire instanceof Wire) {
|
||||||
$_func = $func;
|
$_func = $func;
|
||||||
$func = $expire;
|
$func = $expire;
|
||||||
$expire = $_func === null ? null : $this->getExpires($_func);
|
$expire = $_func === null ? null : $this->getExpires($_func, false);
|
||||||
unset($_func);
|
unset($_func);
|
||||||
} else {
|
} else {
|
||||||
$expire = $this->getExpires($expire);
|
$expire = $this->getExpires($expire, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($expire === WireCache::expireNow) return ($getMultiple ? false : $values);
|
if($expire === self::expireNow) {
|
||||||
|
// forced expiration now
|
||||||
|
$expireNow = true;
|
||||||
|
$expires = array();
|
||||||
|
|
||||||
|
} else if(in_array($expire, $expireIgnores, true)) {
|
||||||
|
// no expires conditions to match when:
|
||||||
|
// ignore, reserved, never, save, or selector
|
||||||
|
$expires = array();
|
||||||
|
|
||||||
|
} else if($func !== null || empty($expire)) {
|
||||||
|
// match row only if its expiration is greater than current date/time
|
||||||
|
// or if it has one of the expirations at or below never (save, reserved, etc.)
|
||||||
|
// also use this if $func in play since the expire is used for save rather than get
|
||||||
|
$expires = array(
|
||||||
|
'> ' . date(self::dateFormat),
|
||||||
|
'<= ' . self::expireNever
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// expire represents date/time of expiration
|
||||||
|
$expires = array(
|
||||||
|
'< ' . $expire,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if($getMultiple) {
|
if($getMultiple) {
|
||||||
$names = $name;
|
$names = $name;
|
||||||
@@ -276,34 +328,56 @@ class WireCache extends Wire {
|
|||||||
throw new WireException("Function (\$func) may not be specified to \$cache->get() when requesting multiple caches.");
|
throw new WireException("Function (\$func) may not be specified to \$cache->get() when requesting multiple caches.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$cacher = $this->cacher();
|
$findOptions = array(
|
||||||
|
'names' => $names,
|
||||||
|
'expires' => $expires,
|
||||||
|
'get' => array('name', 'data')
|
||||||
|
);
|
||||||
|
|
||||||
if($getMultiple) {
|
if($getMultiple) {
|
||||||
$values = $expireNow ? array() : $cacher->getMultiple($names, $expire);
|
// get array value
|
||||||
foreach($values as $key => $value) {
|
$rows = $expireNow ? array() : $this->cacher()->find($findOptions);
|
||||||
if($this->looksLikeJSON($value)) {
|
foreach($rows as $row) {
|
||||||
$value = $this->decodeJSON($value);
|
$value = $row['data'];
|
||||||
$values[$key] = $value;
|
$name = $row['name'];
|
||||||
}
|
if($this->looksLikeJSON($value)) $value = $this->decodeJSON($value);
|
||||||
|
if($value !== false) $values[$name] = $value;
|
||||||
}
|
}
|
||||||
|
unset($rows);
|
||||||
foreach($names as $s) {
|
foreach($names as $s) {
|
||||||
// ensure there is at least a placeholder for all requested caches
|
// ensure there is at least a placeholder for all requested caches
|
||||||
if(!isset($values[$s]) && !isset($wildcards[$s])) $values[$s] = '';
|
if(!isset($values[$s]) && !isset($wildcards[$s])) $values[$s] = '';
|
||||||
}
|
}
|
||||||
} else {
|
if($expireNow) {
|
||||||
$value = $expireNow ? false : $cacher->get($name, $expire);
|
// warning: expireNow in getMultiple mode does not support render cache value
|
||||||
if($value !== false && $this->looksLikeJSON($value)) {
|
|
||||||
$value = $this->decodeJSON($value);
|
|
||||||
}
|
}
|
||||||
if(empty($value) && $func !== null && is_callable($func)) {
|
|
||||||
// generate the cache now from the given callable function
|
} else {
|
||||||
$value = $this->renderCacheValue($name, $expire, $func);
|
// get single cache value
|
||||||
|
$findOptions['get'] = array('data');
|
||||||
|
$value = $expireNow ? array() : $this->cacher()->find($findOptions);
|
||||||
|
$value = count($value) ? reset($value) : null;
|
||||||
|
|
||||||
|
if(empty($value)) {
|
||||||
|
if($func !== null && is_callable($func)) {
|
||||||
|
// generate the cache now from the given callable function
|
||||||
|
$value = $this->renderCacheValue($name, $expire, $func);
|
||||||
|
} else {
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = $value['data'];
|
||||||
|
if(!empty($value) && $this->looksLikeJSON($value)) {
|
||||||
|
$value = $this->decodeJSON($value);
|
||||||
|
if($value === false) $value = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $getMultiple ? $values : $value;
|
return $getMultiple ? $values : $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render and save a cache value, when given a function to do so
|
* Render and save a cache value, when given a function to do so
|
||||||
*
|
*
|
||||||
@@ -415,6 +489,12 @@ class WireCache extends Wire {
|
|||||||
public function save($name, $data, $expire = self::expireDaily) {
|
public function save($name, $data, $expire = self::expireDaily) {
|
||||||
$options = array(); // additional data to pass along to cacher save() method
|
$options = array(); // additional data to pass along to cacher save() method
|
||||||
|
|
||||||
|
if(empty($expire)) $expire = self::expireDaily;
|
||||||
|
|
||||||
|
if($expire === WireCache::expireSelector) {
|
||||||
|
$this->cacheNameSelectors = null;
|
||||||
|
}
|
||||||
|
|
||||||
if(is_array($data)) {
|
if(is_array($data)) {
|
||||||
if(array_key_exists('WireCache', $data)) {
|
if(array_key_exists('WireCache', $data)) {
|
||||||
throw new WireException("Cannot cache array that has 'WireCache' array key (reserved for internal use)");
|
throw new WireException("Cannot cache array that has 'WireCache' array key (reserved for internal use)");
|
||||||
@@ -504,17 +584,20 @@ class WireCache extends Wire {
|
|||||||
* Given an expiration seconds, date, page, or template, convert it to an ISO-8601 date
|
* Given an expiration seconds, date, page, or template, convert it to an ISO-8601 date
|
||||||
*
|
*
|
||||||
* Returns an array if expires info requires multiple parts, like with self::expireSelector.
|
* Returns an array if expires info requires multiple parts, like with self::expireSelector.
|
||||||
* In this case it returns array with array('expires' => date, 'selector' => selector);
|
* In this case it returns array with array('expire' => date, 'selector' => selector);
|
||||||
|
* To only allow returning of date strings, specify false for the $verbose argument.
|
||||||
|
* Or to always get an array return value, specify true for $verbose.
|
||||||
*
|
*
|
||||||
* #pw-internal
|
* #pw-internal
|
||||||
*
|
*
|
||||||
* @param $expire
|
* @param Page|Template|string|array|int $expire
|
||||||
|
* @param bool|null $verbose Return verbose array? true=always, false=never, null=when appropriate (i.e. selector)
|
||||||
* @return string|array
|
* @return string|array
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function getExpires($expire) {
|
public function getExpires($expire, $verbose = null) {
|
||||||
|
|
||||||
if(is_object($expire) && $expire->id) {
|
if($expire instanceof Wire && $expire->id) {
|
||||||
|
|
||||||
if($expire instanceof Page) {
|
if($expire instanceof Page) {
|
||||||
// page object
|
// page object
|
||||||
@@ -532,23 +615,30 @@ class WireCache extends Wire {
|
|||||||
} else if(is_array($expire)) {
|
} else if(is_array($expire)) {
|
||||||
// expire value already prepared by a previous call, just return it
|
// expire value already prepared by a previous call, just return it
|
||||||
if(isset($expire['selector']) && isset($expire['expire'])) {
|
if(isset($expire['selector']) && isset($expire['expire'])) {
|
||||||
return $expire;
|
if($verbose || $verbose === null) return $expire; // return array
|
||||||
|
$expire = self::expireSelector;
|
||||||
|
} else {
|
||||||
|
// array without 'selector' is an unknown array
|
||||||
|
$expire = self::expireDaily;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(is_string($expire) && isset($this->expireNames[$expire])) {
|
} else if(is_string($expire) && isset($this->expireNames[$expire])) {
|
||||||
// named expiration constant like "hourly", "daily", etc.
|
// named expiration constant like "hourly", "daily", etc.
|
||||||
$expire = time() + $this->expireNames[$expire];
|
$expire = time() + $this->expireNames[$expire];
|
||||||
|
|
||||||
} else if(in_array($expire, array(self::expireNever, self::expireReserved, self::expireSave))) {
|
} else if(in_array($expire, array(self::expireNever, self::expireReserved, self::expireSave, self::expireNow))) {
|
||||||
// good, we'll take it as-is
|
// good, we'll take it as-is
|
||||||
return $expire;
|
return $verbose ? array('expire' => $expire) : $expire;
|
||||||
|
|
||||||
} else if(is_string($expire) && Selectors::stringHasSelector($expire)) {
|
} else if(is_string($expire) && Selectors::stringHasSelector($expire)) {
|
||||||
// expire when page matches selector
|
// expire when page matches selector
|
||||||
return array(
|
if($verbose || $verbose === null) {
|
||||||
'expire' => self::expireSelector,
|
return array(
|
||||||
'selector' => $expire
|
'expire' => self::expireSelector,
|
||||||
);
|
'selector' => $expire
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return self::expireSelector;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -579,6 +669,8 @@ class WireCache extends Wire {
|
|||||||
|
|
||||||
$expire = date(self::dateFormat, $expire);
|
$expire = date(self::dateFormat, $expire);
|
||||||
|
|
||||||
|
if($verbose) $expire = array('expire' => $expire);
|
||||||
|
|
||||||
return $expire;
|
return $expire;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,19 +686,35 @@ class WireCache extends Wire {
|
|||||||
* ~~~~~
|
* ~~~~~
|
||||||
*
|
*
|
||||||
* @param string $name Name of cache, or partial name with wildcard (i.e. "MyCache*") to clear multiple caches.
|
* @param string $name Name of cache, or partial name with wildcard (i.e. "MyCache*") to clear multiple caches.
|
||||||
* @return bool True on success, false on failure
|
* @return bool True on success, false if no cache was cleared
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function delete($name) {
|
public function delete($name) {
|
||||||
try {
|
if(strpos($name, '*') !== false) {
|
||||||
$success = $this->cacher()->delete($name);
|
$rows = $this->cacher()->find(array(
|
||||||
$this->log("Cleared cache: $name");
|
'names' => array($name),
|
||||||
} catch(\Exception $e) {
|
'get' => array('name'),
|
||||||
$this->trackException($e, true);
|
));
|
||||||
$this->error($e->getMessage());
|
} else {
|
||||||
$success = false;
|
$rows = array(array('name' => $name));
|
||||||
}
|
}
|
||||||
return $success;
|
$clearedNames = array();
|
||||||
|
foreach($rows as $row) {
|
||||||
|
$name = $row['name'];
|
||||||
|
try {
|
||||||
|
$success = $this->cacher()->delete($name);
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
$this->trackException($e, true);
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
$success = false;
|
||||||
|
}
|
||||||
|
if($success) $clearedNames[] = $name;
|
||||||
|
}
|
||||||
|
if(count($clearedNames)) {
|
||||||
|
$this->log("Cleared cache: " . implode(', ', $clearedNames));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -685,14 +793,193 @@ class WireCache extends Wire {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function maintenance($obj = null) {
|
public function maintenance($obj = null) {
|
||||||
try {
|
|
||||||
$result = $this->cacher()->maintenance($obj);
|
static $done = false;
|
||||||
} catch(\Exception $e) {
|
|
||||||
$this->trackException($e, false);
|
$forceRun = false;
|
||||||
$this->error($e->getMessage(), Notice::debug | Notice::log);
|
$database = $this->wire()->database;
|
||||||
$result = false;
|
$config = $this->wire()->config;
|
||||||
|
|
||||||
|
if(!$database || !$config) return false;
|
||||||
|
|
||||||
|
if(is_object($obj)) {
|
||||||
|
// check to see if it is worthwhile to perform this kind of maintenance at all
|
||||||
|
if($this->usePageTemplateMaintenance === null) {
|
||||||
|
$rows = $this->cacher()->find(array(
|
||||||
|
'get' => array('name'),
|
||||||
|
'expiresMode' => 'OR',
|
||||||
|
'expires' => array(
|
||||||
|
'= ' . self::expireSave,
|
||||||
|
'= ' . self::expireSelector
|
||||||
|
)
|
||||||
|
));
|
||||||
|
if(!count($rows)) {
|
||||||
|
$templates = $this->wire()->templates;
|
||||||
|
if(!$templates) $templates = array();
|
||||||
|
$minID = 999999;
|
||||||
|
$maxID = 0;
|
||||||
|
foreach($templates as $template) {
|
||||||
|
if($template->id > $maxID) $maxID = $template->id;
|
||||||
|
if($template->id < $minID) $minID = $template->id;
|
||||||
|
}
|
||||||
|
$rows = $this->cacher()->find(array(
|
||||||
|
'get' => array('name'),
|
||||||
|
'expiresMode' => 'AND',
|
||||||
|
'expires' => array(
|
||||||
|
'>= ' . date(self::dateFormat, $minID),
|
||||||
|
'<= ' . date(self::dateFormat, $maxID),
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
$this->usePageTemplateMaintenance = count($rows);
|
||||||
|
}
|
||||||
|
if($this->usePageTemplateMaintenance) {
|
||||||
|
if($obj instanceof Page) return $this->maintenancePage($obj);
|
||||||
|
if($obj instanceof Template) return $this->maintenanceTemplate($obj);
|
||||||
|
} else {
|
||||||
|
// skip it: no possible caches to maintain
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if($obj === true) {
|
||||||
|
// force run general maintenance, even if run earlier
|
||||||
|
$forceRun = true;
|
||||||
|
$done = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// general maintenance
|
||||||
|
if($done) return true;
|
||||||
|
$done = true;
|
||||||
}
|
}
|
||||||
return $result;
|
|
||||||
|
// don't perform general maintenance during ajax requests
|
||||||
|
if($config->ajax && !$forceRun) return false;
|
||||||
|
|
||||||
|
if(!$forceRun) {
|
||||||
|
// run general maintenance only once every 10 minutes
|
||||||
|
$filename = $this->wire()->config->paths->cache . 'WireCache.maint';
|
||||||
|
if(@filemtime($filename) > (time() - 600)) return false;
|
||||||
|
touch($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform general maintenance now
|
||||||
|
return $this->maintenanceGeneral();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General maintenance removes expired caches
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function maintenanceGeneral() {
|
||||||
|
|
||||||
|
$rows = $this->cacher()->find(array(
|
||||||
|
'get' => array('name'),
|
||||||
|
'expiresMode' => 'AND',
|
||||||
|
'expires' => array(
|
||||||
|
'<= ' . date(self::dateFormat, time()),
|
||||||
|
'> ' . self::expireNever
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
$qty = 0;
|
||||||
|
|
||||||
|
foreach($rows as $row) {
|
||||||
|
if($this->delete($row['name'])) $qty++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($qty) $this->log(sprintf('General maintenance expired %d cache(s)', $qty));
|
||||||
|
|
||||||
|
return $qty > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run maintenance for a page that was just saved or deleted
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function maintenancePage(Page $page) {
|
||||||
|
|
||||||
|
$qty = 0;
|
||||||
|
|
||||||
|
if($this->cacheNameSelectors === null) {
|
||||||
|
// locate all caches that specify selector strings and cache them so that
|
||||||
|
// we don't have to re-load them on every page save
|
||||||
|
$this->cacheNameSelectors = array();
|
||||||
|
$rows = $this->cacher()->find(array(
|
||||||
|
'expires' => array(
|
||||||
|
'= ' . self::expireSelector
|
||||||
|
)
|
||||||
|
));
|
||||||
|
foreach($rows as $row) {
|
||||||
|
$data = json_decode($row['data'], true);
|
||||||
|
if($data === false || !isset($data['selector'])) continue;
|
||||||
|
$name = $row['name'];
|
||||||
|
/** @var Selectors $selectors */
|
||||||
|
$selectors = $this->wire(new Selectors($data['selector']));
|
||||||
|
$this->cacheNameSelectors[$name] = $selectors;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// cacheNameSelectors already loaded once and is in cache
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine which selectors match the page: the $clearNames array
|
||||||
|
// will hold the selectors that match this $page
|
||||||
|
foreach($this->cacheNameSelectors as $name => $selectors) {
|
||||||
|
if($page->matches($selectors)) {
|
||||||
|
if($this->delete($name)) $qty++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $this->cacher()->find(array(
|
||||||
|
'get' => array('name'),
|
||||||
|
'expiresMode' => 'OR',
|
||||||
|
'expires' => array(
|
||||||
|
'= ' . self::expireSave,
|
||||||
|
'= ' . date(self::dateFormat, $page->template->id)
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach($rows as $row) {
|
||||||
|
if($this->delete($row['name'])) $qty++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($qty) $this->log(sprintf('Maintenance expired %d cache(s) for saved page', $qty));
|
||||||
|
|
||||||
|
return $qty > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run maintenance for a template that was just saved or deleted
|
||||||
|
*
|
||||||
|
* @param Template $template
|
||||||
|
* @return bool Returns true if any caches were deleted, false if not
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function maintenanceTemplate(Template $template) {
|
||||||
|
|
||||||
|
$rows = $this->cacher()->find(array(
|
||||||
|
'get' => array('name'),
|
||||||
|
'expiresMode' => 'OR',
|
||||||
|
'expires' => array(
|
||||||
|
'= ' . self::expireSave,
|
||||||
|
'= ' . date(self::dateFormat, $template->id)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
$qty = 0;
|
||||||
|
|
||||||
|
foreach($rows as $row) {
|
||||||
|
if($this->delete($row['name'])) $qty++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($qty) $this->log(sprintf('Maintenance expired %d cache(s) for saved template', $qty));
|
||||||
|
|
||||||
|
return $qty > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -769,19 +1056,95 @@ class WireCache extends Wire {
|
|||||||
* @param bool $verbose Whether to be more verbose for human readability
|
* @param bool $verbose Whether to be more verbose for human readability
|
||||||
* @param array|string $names Optionally specify name(s) of cache to get info. If omitted, all caches are included.
|
* @param array|string $names Optionally specify name(s) of cache to get info. If omitted, all caches are included.
|
||||||
* @param array|string $exclude Exclude any caches that begin with any of these namespaces (default=[])
|
* @param array|string $exclude Exclude any caches that begin with any of these namespaces (default=[])
|
||||||
|
* @param array $cols Columns to get, default = [ 'name', 'expires', 'data', 'size' ]
|
||||||
* @return array of arrays of cache info
|
* @return array of arrays of cache info
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function getInfo($verbose = true, $names = array(), $exclude = array()) {
|
public function getInfo($verbose = true, $names = array(), $exclude = array(), array $cols = array()) {
|
||||||
|
|
||||||
if(is_string($names)) $names = empty($names) ? array() : array($names);
|
if(is_string($names)) $names = empty($names) ? array() : array($names);
|
||||||
if(is_string($exclude)) $exclude = empty($exclude) ? array() : array($exclude);
|
if(is_string($exclude)) $exclude = empty($exclude) ? array() : array($exclude);
|
||||||
|
if(empty($cols)) $cols = array('name', 'expires', 'data', 'size');
|
||||||
|
|
||||||
return $this->cacher()->getInfo(array(
|
$all = array();
|
||||||
'verbose' => $verbose,
|
$options = count($names) ? array('names' => $names) : array();
|
||||||
'names' => $names,
|
$options['get'] = $cols;
|
||||||
'exclude' => $exclude
|
$templates = $this->wire()->templates;
|
||||||
));
|
|
||||||
|
foreach($this->cacher()->find($options) as $row) {
|
||||||
|
|
||||||
|
if(count($exclude)) {
|
||||||
|
$skip = false;
|
||||||
|
foreach($exclude as $value) {
|
||||||
|
if(stripos($row['name'], $value) !== 0) continue;
|
||||||
|
$skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if($skip) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info = array(
|
||||||
|
'name' => $row['name'],
|
||||||
|
'type' => 'string',
|
||||||
|
'expires' => '',
|
||||||
|
'size' => 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if(isset($row['data']) && $this->looksLikeJSON($row['data'])) {
|
||||||
|
// json encoded
|
||||||
|
$data = json_decode($row['data'], true);
|
||||||
|
if(is_array($data)) {
|
||||||
|
if(array_key_exists('WireCache', $data)) {
|
||||||
|
if(isset($data['selector'])) {
|
||||||
|
$selector = $data['selector'];
|
||||||
|
$info['expires'] = $verbose ? 'when selector matches modified page' : 'selector';
|
||||||
|
$info['selector'] = $selector;
|
||||||
|
}
|
||||||
|
$data = $data['WireCache'];
|
||||||
|
}
|
||||||
|
if(is_array($data) && array_key_exists('PageArray', $data) && array_key_exists('template', $data)) {
|
||||||
|
$info['type'] = 'PageArray';
|
||||||
|
if($verbose) $info['type'] .= ' (' . count($data['PageArray']) . ' pages)';
|
||||||
|
} else if(is_array($data)) {
|
||||||
|
$info['type'] = 'array';
|
||||||
|
if($verbose) $info['type'] .= ' (' . count($data) . ' items)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($info['expires'])) {
|
||||||
|
if($row['expires'] === WireCache::expireNever) {
|
||||||
|
$info['expires'] = $verbose ? 'never' : '';
|
||||||
|
} else if($row['expires'] === WireCache::expireReserved) {
|
||||||
|
$info['expires'] = $verbose ? 'reserved' : '';
|
||||||
|
} else if($row['expires'] === WireCache::expireSave) {
|
||||||
|
$info['expires'] = $verbose ? 'when any page or template is modified' : 'save';
|
||||||
|
} else if($row['expires'] < WireCache::expireSave) {
|
||||||
|
// potential template ID encoded as date string
|
||||||
|
$templateId = strtotime($row['expires']);
|
||||||
|
$template = $templates->get($templateId);
|
||||||
|
if($template) {
|
||||||
|
$info['expires'] = $verbose ? "when '$template->name' page or template is modified" : 'save';
|
||||||
|
$info['template'] = $template->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(empty($info['expires'])) {
|
||||||
|
$info['expires'] = $row['expires'];
|
||||||
|
if($verbose) $info['expires'] .= " (" . wireRelativeTimeStr($row['expires']) . ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($row['size'])) {
|
||||||
|
$info['size'] = $row['size'];
|
||||||
|
} else if(isset($row['data'])) {
|
||||||
|
$info['size'] = strlen($row['data']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$all[] = $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $all;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -943,7 +1306,7 @@ class WireCache extends Wire {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function looksLikeJSON(&$str) {
|
public function looksLikeJSON(&$str) {
|
||||||
if(empty($str)) return false;
|
if(empty($str) || !is_string($str)) return false;
|
||||||
$c = substr($str, 0, 1);
|
$c = substr($str, 0, 1);
|
||||||
if($c === '{' && substr(trim($str), -1) === '}') return true;
|
if($c === '{' && substr(trim($str), -1) === '}') return true;
|
||||||
if($c === '[' && substr(trim($str), -1) === ']') return true;
|
if($c === '[' && substr(trim($str), -1) === ']') return true;
|
||||||
@@ -989,6 +1352,39 @@ class WireCache extends Wire {
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set WireCache module to use for caching
|
||||||
|
*
|
||||||
|
* @param WireCacheInterface $module
|
||||||
|
* @since 3.0.218
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function setCacheModule(WireCacheInterface $module) {
|
||||||
|
/** @var Wire|WireCacheInterface $module */
|
||||||
|
if($this->cacher !== null && $this->cacher->className() !== self::defaultCacheClass) {
|
||||||
|
$class1 = $this->cacher->className();
|
||||||
|
$class2 = $module->className();
|
||||||
|
$user = $this->wire()->user;
|
||||||
|
if($user && $user->isSuperuser()) {
|
||||||
|
$this->warning(
|
||||||
|
"Warning: there is more than one WireCache module installed. " .
|
||||||
|
"Please uninstall '$class1' or '$class2'."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->cacher = $module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get WireCache module that is currently being used
|
||||||
|
*
|
||||||
|
* @return WireCacheInterface
|
||||||
|
* @since 3.0.218
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function getCacheModule() {
|
||||||
|
return $this->cacher();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save to the cache log
|
* Save to the cache log
|
||||||
|
@@ -11,117 +11,112 @@
|
|||||||
*/
|
*/
|
||||||
class WireCacheDatabase extends Wire implements WireCacheInterface {
|
class WireCacheDatabase extends Wire implements WireCacheInterface {
|
||||||
|
|
||||||
const useLog = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Memory cache used by the maintenancePage method
|
* Find caches by names or expirations and return requested values
|
||||||
*
|
*
|
||||||
* @var array|null Once determined becomes array of cache names => Selectors objects
|
* @param array $options
|
||||||
|
* - `names` (array): Names of caches to find (OR condition), optionally appended with wildcard `*`.
|
||||||
|
* - `expires` (array): Expirations of caches to match in ISO-8601 date format, prefixed with operator and space (see expiresMode mode below).
|
||||||
|
* - `expiresMode` (string): Whether it should match any one condition 'OR', or all conditions 'AND' (default='OR')
|
||||||
|
* - `get` (array): Properties to get in return value, one or more of [ `name`, `expires`, `data`, `size` ] (default=all)
|
||||||
|
* @return array Returns array of associative arrays, each containing requested properties
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected $cacheNameSelectors = null;
|
public function find(array $options) {
|
||||||
|
|
||||||
/**
|
$defaults = array(
|
||||||
* Whether or not it's worthwhile to attempt Page or Template maintenance after saves
|
'names' => array(),
|
||||||
*
|
'expires' => array(),
|
||||||
* @var null|bool
|
'expiresMode' => 'OR',
|
||||||
*
|
'get' => array('name', 'expires', 'data'),
|
||||||
*/
|
);
|
||||||
protected $usePageTemplateMaintenance = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get cache by name
|
|
||||||
*
|
|
||||||
* @param string $name Cache name to get
|
|
||||||
* @param string|array|null $expire Datetime in 'YYYY-MM-DD HH:MM:SS' format or array of them, or null for any
|
|
||||||
* @return string|false
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function get($name, $expire) {
|
|
||||||
$values = $this->getMultiple(array($name), $expire);
|
|
||||||
return count($values) ? reset($values) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find multiple caches by name and return them
|
|
||||||
*
|
|
||||||
* @param array $names Cache names to get
|
|
||||||
* @param string|array|null|false $expire Datetime in 'YYYY-MM-DD HH:MM:SS' format or array of them, or null for any, false to ignore
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getMultiple(array $names, $expire) {
|
|
||||||
|
|
||||||
|
$database = $this->wire()->database;
|
||||||
|
$options = array_merge($defaults, $options);
|
||||||
$where = array();
|
$where = array();
|
||||||
|
$whereNames = array();
|
||||||
|
$whereExpires = array();
|
||||||
$binds = array();
|
$binds = array();
|
||||||
$n = 0;
|
$cols = array();
|
||||||
|
|
||||||
foreach($names as $s) {
|
if(count($options['names'])) {
|
||||||
$n++;
|
$n = 0;
|
||||||
if(strpos($s, '*') !== false) {
|
foreach($options['names'] as $name) {
|
||||||
// retrieve all caches matching wildcard
|
$n++;
|
||||||
$s = str_replace('*', '%', $s);
|
if(strpos($name, '*') !== false) {
|
||||||
$where[$n] = "name LIKE :name$n";
|
$name = str_replace('*', '%', $name);
|
||||||
} else {
|
$whereNames[] = "name LIKE :name$n";
|
||||||
$where[$n] = "name=:name$n";
|
} else {
|
||||||
|
$whereNames[] = "name=:name$n";
|
||||||
|
}
|
||||||
|
$binds[":name$n"] = $name;
|
||||||
}
|
}
|
||||||
$binds[":name$n"] = $s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql = "SELECT name, data FROM caches WHERE (" . implode(' OR ', $where) . ") ";
|
if(count($options['expires'])) {
|
||||||
|
$n = 0;
|
||||||
|
foreach($options['expires'] as $expires) {
|
||||||
|
$operator = '=';
|
||||||
|
if(strpos($expires, ' ')) {
|
||||||
|
// string in format: '>= YYYY-MM-DD HH:MM:SS'
|
||||||
|
list($op, $expires) = explode(' ', $expires, 2);
|
||||||
|
if($database->isOperator($op)) $operator = $op;
|
||||||
|
}
|
||||||
|
$n++;
|
||||||
|
$whereExpires[] = "expires$operator:expires$n";
|
||||||
|
$binds[":expires$n"] = $expires;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($expire === null) {
|
if(count($whereNames)) {
|
||||||
$sql .= "AND (expires>=:now OR expires<=:never) ";
|
$where[] = '(' . implode(' OR ', $whereNames) . ')';
|
||||||
$binds[':now'] = date(WireCache::dateFormat, time());
|
}
|
||||||
$binds[':never'] = WireCache::expireNever;
|
|
||||||
} else if($expire === WireCache::expireIgnore) {
|
if(count($whereExpires)) {
|
||||||
// ignore expiration
|
$mode = strtoupper($options['expiresMode']) === 'AND' ? 'AND' : 'OR';
|
||||||
} else if(is_array($expire)) {
|
$where[] = '(' . implode(" $mode ", $whereExpires) . ')';
|
||||||
// expire is specified by a page selector, so we just let it through
|
}
|
||||||
// since anything present is assumed to be valid
|
|
||||||
|
foreach($options['get'] as $col) {
|
||||||
|
if($col === 'name' || $col === 'expires' || $col === 'data') $cols[] = $col;
|
||||||
|
if($col === 'size') $cols[] = 'LENGTH(data) AS size';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($cols)) return array();
|
||||||
|
|
||||||
|
$sql = 'SELECT ' . implode(',', $cols) . ' FROM caches ';
|
||||||
|
if(count($where)) {
|
||||||
|
$sql .= 'WHERE ' . implode(' AND ', $where);
|
||||||
} else {
|
} else {
|
||||||
$sql .= "AND expires<=:expire ";
|
// getting all
|
||||||
$binds[':expire'] = $expire;
|
$sql .= 'ORDER BY name';
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->wire()->database->prepare($sql, "cache.get(" .
|
$query = $database->prepare($sql);
|
||||||
implode('|', $names) . ", " . ($expire ? print_r($expire, true) : "null") . ")");
|
|
||||||
|
|
||||||
foreach($binds as $key => $value) {
|
foreach($binds as $bindKey => $bindValue) {
|
||||||
$query->bindValue($key, $value);
|
$query->bindValue($bindKey, $bindValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
$values = array(); // return value for multi-mode
|
if(!$this->executeQuery($query)) return array();
|
||||||
|
|
||||||
$query->execute();
|
|
||||||
|
|
||||||
if(!$query->rowCount()) return $values;
|
|
||||||
|
|
||||||
while($row = $query->fetch(\PDO::FETCH_NUM)) {
|
|
||||||
list($name, $value) = $row;
|
|
||||||
$values[$name] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$rows = $query->fetchAll(\PDO::FETCH_ASSOC);
|
||||||
$query->closeCursor();
|
$query->closeCursor();
|
||||||
|
|
||||||
return $values;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a cache
|
* Save a cache
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name Name of cache
|
||||||
* @param string $data
|
* @param string $data Data to save in cache
|
||||||
* @param string $expire
|
* @param string $expire String in ISO-8601 date format
|
||||||
* @return bool
|
* @return bool
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function save($name, $data, $expire) {
|
public function save($name, $data, $expire) {
|
||||||
|
|
||||||
if($expire === WireCache::expireSelector) {
|
|
||||||
$this->cacheNameSelectors = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql =
|
$sql =
|
||||||
'INSERT INTO caches (`name`, `data`, `expires`) VALUES(:name, :data, :expires) ' .
|
'INSERT INTO caches (`name`, `data`, `expires`) VALUES(:name, :data, :expires) ' .
|
||||||
'ON DUPLICATE KEY UPDATE `data`=VALUES(`data`), `expires`=VALUES(`expires`)';
|
'ON DUPLICATE KEY UPDATE `data`=VALUES(`data`), `expires`=VALUES(`expires`)';
|
||||||
@@ -131,7 +126,7 @@ class WireCacheDatabase extends Wire implements WireCacheInterface {
|
|||||||
$query->bindValue(':data', $data);
|
$query->bindValue(':data', $data);
|
||||||
$query->bindValue(':expires', $expire);
|
$query->bindValue(':expires', $expire);
|
||||||
|
|
||||||
$result = $query->execute();
|
$result = $this->executeQuery($query);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
@@ -144,390 +139,94 @@ class WireCacheDatabase extends Wire implements WireCacheInterface {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function delete($name) {
|
public function delete($name) {
|
||||||
if(strpos($name, '*') !== false) {
|
$sql = 'DELETE FROM caches WHERE name=:name';
|
||||||
// delete all caches matching wildcard
|
|
||||||
$name = str_replace('*', '%', $name);
|
|
||||||
if($name === '%') return $this->deleteAll() ? true : false;
|
|
||||||
$sql = 'DELETE FROM caches WHERE name LIKE :name';
|
|
||||||
} else {
|
|
||||||
$sql = 'DELETE FROM caches WHERE name=:name';
|
|
||||||
}
|
|
||||||
$query = $this->wire()->database->prepare($sql, "cache.delete($name)");
|
$query = $this->wire()->database->prepare($sql, "cache.delete($name)");
|
||||||
$query->bindValue(':name', $name);
|
$query->bindValue(':name', $name);
|
||||||
$result = $query->execute();
|
if(!$this->executeQuery($query)) return false;
|
||||||
$query->closeCursor();
|
$query->closeCursor();
|
||||||
return $result;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all caches
|
* Delete all caches (except those reserved by the system)
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function deleteAll() {
|
public function deleteAll() {
|
||||||
$sql = "DELETE FROM caches WHERE expires!=:reserved";
|
return $this->_deleteAll();
|
||||||
$query = $this->wire()->database->prepare($sql, "cache.deleteAll()");
|
|
||||||
$query->bindValue(':reserved', WireCache::expireReserved);
|
|
||||||
$query->execute();
|
|
||||||
$qty = $query->rowCount();
|
|
||||||
$query->closeCursor();
|
|
||||||
return $qty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expire all caches
|
* Expire all caches (except those that should never expire)
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function expireAll() {
|
public function expireAll() {
|
||||||
$sql = "DELETE FROM caches WHERE expires>:never";
|
return $this->_deleteAll(true);
|
||||||
$query = $this->wire()->database->prepare($sql, "cache.expireAll()");
|
}
|
||||||
$query->bindValue(':never', WireCache::expireNever);
|
|
||||||
$query->execute();
|
/**
|
||||||
|
* Implementation for deleteAll and expireAll methods
|
||||||
|
*
|
||||||
|
* @param bool $expireAll
|
||||||
|
* @return int
|
||||||
|
* @throws WireException
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function _deleteAll($expireAll = false) {
|
||||||
|
$sql = 'DELETE FROM caches WHERE ' . ($expireAll ? 'expires>:never' : 'expires!=:reserved');
|
||||||
|
$query = $this->wire()->database->prepare($sql, "cache.deleteAll()");
|
||||||
|
$query->bindValue(':expires', ($expireAll ? WireCache::expireNever : WireCache::expireReserved));
|
||||||
|
if(!$this->executeQuery($query)) return 0;
|
||||||
$qty = $query->rowCount();
|
$qty = $query->rowCount();
|
||||||
$query->closeCursor();
|
$query->closeCursor();
|
||||||
return $qty;
|
return $qty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache maintenance removes expired caches
|
* Execute query
|
||||||
*
|
*
|
||||||
* Should be called as part of a regular maintenance routine and after page/template save/deletion.
|
* @param \PDOStatement $query
|
||||||
* ProcessWire already calls this automatically, so you don’t typically need to call this method on your own.
|
|
||||||
*
|
|
||||||
* #pw-group-advanced
|
|
||||||
*
|
|
||||||
* @param Template|Page|null|bool Item to run maintenance for or, if not specified, general maintenance is performed.
|
|
||||||
* General maintenance only runs once per request. Specify boolean true to force general maintenance to run.
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function maintenance($obj = null) {
|
protected function executeQuery(\PDOStatement $query) {
|
||||||
|
$install = false;
|
||||||
|
try {
|
||||||
|
$result = $query->execute();
|
||||||
|
} catch(\PDOException $e) {
|
||||||
|
$result = false;
|
||||||
|
$install = $e->getCode() === '42S02'; // table does not exist
|
||||||
|
if(!$install) throw $e;
|
||||||
|
}
|
||||||
|
if($install) $this->install();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
static $done = false;
|
/**
|
||||||
|
* Create the caches table if it happens to have been deleted
|
||||||
$forceRun = false;
|
*
|
||||||
|
*/
|
||||||
|
protected function install() {
|
||||||
$database = $this->wire()->database;
|
$database = $this->wire()->database;
|
||||||
$config = $this->wire()->config;
|
$config = $this->wire()->config;
|
||||||
|
$dbEngine = $config->dbEngine;
|
||||||
if(!$database || !$config) return false;
|
$dbCharset = $config->dbCharset;
|
||||||
|
if($database->tableExists('caches')) return;
|
||||||
if(is_object($obj)) {
|
try {
|
||||||
|
$this->wire()->database->exec("
|
||||||
// check to see if it is worthwhile to perform this kind of maintenance at all
|
CREATE TABLE caches (
|
||||||
if($this->usePageTemplateMaintenance === null) {
|
`name` VARCHAR(191) NOT NULL PRIMARY KEY,
|
||||||
$templates = $this->wire()->templates;
|
`data` MEDIUMTEXT NOT NULL,
|
||||||
if(!$templates) $templates = array();
|
`expires` DATETIME NOT NULL,
|
||||||
$minID = 999999;
|
INDEX `expires` (`expires`)
|
||||||
$maxID = 0;
|
) ENGINE=$dbEngine DEFAULT CHARSET=$dbCharset;
|
||||||
foreach($templates as $template) {
|
");
|
||||||
if($template->id > $maxID) $maxID = $template->id;
|
$this->message("Re-created 'caches' table");
|
||||||
if($template->id < $minID) $minID = $template->id;
|
} catch(\Exception $e) {
|
||||||
}
|
$this->error("Unable to create 'caches' table");
|
||||||
$sql =
|
|
||||||
"SELECT COUNT(*) FROM caches " .
|
|
||||||
"WHERE (expires=:expireSave OR expires=:expireSelector) " .
|
|
||||||
"OR (expires>=:minID AND expires<=:maxID)";
|
|
||||||
|
|
||||||
$query = $database->prepare($sql);
|
|
||||||
$query->bindValue(':expireSave', WireCache::expireSave);
|
|
||||||
$query->bindValue(':expireSelector', WireCache::expireSelector);
|
|
||||||
$query->bindValue(':minID', date(WireCache::dateFormat, $minID));
|
|
||||||
$query->bindValue(':maxID', date(WireCache::dateFormat, $maxID));
|
|
||||||
$query->execute();
|
|
||||||
$this->usePageTemplateMaintenance = (int) $query->fetchColumn();
|
|
||||||
$query->closeCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->usePageTemplateMaintenance) {
|
|
||||||
if($obj instanceof Page) return $this->maintenancePage($obj);
|
|
||||||
if($obj instanceof Template) return $this->maintenanceTemplate($obj);
|
|
||||||
} else {
|
|
||||||
// skip it: no possible caches to maintain
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} else if($obj === true) {
|
|
||||||
// force run general maintenance, even if run earlier
|
|
||||||
$forceRun = true;
|
|
||||||
$done = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// general maintenance: only perform maintenance once per request
|
|
||||||
if($done) return true;
|
|
||||||
$done = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't perform general maintenance during ajax requests
|
|
||||||
if($config->ajax && !$forceRun) return false;
|
|
||||||
|
|
||||||
// perform general maintenance now
|
|
||||||
return $this->maintenanceGeneral();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* General maintenance removes expired caches
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function maintenanceGeneral() {
|
|
||||||
|
|
||||||
$database = $this->wire()->database;
|
|
||||||
|
|
||||||
$sql = 'DELETE FROM caches WHERE (expires<=:now AND expires>:never) ';
|
|
||||||
$query = $database->prepare($sql, "cache.maintenance()");
|
|
||||||
$query->bindValue(':now', date(WireCache::dateFormat, time()));
|
|
||||||
$query->bindValue(':never', WireCache::expireNever);
|
|
||||||
|
|
||||||
$result = $query->execute();
|
|
||||||
$qty = $result ? $query->rowCount() : 0;
|
|
||||||
if(self::useLog && $qty) $this->wire()->cache->log(sprintf('General maintenance expired %d cache(s)', $qty));
|
|
||||||
$query->closeCursor();
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run maintenance for a page that was just saved or deleted
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function maintenancePage(Page $page) {
|
|
||||||
|
|
||||||
$database = $this->wire()->database;
|
|
||||||
|
|
||||||
if($this->cacheNameSelectors === null) {
|
|
||||||
// locate all caches that specify selector strings and cache them so that
|
|
||||||
// we don't have to re-load them on every page save
|
|
||||||
$this->cacheNameSelectors = array();
|
|
||||||
try {
|
|
||||||
$query = $database->prepare("SELECT * FROM caches WHERE expires=:expire");
|
|
||||||
$query->bindValue(':expire', WireCache::expireSelector);
|
|
||||||
$query->execute();
|
|
||||||
} catch(\Exception $e) {
|
|
||||||
$this->trackException($e, false);
|
|
||||||
$this->error($e->getMessage(), Notice::log);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if($query->rowCount()) {
|
|
||||||
while($row = $query->fetch(\PDO::FETCH_ASSOC)) {
|
|
||||||
$data = json_decode($row['data'], true);
|
|
||||||
if($data !== false && isset($data['selector'])) {
|
|
||||||
$name = $row['name'];
|
|
||||||
$selectors = $this->wire(new Selectors($data['selector']));
|
|
||||||
$this->cacheNameSelectors[$name] = $selectors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// cacheNameSelectors already loaded once and is in cache
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine which selectors match the page: the $clearNames array
|
|
||||||
// will hold the selectors that match this $page
|
|
||||||
$n = 0;
|
|
||||||
$clearNames = array();
|
|
||||||
foreach($this->cacheNameSelectors as $name => $selectors) {
|
|
||||||
if($page->matches($selectors)) {
|
|
||||||
$clearNames["name" . (++$n)] = $name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear any caches that expire on expireSave or specific page template
|
|
||||||
$sql = "expires=:expireSave OR expires=:expireTemplateID ";
|
|
||||||
|
|
||||||
// expire any caches that match names found in cacheNameSelectors
|
|
||||||
foreach($clearNames as $key => $name) {
|
|
||||||
$sql .= "OR name=:$key ";
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = $database->prepare("DELETE FROM caches WHERE $sql");
|
|
||||||
|
|
||||||
// bind values
|
|
||||||
$query->bindValue(':expireSave', WireCache::expireSave);
|
|
||||||
$query->bindValue(':expireTemplateID', date(WireCache::dateFormat, $page->template->id));
|
|
||||||
|
|
||||||
foreach($clearNames as $key => $name) {
|
|
||||||
$query->bindValue(":$key", $name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = $query->execute();
|
|
||||||
$qty = $result ? $query->rowCount() : 0;
|
|
||||||
if(self::useLog && $qty) {
|
|
||||||
$this->wire()->cache->log(sprintf('Maintenance expired %d cache(s) for saved page', $qty));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run maintenance for a template that was just saved or deleted
|
|
||||||
*
|
|
||||||
* @param Template $template
|
|
||||||
* @return bool Returns true if any caches were deleted, false if not
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function maintenanceTemplate(Template $template) {
|
|
||||||
|
|
||||||
$sql = 'DELETE FROM caches WHERE expires=:expireTemplateID OR expires=:expireSave';
|
|
||||||
$query = $this->wire()->database->prepare($sql);
|
|
||||||
|
|
||||||
$query->bindValue(':expireSave', WireCache::expireSave);
|
|
||||||
$query->bindValue(':expireTemplateID', date(WireCache::dateFormat, $template->id));
|
|
||||||
|
|
||||||
$result = $query->execute();
|
|
||||||
$qty = $result ? $query->rowCount() : 0;
|
|
||||||
if(self::useLog && $qty) $this->wire()->cache->log(sprintf('Maintenance expired %d cache(s) for saved template', $qty));
|
|
||||||
|
|
||||||
return $qty > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get info about caches
|
|
||||||
*
|
|
||||||
* @param array $options
|
|
||||||
* - `verbose` (bool): Return verbose details? (default=true)
|
|
||||||
* - `names` (array): Names of caches to return info for, or omit for all (default=[])
|
|
||||||
* - `exclude` (array): Name prefixes of caches to exclude from return value (default=[])
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getInfo(array $options = array()) {
|
|
||||||
|
|
||||||
$templates = $this->wire()->templates;
|
|
||||||
$database = $this->wire()->database;
|
|
||||||
|
|
||||||
$defaults = array(
|
|
||||||
'verbose' => true,
|
|
||||||
'names' => array(),
|
|
||||||
'exclude' => array()
|
|
||||||
);
|
|
||||||
|
|
||||||
$options = array_merge($defaults, $options);
|
|
||||||
$verbose = (bool) $options['verbose'];
|
|
||||||
$names = $options['names'];
|
|
||||||
$exclude = $options['exclude'];
|
|
||||||
|
|
||||||
$all = array();
|
|
||||||
$binds = array();
|
|
||||||
$wheres = array();
|
|
||||||
$sql = "SELECT name, data, expires FROM caches ";
|
|
||||||
|
|
||||||
if(count($names)) {
|
|
||||||
$a = array();
|
|
||||||
foreach($names as $n => $s) {
|
|
||||||
$a[] = "name=:name$n";
|
|
||||||
$binds[":name$n"] = $s;
|
|
||||||
}
|
|
||||||
$wheres[] = '(' . implode(' OR ', $a) . ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($exclude)) {
|
|
||||||
foreach($exclude as $n => $s) {
|
|
||||||
$wheres[] = "name NOT LIKE :ex$n";
|
|
||||||
$binds[":ex$n"] = $s . '%';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($wheres)) {
|
|
||||||
$sql .= "WHERE " . implode(' AND ', $wheres);
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = $database->prepare($sql);
|
|
||||||
|
|
||||||
foreach($binds as $key => $val) {
|
|
||||||
$query->bindValue($key, $val);
|
|
||||||
}
|
|
||||||
|
|
||||||
$query->execute();
|
|
||||||
|
|
||||||
while($row = $query->fetch(\PDO::FETCH_ASSOC)) {
|
|
||||||
|
|
||||||
$info = array(
|
|
||||||
'name' => $row['name'],
|
|
||||||
'type' => 'string',
|
|
||||||
'expires' => '',
|
|
||||||
);
|
|
||||||
|
|
||||||
if($this->wire()->cache->looksLikeJSON($row['data'])) {
|
|
||||||
// json encoded
|
|
||||||
$data = json_decode($row['data'], true);
|
|
||||||
if(is_array($data)) {
|
|
||||||
if(array_key_exists('WireCache', $data)) {
|
|
||||||
if(isset($data['selector'])) {
|
|
||||||
$selector = $data['selector'];
|
|
||||||
$info['expires'] = $verbose ? 'when selector matches modified page' : 'selector';
|
|
||||||
$info['selector'] = $selector;
|
|
||||||
}
|
|
||||||
$data = $data['WireCache'];
|
|
||||||
}
|
|
||||||
if(is_array($data) && array_key_exists('PageArray', $data) && array_key_exists('template', $data)) {
|
|
||||||
$info['type'] = 'PageArray';
|
|
||||||
if($verbose) $info['type'] .= ' (' . count($data['PageArray']) . ' pages)';
|
|
||||||
} else if(is_array($data)) {
|
|
||||||
$info['type'] = 'array';
|
|
||||||
if($verbose) $info['type'] .= ' (' . count($data) . ' items)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(empty($info['expires'])) {
|
|
||||||
if($row['expires'] === WireCache::expireNever) {
|
|
||||||
$info['expires'] = $verbose ? 'never' : '';
|
|
||||||
} else if($row['expires'] === WireCache::expireReserved) {
|
|
||||||
$info['expires'] = $verbose ? 'reserved' : '';
|
|
||||||
} else if($row['expires'] === WireCache::expireSave) {
|
|
||||||
$info['expires'] = $verbose ? 'when any page or template is modified' : 'save';
|
|
||||||
} else if($row['expires'] < WireCache::expireSave) {
|
|
||||||
// potential template ID encoded as date string
|
|
||||||
$templateId = strtotime($row['expires']);
|
|
||||||
$template = $templates->get($templateId);
|
|
||||||
if($template) {
|
|
||||||
$info['expires'] = $verbose ? "when '$template->name' page or template is modified" : 'save';
|
|
||||||
$info['template'] = $template->id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(empty($info['expires'])) {
|
|
||||||
$info['expires'] = $row['expires'];
|
|
||||||
if($verbose) $info['expires'] .= " (" . wireRelativeTimeStr($row['expires']) . ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($verbose) $info['size'] = strlen($row['data']);
|
|
||||||
|
|
||||||
$all[] = $info;
|
|
||||||
}
|
|
||||||
|
|
||||||
$query->closeCursor();
|
|
||||||
|
|
||||||
return $all;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save to the cache log
|
|
||||||
*
|
|
||||||
* #pw-internal
|
|
||||||
*
|
|
||||||
* @param string $str Message to log
|
|
||||||
* @param array $options
|
|
||||||
* @return WireLog
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___log($str = '', array $options = array()) {
|
|
||||||
//parent::___log($str, array('name' => 'cache'));
|
|
||||||
if(self::useLog) {
|
|
||||||
return $this->wire()->cache->log($str, $options);
|
|
||||||
} else {
|
|
||||||
$str = ''; // disable log
|
|
||||||
}
|
|
||||||
return parent::___log($str, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -263,17 +263,19 @@ echo
|
|||||||
<?php
|
<?php
|
||||||
$o = ''; $oc = 0;
|
$o = ''; $oc = 0;
|
||||||
$timers = Debug::getSavedTimers();
|
$timers = Debug::getSavedTimers();
|
||||||
|
$timer1 = '';
|
||||||
foreach($timers as $name => $timer) {
|
foreach($timers as $name => $timer) {
|
||||||
|
if($timer1 === '') $timer1 = $timer;
|
||||||
$o .= "<tr><th>$name</th><td>$timer</td></tr>";
|
$o .= "<tr><th>$name</th><td>$timer</td></tr>";
|
||||||
$oc++;
|
$oc++;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h3><a href='#'><?php echo __('Timers', __FILE__) . " <span class='ui-priority-secondary'>($oc)</span>"; ?></a></h3>
|
<h3><a href='#'><?php echo __('Timers', __FILE__) . " <span class='ui-priority-secondary'>($oc) {$timer1}s</span>"; ?></a></h3>
|
||||||
<div>
|
<div>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php echo $o; unset($o, $oc); ?>
|
<?php echo $o; unset($o, $oc, $timers, $name, $timer, $timer1); ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class='description'>To add more timers here…</p>
|
<p class='description'>To add more timers here…</p>
|
||||||
@@ -373,13 +375,13 @@ Debug::saveTimer('timer-name', 'optional notes'); // stop and save timer
|
|||||||
<?php
|
<?php
|
||||||
$o = '';
|
$o = '';
|
||||||
$oc = 0;
|
$oc = 0;
|
||||||
foreach($cache->getInfo(true, array(), array('FileCompiler', 'Modules', 'Permissions.')) as $info) {
|
foreach($cache->getInfo(true, array(), array('FileCompiler', 'Modules', 'Permissions.'), array('name', 'expires', 'size')) as $info) {
|
||||||
$oc++;
|
$oc++;
|
||||||
$o .= "<table class=''><thead><tr><th colspan='2'>";
|
$o .= "<table class=''><thead><tr><th colspan='2'>";
|
||||||
$o .= $sanitizer->entities($info['name']) . "</th></tr></thead><tbody>";
|
$o .= $sanitizer->entities($info['name']) . "</th></tr></thead><tbody>";
|
||||||
foreach($info as $key => $value) {
|
foreach($info as $key => $value) {
|
||||||
if($key == 'name') continue;
|
if($key === 'name' || $key === 'type') continue;
|
||||||
if($key == 'size') $value = wireBytesStr($value);
|
if($key === 'size') $value = wireBytesStr($value);
|
||||||
$key = $sanitizer->entities($key);
|
$key = $sanitizer->entities($key);
|
||||||
$value = $sanitizer->entities($value);
|
$value = $sanitizer->entities($value);
|
||||||
$o .= "<tr><th width='30%'>$key</th><td>$value</td></tr>";
|
$o .= "<tr><th width='30%'>$key</th><td>$value</td></tr>";
|
||||||
@@ -390,6 +392,7 @@ Debug::saveTimer('timer-name', 'optional notes'); // stop and save timer
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h3><a href='#'><?php echo __('Cache') . " <span class='ui-priority-secondary'>($oc)</span>"; ?></a></h3>
|
<h3><a href='#'><?php echo __('Cache') . " <span class='ui-priority-secondary'>($oc)</span>"; ?></a></h3>
|
||||||
<div>
|
<div>
|
||||||
|
<p class='detail'><?php echo $cache->getCacheModule()->className(); ?></p>
|
||||||
<?php echo $o; unset($o, $oc); ?>
|
<?php echo $o; unset($o, $oc); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user