wiki.techinc.nl/tests/phpunit/DynamicPropertyTestHelper.php
Máté Szabó 97bb70de85 Introduce and use DynamicPropertyTestHelper
PHP 8.2 has deprecated dynamic property creation on classes that do not
explicitly allow it via the #[AllowDynamicProperties] annotation. The
recommended migration path for associating arbitrary data with objects
is the WeakMap class, but it is only available on PHP 8.0 and above.

Since MediaWiki still supports PHP 7.4, and the test framework needs to
be able to associate some state with objects it does not own, like DB
connection handles, introduce and use a new DynamicPropertyTestHelper
shim class that uses WeakMap if available and falls back to regular
dynamic property creation otherwise. Convert the DB setup-related
dynamic property usage in MediaWikiIntegrationTestCase to use this
class.

Bug: T326466
Change-Id: I1054f6f944d491b536949cada33e2ac670e026f8
2023-11-03 13:50:53 -04:00

85 lines
3.2 KiB
PHP

<?php
declare( strict_types = 1 );
use Wikimedia\Assert\Assert;
// We want to allow dynamic property creation on any object.
// phpcs:disable MediaWiki.Commenting.FunctionComment.ObjectTypeHintParam
/**
* Utility class for dealing with dynamic property creation in tests.
*
* PHP 8.2 has deprecated dynamic property creation for objects not explicitly annotated with #[AllowDynamicProperties].
* The recommended migration path for associating arbitrary data with objects is WeakMap, which is only available starting from PHP 8.0.
* As long as MediaWiki still supports PHP 7.4, this requires a compatibility layer for dynamic property creation on classes
* that do not support it, by utilizing WeakMap when available and falling back to regular dynamic property creation
* on PHP 7.4.
*
* This class can be removed and its usages converted into direct WeakMap usage once MediaWiki only supports PHP 8.0 and above.
*
* @since 1.42
* @internal Only for use by core PHPUnit setup functionality.
*/
class DynamicPropertyTestHelper {
/**
* Associative array of WeakMaps holding dynamic properties keyed by property name.
* @var WeakMap[]
*/
private static $properties = [];
/**
* Get the value of the given dynamic property from the given object.
* @param object $owner Object to fetch the dynamic property for
* @param string $propName Name of the dynamic property to get
* @return mixed The value of the dynamic property, or null if not set
*/
public static function getDynamicProperty( object $owner, string $propName ) {
if ( class_exists( WeakMap::class ) ) {
self::$properties[$propName] ??= new WeakMap();
return self::$properties[$propName][$owner] ?? null;
}
return $owner->$propName ?? null;
}
/**
* Set the given dynamic property to the given value on an object.
* @param object $owner Object to set the dynamic property on
* @param string $propName Name of the dynamic property to set
* @param mixed $value The property value to set; must not be null
* @return void
*/
public static function setDynamicProperty( object $owner, string $propName, $value ): void {
// getDynamicProperty() uses a null return value as an idiom for "property does not exist",
// which precludes supporting null values for dynamic properties without a separate method
// to check for the existence of a possibly nullable property.
// Since existing test cases do not seem to extensively rely on setting null values,
// explicitly forbid them here.
Assert::parameter( $value !== null, '$value', 'must not be null' );
if ( class_exists( WeakMap::class ) ) {
self::$properties[$propName] ??= new WeakMap();
self::$properties[$propName][$owner] = $value;
} else {
$owner->$propName = $value;
}
}
/**
* Unset the given dynamic property on the given object.
* @param object $owner Object to unset the dynamic property on
* @param string $propName Name of the dynamic property to unset
* @return void
*/
public static function unsetDynamicProperty( object $owner, string $propName ): void {
if ( class_exists( WeakMap::class ) ) {
self::$properties[$propName] ??= new WeakMap();
unset( self::$properties[$propName][$owner] );
} else {
unset( $owner->$propName );
}
}
}
// phpcs:enable