MediaWiki REL1_39
DeprecationHelper.php
Go to the documentation of this file.
1<?php
60trait DeprecationHelper {
61
71 protected $deprecatedPublicProperties = [];
72
78 private $dynamicPropertiesAccessDeprecated = false;
79
94 protected function deprecatePublicProperty(
95 $property,
96 $version,
97 $class = null,
98 $component = null
99 ) {
100 $this->deprecatedPublicProperties[$property] = [
101 $version,
102 $class ?: __CLASS__,
103 $component,
104 null, null
105 ];
106 }
107
126 string $property,
127 string $version,
128 $getter,
129 $setter = null,
130 $class = null,
131 $component = null
132 ) {
133 $this->deprecatedPublicProperties[$property] = [
134 $version,
135 $class ?: __CLASS__,
136 null,
137 $getter,
138 $setter,
139 $component
140 ];
141 }
142
152 string $version,
153 string $class = null,
154 string $component = null
155 ) {
156 $this->dynamicPropertiesAccessDeprecated = [ $version, $class ?: __CLASS__, $component ];
157 }
158
159 public function __isset( $name ) {
160 // Overriding magic __isset is required not only for isset() and empty(),
161 // but to correctly support null coalescing for dynamic properties,
162 // e.g. $foo->bar ?? 'default'
163 if ( isset( $this->deprecatedPublicProperties[$name] ) ) {
164 list( $version, $class, $component, $getter ) = $this->deprecatedPublicProperties[$name];
165 $qualifiedName = $class . '::$' . $name;
166 wfDeprecated( $qualifiedName, $version, $component, 2 );
167 if ( $getter ) {
168 return $this->deprecationHelperCallGetter( $getter );
169 }
170 return true;
171 }
172
173 $ownerClass = $this->deprecationHelperGetPropertyOwner( $name );
174 if ( $ownerClass ) {
175 // Someone tried to access a normal non-public property. Try to behave like PHP would.
176 return false;
177 } else {
178 if ( $this->dynamicPropertiesAccessDeprecated ) {
179 [ $version, $class, $component ] = $this->dynamicPropertiesAccessDeprecated;
180 $qualifiedName = $class . '::$' . $name;
181 wfDeprecated( $qualifiedName, $version, $component, 2 );
182 }
183 return false;
184 }
185 }
186
187 public function __get( $name ) {
188 if ( isset( $this->deprecatedPublicProperties[$name] ) ) {
189 list( $version, $class, $component, $getter ) = $this->deprecatedPublicProperties[$name];
190 $qualifiedName = $class . '::$' . $name;
191 wfDeprecated( $qualifiedName, $version, $component, 2 );
192 if ( $getter ) {
193 return $this->deprecationHelperCallGetter( $getter );
194 }
195 return $this->$name;
196 }
197
198 $ownerClass = $this->deprecationHelperGetPropertyOwner( $name );
199 $qualifiedName = ( $ownerClass ?: get_class( $this ) ) . '::$' . $name;
200 if ( $ownerClass ) {
201 // Someone tried to access a normal non-public property. Try to behave like PHP would.
202 trigger_error( "Cannot access non-public property $qualifiedName", E_USER_ERROR );
203 } elseif ( property_exists( $this, $name ) ) {
204 // Normally __get method will not be even called if the property exists,
205 // but in tests if we mock an object that uses DeprecationHelper,
206 // __get and __set magic methods will be mocked as well, and called
207 // regardless of the property existence. Support that use-case.
208 return $this->$name;
209 } else {
210 // Non-existing property. Try to behave like PHP would.
211 trigger_error( "Undefined property: $qualifiedName", E_USER_NOTICE );
212 }
213 return null;
214 }
215
216 public function __set( $name, $value ) {
217 if ( isset( $this->deprecatedPublicProperties[$name] ) ) {
218 list( $version, $class, $component, , $setter ) = $this->deprecatedPublicProperties[$name];
219 $qualifiedName = $class . '::$' . $name;
220 wfDeprecated( $qualifiedName, $version, $component, 2 );
221 if ( $setter ) {
222 $this->deprecationHelperCallSetter( $setter, $value );
223 } elseif ( property_exists( $this, $name ) ) {
224 $this->$name = $value;
225 } else {
226 trigger_error( "Cannot access non-public property $qualifiedName", E_USER_ERROR );
227 }
228 return;
229 }
230
231 $ownerClass = $this->deprecationHelperGetPropertyOwner( $name );
232 $qualifiedName = ( $ownerClass ?: get_class( $this ) ) . '::$' . $name;
233 if ( $ownerClass ) {
234 // Someone tried to access a normal non-public property. Try to behave like PHP would.
235 trigger_error( "Cannot access non-public property $qualifiedName", E_USER_ERROR );
236 } else {
237 if ( $this->dynamicPropertiesAccessDeprecated ) {
238 [ $version, $class, $component ] = $this->dynamicPropertiesAccessDeprecated;
239 $qualifiedName = $class . '::$' . $name;
240 wfDeprecated( $qualifiedName, $version, $component, 2 );
241 }
242 // Non-existing property. Try to behave like PHP would.
243 $this->$name = $value;
244 }
245 }
246
254 private function deprecationHelperGetPropertyOwner( $property ) {
255 // Returning false is a non-error path and should avoid slow checks like reflection.
256 // Use array cast hack instead.
257 $obfuscatedProps = array_keys( (array)$this );
258 $obfuscatedPropTail = "\0$property";
259 foreach ( $obfuscatedProps as $obfuscatedProp ) {
260 // private props are in the form \0<classname>\0<propname>
261 if ( strpos( $obfuscatedProp, $obfuscatedPropTail, 1 ) !== false ) {
262 $classname = substr( $obfuscatedProp, 1, -strlen( $obfuscatedPropTail ) );
263 if ( $classname === '*' ) {
264 // protected property; we didn't get the name, but we are on an error path
265 // now so it's fine to use reflection
266 return ( new ReflectionProperty( $this, $property ) )->getDeclaringClass()->getName();
267 }
268 return $classname;
269 }
270 }
271 return false;
272 }
273
274 private function deprecationHelperCallGetter( $getter ) {
275 if ( is_string( $getter ) ) {
276 $getter = [ $this, $getter ];
277 }
278 return $getter();
279 }
280
281 private function deprecationHelperCallSetter( $setter, $value ) {
282 if ( is_string( $setter ) ) {
283 $setter = [ $this, $setter ];
284 }
285 $setter( $value );
286 }
287}
deprecatePublicPropertyFallback(string $property, string $version, $getter, $setter=null, $class=null, $component=null)
Mark a removed public property as deprecated and provide fallback getter and setter callables.
deprecatePublicProperty( $property, $version, $class=null, $component=null)
Mark a property as deprecated.
__set( $name, $value)
__get( $name)
deprecateDynamicPropertiesAccess(string $version, string $class=null, string $component=null)
Emit deprecation warnings when dynamic and unknown properties are accessed.
__isset( $name)
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.