Use deprecation helper

Instead of writing our own magic getters and setters for all variables
that used to be public, this adds a trait that does that in a generic
way.
This trait was copied from MediaWiki and adjusted to DokuWiki.
The original author seems to be @tgr Tisza Gergő

The downside of this trait is that the properties keep their
(potentially undesired) name. While that could be fixed within the
helper, that might add unnecessary complexity. The name can change when
support is dropped.
This commit is contained in:
Michael Große 2019-02-22 23:23:30 +01:00
parent fec08cc9d0
commit b4b0b31be1
No known key found for this signature in database
GPG Key ID: BDD834E001B99EDC
6 changed files with 179 additions and 60 deletions

View File

@ -2,20 +2,29 @@
namespace dokuwiki\Cache;
use \dokuwiki\Debug\PropertyDeprecationHelper;
/**
* Generic handling of caching
*/
class Cache
{
use PropertyDeprecationHelper;
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
// phpcs:disable
/**
* @deprecated since 2019-02-02 use the respective getters instead!
*/
protected $_event = ''; // event to be triggered during useCache
protected $_time;
protected $_nocache = false; // if set to true, cache will not be used or stored
// phpcs:enable
/**
* @param string $key primary identifier
@ -26,59 +35,28 @@ class Cache
$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
);
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;
/**
* @deprecated since 2019-02-02 use the respective getters instead!
*/
$this->deprecatePublicProperty('_event');
$this->deprecatePublicProperty('_time');
$this->deprecatePublicProperty('_nocache');
}
public function getTime()
{
return $this->time;
return $this->_time;
}
public function getEvent()
{
return $this->event;
return $this->_event;
}
public function setEvent($event)
public function setEvent($_event)
{
$this->event = $event;
$this->_event = $_event;
}
/**
@ -99,8 +77,8 @@ class Cache
$this->depends = $depends;
$this->addDependencies();
if ($this->event) {
return $this->stats(trigger_event($this->event, $this, array($this, 'makeDefaultCacheDecision')));
if ($this->_event) {
return $this->stats(trigger_event($this->_event, $this, array($this, 'makeDefaultCacheDecision')));
} else {
return $this->stats($this->makeDefaultCacheDecision());
}
@ -125,24 +103,24 @@ class Cache
public function makeDefaultCacheDecision()
{
if ($this->nocache) {
if ($this->_nocache) {
return false;
} // caching turned off
if (!empty($this->depends['purge'])) {
return false;
} // purge requested?
if (!($this->time = @filemtime($this->cache))) {
if (!($this->_time = @filemtime($this->cache))) {
return false;
} // cache exists?
// cache too old?
if (!empty($this->depends['age']) && ((time() - $this->time) > $this->depends['age'])) {
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)) {
if ($this->_time <= @filemtime($file)) {
return false;
} // cache older than files it depends on?
}
@ -185,7 +163,7 @@ class Cache
*/
public function storeCache($data)
{
if ($this->nocache) {
if ($this->_nocache) {
return false;
}
@ -251,6 +229,6 @@ class Cache
*/
public function isNoCache()
{
return $this->nocache;
return $this->_nocache;
}
}

View File

@ -37,7 +37,7 @@ class CacheInstructions extends \dokuwiki\Cache\CacheParser
*/
public function storeCache($instructions)
{
if ($this->nocache) {
if ($this->_nocache) {
return false;
}

View File

@ -12,8 +12,6 @@ class CacheParser extends Cache
public $mode = ''; // input mode (represents the processing the input file will undergo)
public $page = '';
public $event = 'PARSER_CACHE_USE';
/**
*
* @param string $id page id
@ -28,6 +26,7 @@ class CacheParser extends Cache
$this->file = $file;
$this->mode = $mode;
$this->_event = 'PARSER_CACHE_USE';
parent::__construct($file . $_SERVER['HTTP_HOST'] . $_SERVER['SERVER_PORT'], '.' . $mode);
}

View File

@ -26,14 +26,14 @@ class CacheRenderer extends CacheParser
}
// meta cache older than file it depends on?
if ($this->time < @filemtime(metaFN($this->page, '.meta'))) {
if ($this->_time < @filemtime(metaFN($this->page, '.meta'))) {
return false;
}
// check current link existence is consistent with cache version
// first check the purgefile
// - if the cache is more recent than the purgefile we know no links can have been updated
if ($this->time >= @filemtime($conf['cachedir'] . '/purgefile')) {
if ($this->_time >= @filemtime($conf['cachedir'] . '/purgefile')) {
return true;
}
@ -62,7 +62,7 @@ class CacheRenderer extends CacheParser
// -1 : do not cache (should not be overridden)
// 0 : cache never expires (can be overridden) - no need to set depends['age']
if ($conf['cachetime'] == -1) {
$this->nocache = true;
$this->_nocache = true;
return;
} elseif ($conf['cachetime'] > 0) {
$this->depends['age'] = isset($this->depends['age']) ?

View File

@ -0,0 +1,137 @@
<?php
/**
* Trait for issuing warnings on deprecated access.
*
* Adapted from https://github.com/wikimedia/mediawiki/blob/4aedefdbfd193f323097354bf581de1c93f02715/includes/debug/DeprecationHelper.php
*
*/
namespace dokuwiki\Debug;
/**
* Use this trait in classes which have properties for which public access
* is deprecated. Set the list of properties in $deprecatedPublicProperties
* and make the properties non-public. The trait will preserve public access
* but issue deprecation warnings when it is needed.
*
* Example usage:
* class Foo {
* use DeprecationHelper;
* protected $bar;
* public function __construct() {
* $this->deprecatePublicProperty( 'bar', '1.21', __CLASS__ );
* }
* }
*
* $foo = new Foo;
* $foo->bar; // works but logs a warning
*
* Cannot be used with classes that have their own __get/__set methods.
*
*/
trait PropertyDeprecationHelper
{
/**
* List of deprecated properties, in <property name> => <class> format
* where <class> is the the name of the class defining the property
*
* E.g. [ '_event' => '\dokuwiki\Cache\Cache' ]
* @var string[]
*/
protected $deprecatedPublicProperties = [];
/**
* Mark a property as deprecated. Only use this for properties that used to be public and only
* call it in the constructor.
*
* @param string $property The name of the property.
* @param null $class name of the class defining the property
* @see dbg_deprecated()
*/
protected function deprecatePublicProperty(
$property,
$class = null
) {
$this->deprecatedPublicProperties[$property] = $class ?: get_class();
}
public function __get($name)
{
if (isset($this->deprecatedPublicProperties[$name])) {
$class = $this->deprecatedPublicProperties[$name];
$qualifiedName = $class . '::$' . $name;
dbg_deprecated('', $qualifiedName);
return $this->$name;
}
$qualifiedName = get_class() . '::$' . $name;
if ($this->deprecationHelperGetPropertyOwner($name)) {
// Someone tried to access a normal non-public property. Try to behave like PHP would.
trigger_error("Cannot access non-public property $qualifiedName", E_USER_ERROR);
} else {
// Non-existing property. Try to behave like PHP would.
trigger_error("Undefined property: $qualifiedName", E_USER_NOTICE);
}
return null;
}
public function __set($name, $value)
{
if (isset($this->deprecatedPublicProperties[$name])) {
$class = $this->deprecatedPublicProperties[$name];
$qualifiedName = $class . '::$' . $name;
dbg_deprecated('', $qualifiedName);
$this->$name = $value;
return;
}
$qualifiedName = get_class() . '::$' . $name;
if ($this->deprecationHelperGetPropertyOwner($name)) {
// Someone tried to access a normal non-public property. Try to behave like PHP would.
trigger_error("Cannot access non-public property $qualifiedName", E_USER_ERROR);
} else {
// Non-existing property. Try to behave like PHP would.
$this->$name = $value;
}
}
/**
* Like property_exists but also check for non-visible private properties and returns which
* class in the inheritance chain declared the property.
* @param string $property
* @return string|bool Best guess for the class in which the property is defined.
*/
private function deprecationHelperGetPropertyOwner($property)
{
// Easy branch: check for protected property / private property of the current class.
if (property_exists($this, $property)) {
// The class name is not necessarily correct here but getting the correct class
// name would be expensive, this will work most of the time and getting it
// wrong is not a big deal.
return __CLASS__;
}
// property_exists() returns false when the property does exist but is private (and not
// defined by the current class, for some value of "current" that differs slightly
// between engines).
// Since PHP triggers an error on public access of non-public properties but happily
// allows public access to undefined properties, we need to detect this case as well.
// Reflection is slow so use array cast hack to check for that:
$obfuscatedProps = array_keys((array)$this);
$obfuscatedPropTail = "\0$property";
foreach ($obfuscatedProps as $obfuscatedProp) {
// private props are in the form \0<classname>\0<propname>
if (strpos($obfuscatedProp, $obfuscatedPropTail, 1) !== false) {
$classname = substr($obfuscatedProp, 1, -strlen($obfuscatedPropTail));
if ($classname === '*') {
// sanity; this shouldn't be possible as protected properties were handled earlier
$classname = __CLASS__;
}
return $classname;
}
}
return false;
}
}

View File

@ -444,9 +444,10 @@ function dbglog($msg,$header=''){
* Log accesses to deprecated fucntions to the debug log
*
* @param string $alternative The function or method that should be used instead
* @param string|null $deprecatedThing What is deprecated if not the current method
* @triggers INFO_DEPRECATION_LOG
*/
function dbg_deprecated($alternative = '') {
function dbg_deprecated($alternative = '', $deprecatedThing = null) {
global $conf;
global $EVENT_HANDLER;
if(!$conf['allowdebug'] && !$EVENT_HANDLER->hasHandlerForEvent('INFO_DEPRECATION_LOG')) {
@ -459,10 +460,14 @@ function dbg_deprecated($alternative = '') {
$self = $backtrace[0];
$call = $backtrace[1];
if ($deprecatedThing === null) {
$deprecatedThing = trim($self['class'] . '::' . $self['function'] . '()', ':');
}
$data = [
'trace' => $backtrace,
'alternative' => $alternative,
'called' => trim($self['class'] . '::' . $self['function'] . '()', ':'),
'called' => $deprecatedThing,
'caller' => trim($call['class'] . '::' . $call['function'] . '()', ':'),
'file' => $call['file'],
'line' => $call['line'],