Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor cache.php to better conform with PSR 2 #2689

Merged
merged 10 commits into from Feb 27, 2019
4 changes: 3 additions & 1 deletion _test/tests/inc/cache_stalecheck.test.php
@@ -1,5 +1,7 @@
<?php

use dokuwiki\Cache\CacheRenderer;

class cache_stalecheck_test extends DokuWikiTest {
function test_staleness() {
global $ID;
Expand All @@ -11,7 +13,7 @@ function test_staleness() {
saveWikiText($ID, 'Fresh', 'Created');

# Create stale cache
$cache = new cache_renderer($ID, $file, 'xhtml');
$cache = new CacheRenderer($ID, $file, 'xhtml');
$cache->storeCache('Stale');
$stale = $cache->retrieveCache();

Expand Down
8 changes: 5 additions & 3 deletions _test/tests/inc/cache_use.test.php
@@ -1,5 +1,7 @@
<?php

use dokuwiki\Cache\CacheRenderer;

/**
* Class cache_use_test
*
Expand All @@ -8,7 +10,7 @@
* @todo tests marked as flaky until Ticket #694 has been fixed
*/
class cache_use_test extends DokuWikiTest {
/** @var cache_renderer $cache */
/** @var CacheRenderer $cache */
private $cache;

function setUp() {
Expand All @@ -21,7 +23,7 @@ function setUp() {

saveWikiText($ID, 'Content', 'Created');

$this->cache = new cache_renderer($ID, $file, 'xhtml');
$this->cache = new CacheRenderer($ID, $file, 'xhtml');
$this->cache->storeCache('Test');

// set the modification times explicitly (overcome Issue #694)
Expand Down Expand Up @@ -76,6 +78,6 @@ function test_confnocaching() {
$conf['cachetime'] = -1; // disables renderer caching

$this->assertFalse($this->cache->useCache());
$this->assertNotEmpty($this->cache->_nocache);
$this->assertNotEmpty($this->cache->isNoCache());
}
}
5 changes: 3 additions & 2 deletions feed.php
Expand Up @@ -9,6 +9,7 @@
* @global Input $INPUT
*/

use dokuwiki\Cache\Cache;
use dokuwiki\ChangeLog\MediaChangeLog;
use dokuwiki\ChangeLog\PageChangeLog;

Expand All @@ -31,7 +32,7 @@
// the feed is dynamic - we need a cache for each combo
// (but most people just use the default feed so it's still effective)
$key = join('', array_values($opt)).'$'.$_SERVER['REMOTE_USER'].'$'.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'];
$cache = new cache($key, '.feed');
$cache = new Cache($key, '.feed');

// prepare cache depends
$depends['files'] = getConfigFiles('main');
Expand All @@ -45,7 +46,7 @@
header('Content-Type: application/xml; charset=utf-8');
header('X-Robots-Tag: noindex');
if($cache->useCache($depends)) {
http_conditionalRequest($cache->_time);
http_conditionalRequest($cache->getTime());
if($conf['allowdebug']) header("X-CacheUsed: $cache->cache");
print $cache->retrieveCache();
exit;
Expand Down
256 changes: 256 additions & 0 deletions inc/Cache/Cache.php
@@ -0,0 +1,256 @@
<?php

namespace dokuwiki\Cache;

/**
* Generic handling of caching
*/
class Cache
{
public $key = ''; // primary identifier for this item
public $ext = ''; // file ext for cache data, secondary identifier for this item
public $cache = ''; // cache file name
public $depends = array(); // array containing cache dependency information,
// used by makeDefaultCacheDecision to determine cache validity

protected $event = ''; // event to be triggered during useCache
protected $time;
protected $nocache = false; // if set to true, cache will not be used or stored

/**
* @param string $key primary identifier
* @param string $ext file extension
*/
public function __construct($key, $ext)
{
$this->key = $key;
$this->ext = $ext;
$this->cache = getCacheName($key, $ext);
}

/**
* @deprecated since 2019-02-02 use the respective getters instead!
*/
public function __get($key)
{
if ($key === '_event') {
trigger_error(
'\dokuwiki\Cache\Cache::_event is deprecated since 2019-02-02. Use \dokuwiki\Cache\Cache::getEvent()',
E_USER_DEPRECATED
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this error is useful. Don't these errors work like a non-catchable exception, ending code execution? Or do I remember this wrong? Deprecated accesses should continue to work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. The code

trigger_error(
    '\dokuwiki\Cache\Cache::_event is deprecated since 2019-02-02. Use \dokuwiki\Cache\Cache::getEvent()',
    E_USER_DEPRECATED
);

results only in the following output in my error log:

[Tue Feb 19 21:51:54.001036 2019] [php7:notice] [pid 487] [client 127.0.0.1:49030] PHP Deprecated:  \\dokuwiki\\Cache\\Cache::_event is deprecated since 2019-02-02. Use \\dokuwiki\\Cache\\Cache::getEvent() in /h
ome/michael/public_html/dokuwiki/inc/Cache/Cache.php on line 61, referer: http://127.0.0.1/~michael/dokuwiki/doku.php?id=start
[Tue Feb 19 21:51:54.001069 2019] [php7:notice] [pid 487] [client 127.0.0.1:49030] PHP Stack trace:, referer: http://127.0.0.1/~michael/dokuwiki/doku.php?id=start
[Tue Feb 19 21:51:54.001084 2019] [php7:notice] [pid 487] [client 127.0.0.1:49030] PHP   1. {main}() /home/michael/public_html/dokuwiki/lib/exe/css.php:0, referer: http://127.0.0.1/~michael/dokuwiki/doku.php?id=
start
[Tue Feb 19 21:51:54.001091 2019] [php7:notice] [pid 487] [client 127.0.0.1:49030] PHP   2. css_out() /home/michael/public_html/dokuwiki/lib/exe/css.php:20, referer: http://127.0.0.1/~michael/dokuwiki/doku.php?i
d=start
[Tue Feb 19 21:51:54.001104 2019] [php7:notice] [pid 487] [client 127.0.0.1:49030] PHP   3. dokuwiki\\Cache\\Cache->__set($name = '_event', $value = 'CSS_CACHE_USE') /home/michael/public_html/dokuwiki/lib/exe/cs
s.php:115, referer: http://127.0.0.1/~michael/dokuwiki/doku.php?id=start
[Tue Feb 19 21:51:54.001131 2019] [php7:notice] [pid 487] [client 127.0.0.1:49030] PHP   4. trigger_error('\\\\dokuwiki\\\\Cache\\\\Cache::_event is deprecated since 2019-02-02. Use \\\\dokuwiki\\\\Cache\\\\Cach
e::getEvent()', 16384) /home/michael/public_html/dokuwiki/inc/Cache/Cache.php:61, referer: http://127.0.0.1/~michael/dokuwiki/doku.php?id=start

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if you have display_errors on in your php.ini?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect that you then see those error on your website (which you want in dev).
One shouldn't have display_erros on in production.

That being said, maybe this isn't such a good idea after all. As I still get these notices in css.php and js.php where those error are especially annoying.

I will try to have another look at it on Thursday or Friday evening,

dbg_deprecated('\dokuwiki\Cache\Cache::getEvent()');
return $this->getEvent();
}

if ($key === '_time') {
trigger_error(
'\dokuwiki\Cache\Cache::_time is deprecated since 2019-02-02. Use \dokuwiki\Cache\Cache::getTime()',
E_USER_DEPRECATED
);
dbg_deprecated('\dokuwiki\Cache\Cache::getTime()');
return $this->getTime();
}
return $this->$$key;
}

public function __set($name, $value)
{
if ($name === '_event') {
trigger_error(
'\dokuwiki\Cache\Cache::_event is deprecated since 2019-02-02. Use \dokuwiki\Cache\Cache::getEvent()',
E_USER_DEPRECATED
);
dbg_deprecated('\dokuwiki\Cache\Cache::getEvent()');
$this->setEvent($value);
}
$this->$$name = $value;
}

public function getTime()
{
return $this->time;
}

public function getEvent()
{
return $this->event;
}

public function setEvent($event)
{
$this->event = $event;
}

/**
* public method to determine whether the cache can be used
*
* to assist in centralisation of event triggering and calculation of cache statistics,
* don't override this function override makeDefaultCacheDecision()
*
* @param array $depends array of cache dependencies, support dependecies:
* 'age' => max age of the cache in seconds
* 'files' => cache must be younger than mtime of each file
* (nb. dependency passes if file doesn't exist)
*
* @return bool true if cache can be used, false otherwise
*/
public function useCache($depends = array())
{
$this->depends = $depends;
$this->addDependencies();

if ($this->event) {
return $this->stats(trigger_event($this->event, $this, array($this, 'makeDefaultCacheDecision')));
} else {
return $this->stats($this->makeDefaultCacheDecision());
}
}

/**
* internal method containing cache use decision logic
*
* this function processes the following keys in the depends array
* purge - force a purge on any non empty value
* age - expire cache if older than age (seconds)
* files - expire cache if any file in this array was updated more recently than the cache
*
* Note that this function needs to be public as it is used as callback for the event handler
*
* can be overridden
*
* @internal This method may only be called by the event handler! Call \dokuwiki\Cache\Cache::useCache instead!
*
* @return bool see useCache()
*/
public function makeDefaultCacheDecision()
{

if ($this->nocache) {
return false;
} // caching turned off
if (!empty($this->depends['purge'])) {
return false;
} // purge requested?
if (!($this->time = @filemtime($this->cache))) {
return false;
} // cache exists?

// cache too old?
if (!empty($this->depends['age']) && ((time() - $this->time) > $this->depends['age'])) {
return false;
}

if (!empty($this->depends['files'])) {
foreach ($this->depends['files'] as $file) {
if ($this->time <= @filemtime($file)) {
return false;
} // cache older than files it depends on?
}
}

return true;
}

/**
* add dependencies to the depends array
*
* this method should only add dependencies,
* it should not remove any existing dependencies and
* it should only overwrite a dependency when the new value is more stringent than the old
*/
protected function addDependencies()
{
global $INPUT;
if ($INPUT->has('purge')) {
$this->depends['purge'] = true;
} // purge requested
}

/**
* retrieve the cached data
*
* @param bool $clean true to clean line endings, false to leave line endings alone
* @return string cache contents
*/
public function retrieveCache($clean = true)
{
return io_readFile($this->cache, $clean);
}

/**
* cache $data
*
* @param string $data the data to be cached
* @return bool true on success, false otherwise
*/
public function storeCache($data)
{
if ($this->nocache) {
return false;
}

return io_savefile($this->cache, $data);
}

/**
* remove any cached data associated with this cache instance
*/
public function removeCache()
{
@unlink($this->cache);
}

/**
* Record cache hits statistics.
* (Only when debugging allowed, to reduce overhead.)
*
* @param bool $success result of this cache use attempt
* @return bool pass-thru $success value
*/
protected function stats($success)
{
global $conf;
static $stats = null;
static $file;

if (!$conf['allowdebug']) {
return $success;
}

if (is_null($stats)) {
$file = $conf['cachedir'] . '/cache_stats.txt';
$lines = explode("\n", io_readFile($file));

foreach ($lines as $line) {
$i = strpos($line, ',');
$stats[substr($line, 0, $i)] = $line;
}
}

if (isset($stats[$this->ext])) {
list($ext, $count, $hits) = explode(',', $stats[$this->ext]);
} else {
$ext = $this->ext;
$count = 0;
$hits = 0;
}

$count++;
if ($success) {
$hits++;
}
$stats[$this->ext] = "$ext,$count,$hits";

io_saveFile($file, join("\n", $stats));

return $success;
}

/**
* @return bool
*/
public function isNoCache()
{
return $this->nocache;
}
}
46 changes: 46 additions & 0 deletions inc/Cache/CacheInstructions.php
@@ -0,0 +1,46 @@
<?php

namespace dokuwiki\Cache;

/**
* Caching of parser instructions
*/
class CacheInstructions extends \dokuwiki\Cache\CacheParser
{

/**
* @param string $id page id
* @param string $file source file for cache
*/
public function __construct($id, $file)
{
parent::__construct($id, $file, 'i');
}

/**
* retrieve the cached data
*
* @param bool $clean true to clean line endings, false to leave line endings alone
* @return array cache contents
*/
public function retrieveCache($clean = true)
{
$contents = io_readFile($this->cache, false);
return !empty($contents) ? unserialize($contents) : array();
}

/**
* cache $instructions
*
* @param array $instructions the instruction to be cached
* @return bool true on success, false otherwise
*/
public function storeCache($instructions)
{
if ($this->nocache) {
return false;
}

return io_savefile($this->cache, serialize($instructions));
}
}