Initial commit

This commit is contained in:
2021-10-26 13:02:53 +02:00
commit 73843b66ce
4678 changed files with 319494 additions and 0 deletions

View File

@@ -0,0 +1,167 @@
<?php
namespace dokuwiki\Debug;
use Doku_Event;
use dokuwiki\Extension\EventHandler;
class DebugHelper
{
const INFO_DEPRECATION_LOG_EVENT = 'INFO_DEPRECATION_LOG';
/**
* Log accesses to deprecated fucntions to the debug log
*
* @param string $alternative (optional) The function or method that should be used instead
* @param int $callerOffset (optional) How far the deprecated method is removed from this one
*
* @triggers \dokuwiki\Debug::INFO_DEPRECATION_LOG_EVENT
*/
public static function dbgDeprecatedFunction($alternative = '', $callerOffset = 1)
{
global $conf;
/** @var EventHandler $EVENT_HANDLER */
global $EVENT_HANDLER;
if (
!$conf['allowdebug'] &&
($EVENT_HANDLER === null || !$EVENT_HANDLER->hasHandlerForEvent('INFO_DEPRECATION_LOG'))
){
// avoid any work if no one cares
return;
}
$backtrace = debug_backtrace();
for ($i = 0; $i < $callerOffset; $i += 1) {
array_shift($backtrace);
}
list($self, $call) = $backtrace;
self::triggerDeprecationEvent(
$backtrace,
$alternative,
trim(
(!empty($self['class']) ? ($self['class'] . '::') : '') .
$self['function'] . '()', ':'),
trim(
(!empty($call['class']) ? ($call['class'] . '::') : '') .
$call['function'] . '()', ':'),
$call['file'],
$call['line']
);
}
/**
* This marks logs a deprecation warning for a property that should no longer be used
*
* This is usually called withing a magic getter or setter.
* For logging deprecated functions or methods see dbgDeprecatedFunction()
*
* @param string $class The class with the deprecated property
* @param string $propertyName The name of the deprecated property
*
* @triggers \dokuwiki\Debug::INFO_DEPRECATION_LOG_EVENT
*/
public static function dbgDeprecatedProperty($class, $propertyName)
{
global $conf;
global $EVENT_HANDLER;
if (!$conf['allowdebug'] && !$EVENT_HANDLER->hasHandlerForEvent(self::INFO_DEPRECATION_LOG_EVENT)) {
// avoid any work if no one cares
return;
}
$backtrace = debug_backtrace();
array_shift($backtrace);
$call = $backtrace[1];
$caller = trim($call['class'] . '::' . $call['function'] . '()', ':');
$qualifiedName = $class . '::$' . $propertyName;
self::triggerDeprecationEvent(
$backtrace,
'',
$qualifiedName,
$caller,
$backtrace[0]['file'],
$backtrace[0]['line']
);
}
/**
* Trigger a custom deprecation event
*
* Usually dbgDeprecatedFunction() or dbgDeprecatedProperty() should be used instead.
* This method is intended only for those situation where they are not applicable.
*
* @param string $alternative
* @param string $deprecatedThing
* @param string $caller
* @param string $file
* @param int $line
* @param int $callerOffset How many lines should be removed from the beginning of the backtrace
*/
public static function dbgCustomDeprecationEvent(
$alternative,
$deprecatedThing,
$caller,
$file,
$line,
$callerOffset = 1
) {
global $conf;
/** @var EventHandler $EVENT_HANDLER */
global $EVENT_HANDLER;
if (!$conf['allowdebug'] && !$EVENT_HANDLER->hasHandlerForEvent(self::INFO_DEPRECATION_LOG_EVENT)) {
// avoid any work if no one cares
return;
}
$backtrace = array_slice(debug_backtrace(), $callerOffset);
self::triggerDeprecationEvent(
$backtrace,
$alternative,
$deprecatedThing,
$caller,
$file,
$line
);
}
/**
* @param array $backtrace
* @param string $alternative
* @param string $deprecatedThing
* @param string $caller
* @param string $file
* @param int $line
*/
private static function triggerDeprecationEvent(
array $backtrace,
$alternative,
$deprecatedThing,
$caller,
$file,
$line
) {
$data = [
'trace' => $backtrace,
'alternative' => $alternative,
'called' => $deprecatedThing,
'caller' => $caller,
'file' => $file,
'line' => $line,
];
$event = new Doku_Event(self::INFO_DEPRECATION_LOG_EVENT, $data);
if ($event->advise_before()) {
$msg = $event->data['called'] . ' is deprecated. It was called from ';
$msg .= $event->data['caller'] . ' in ' . $event->data['file'] . ':' . $event->data['line'];
if ($event->data['alternative']) {
$msg .= ' ' . $event->data['alternative'] . ' should be used instead!';
}
dbglog($msg);
}
$event->advise_after();
}
}

View File

@@ -0,0 +1,134 @@
<?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 DebugHelper::dbgDeprecatedProperty
*/
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];
DebugHelper::dbgDeprecatedProperty($class, $name);
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];
DebugHelper::dbgDeprecatedProperty($class, $name);
$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;
}
}