[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Trait for issuing warnings on deprecated access. 4 * 5 * Adapted from https://github.com/wikimedia/mediawiki/blob/4aedefdbfd193f323097354bf581de1c93f02715/includes/debug/DeprecationHelper.php 6 * 7 */ 8 9 10 namespace dokuwiki\Debug; 11 12 /** 13 * Use this trait in classes which have properties for which public access 14 * is deprecated. Set the list of properties in $deprecatedPublicProperties 15 * and make the properties non-public. The trait will preserve public access 16 * but issue deprecation warnings when it is needed. 17 * 18 * Example usage: 19 * class Foo { 20 * use DeprecationHelper; 21 * protected $bar; 22 * public function __construct() { 23 * $this->deprecatePublicProperty( 'bar', '1.21', __CLASS__ ); 24 * } 25 * } 26 * 27 * $foo = new Foo; 28 * $foo->bar; // works but logs a warning 29 * 30 * Cannot be used with classes that have their own __get/__set methods. 31 * 32 */ 33 trait PropertyDeprecationHelper 34 { 35 36 /** 37 * List of deprecated properties, in <property name> => <class> format 38 * where <class> is the the name of the class defining the property 39 * 40 * E.g. [ '_event' => '\dokuwiki\Cache\Cache' ] 41 * @var string[] 42 */ 43 protected $deprecatedPublicProperties = []; 44 45 /** 46 * Mark a property as deprecated. Only use this for properties that used to be public and only 47 * call it in the constructor. 48 * 49 * @param string $property The name of the property. 50 * @param null $class name of the class defining the property 51 * @see DebugHelper::dbgDeprecatedProperty 52 */ 53 protected function deprecatePublicProperty( 54 $property, 55 $class = null 56 ) { 57 $this->deprecatedPublicProperties[$property] = $class ?: get_class(); 58 } 59 60 public function __get($name) 61 { 62 if (isset($this->deprecatedPublicProperties[$name])) { 63 $class = $this->deprecatedPublicProperties[$name]; 64 DebugHelper::dbgDeprecatedProperty($class, $name); 65 return $this->$name; 66 } 67 68 $qualifiedName = get_class() . '::$' . $name; 69 if ($this->deprecationHelperGetPropertyOwner($name)) { 70 // Someone tried to access a normal non-public property. Try to behave like PHP would. 71 trigger_error("Cannot access non-public property $qualifiedName", E_USER_ERROR); 72 } else { 73 // Non-existing property. Try to behave like PHP would. 74 trigger_error("Undefined property: $qualifiedName", E_USER_NOTICE); 75 } 76 return null; 77 } 78 79 public function __set($name, $value) 80 { 81 if (isset($this->deprecatedPublicProperties[$name])) { 82 $class = $this->deprecatedPublicProperties[$name]; 83 DebugHelper::dbgDeprecatedProperty($class, $name); 84 $this->$name = $value; 85 return; 86 } 87 88 $qualifiedName = get_class() . '::$' . $name; 89 if ($this->deprecationHelperGetPropertyOwner($name)) { 90 // Someone tried to access a normal non-public property. Try to behave like PHP would. 91 trigger_error("Cannot access non-public property $qualifiedName", E_USER_ERROR); 92 } else { 93 // Non-existing property. Try to behave like PHP would. 94 $this->$name = $value; 95 } 96 } 97 98 /** 99 * Like property_exists but also check for non-visible private properties and returns which 100 * class in the inheritance chain declared the property. 101 * @param string $property 102 * @return string|bool Best guess for the class in which the property is defined. 103 */ 104 private function deprecationHelperGetPropertyOwner($property) 105 { 106 // Easy branch: check for protected property / private property of the current class. 107 if (property_exists($this, $property)) { 108 // The class name is not necessarily correct here but getting the correct class 109 // name would be expensive, this will work most of the time and getting it 110 // wrong is not a big deal. 111 return __CLASS__; 112 } 113 // property_exists() returns false when the property does exist but is private (and not 114 // defined by the current class, for some value of "current" that differs slightly 115 // between engines). 116 // Since PHP triggers an error on public access of non-public properties but happily 117 // allows public access to undefined properties, we need to detect this case as well. 118 // Reflection is slow so use array cast hack to check for that: 119 $obfuscatedProps = array_keys((array)$this); 120 $obfuscatedPropTail = "\0$property"; 121 foreach ($obfuscatedProps as $obfuscatedProp) { 122 // private props are in the form \0<classname>\0<propname> 123 if (strpos($obfuscatedProp, $obfuscatedPropTail, 1) !== false) { 124 $classname = substr($obfuscatedProp, 1, -strlen($obfuscatedPropTail)); 125 if ($classname === '*') { 126 // sanity; this shouldn't be possible as protected properties were handled earlier 127 $classname = __CLASS__; 128 } 129 return $classname; 130 } 131 } 132 return false; 133 } 134 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body