DeprecationHandler.php
Namespace
Drupal\TestTools\Extension\DeprecationBridgeFile
-
core/
tests/ Drupal/ TestTools/ Extension/ DeprecationBridge/ DeprecationHandler.php
View source
<?php
declare (strict_types=1);
namespace Drupal\TestTools\Extension\DeprecationBridge;
use PHPUnit\Framework\TestCase;
/**
* Drupal's PHPUnit extension to manage code deprecation.
*
* This class is a replacement for symfony/phpunit-bridge that does not
* support PHPUnit 10. In the future this extension might be dropped if
* PHPUnit adds support for all deprecation management needs.
*
* @internal
*/
final class DeprecationHandler {
/**
* Indicates if the extension is enabled.
*/
private static bool $enabled = FALSE;
/**
* A list of deprecation messages that should be ignored if detected.
*
* @var list<string>
*/
private static array $deprecationIgnorePatterns = [];
/**
* A list of expected deprecation messages.
*
* @var list<string>
*/
private static array $expectedDeprecations = [];
/**
* A list of deprecation messages collected during test run.
*
* @var list<string>
*/
private static array $collectedDeprecations = [];
/**
* This class should not be instantiated.
*/
private function __construct() {
throw new \LogicException(__CLASS__ . ' should not be instantiated');
}
/**
* Returns the extension configuration.
*
* For historical reasons, the configuration is stored in the
* SYMFONY_DEPRECATIONS_HELPER environment variable.
*
* @return array|false
* An array of configuration variables, of FALSE if the extension is
* disabled.
*/
public static function getConfiguration() : array|false {
$environmentVariable = getenv('SYMFONY_DEPRECATIONS_HELPER');
if ($environmentVariable === 'disabled') {
return FALSE;
}
if ($environmentVariable === FALSE) {
// Ensure ignored deprecation patterns listed in .deprecation-ignore.txt
// are considered in testing.
$relativeFilePath = __DIR__ . "/../../../../../.deprecation-ignore.txt";
$deprecationIgnoreFilename = realpath($relativeFilePath);
if (empty($deprecationIgnoreFilename)) {
throw new \InvalidArgumentException(sprintf('The ignoreFile "%s" does not exist.', $relativeFilePath));
}
$environmentVariable = "ignoreFile={$deprecationIgnoreFilename}";
}
parse_str($environmentVariable, $configuration);
return $configuration;
}
/**
* Determines if the extension is enabled.
*
* @return bool
* TRUE if enabled, FALSE if disabled.
*/
public static function isEnabled() : bool {
return self::$enabled;
}
/**
* Initializes the extension.
*
* @param string|null $ignoreFile
* The path to a file containing ignore patterns for deprecations.
*/
public static function init(?string $ignoreFile = NULL) : void {
if (self::isEnabled()) {
throw new \LogicException(__CLASS__ . ' is already initialized');
}
// Load the deprecation ignore patterns from the specified file.
if ($ignoreFile && !self::$deprecationIgnorePatterns) {
if (!is_file($ignoreFile)) {
throw new \InvalidArgumentException(sprintf('The ignoreFile "%s" does not exist.', $ignoreFile));
}
set_error_handler(static function ($t, $m) use ($ignoreFile, &$line) {
throw new \RuntimeException(sprintf('Invalid pattern found in "%s" on line "%d"', $ignoreFile, 1 + $line) . substr($m, 12));
});
try {
foreach (file($ignoreFile) as $line => $pattern) {
if ((trim($pattern)[0] ?? '#') !== '#') {
preg_match($pattern, '');
self::$deprecationIgnorePatterns[] = $pattern;
}
}
} finally {
restore_error_handler();
}
}
// Mark the extension as enabled.
self::$enabled = TRUE;
}
/**
* Resets the extension.
*
* The extension should be reset at the beginning of each test run to ensure
* matching of expected and actual deprecations.
*/
public static function reset() : void {
if (!self::isEnabled()) {
return;
}
self::$expectedDeprecations = [];
self::$collectedDeprecations = [];
}
/**
* Adds an expected deprecation.
*
* Tests will expect deprecations during the test execution; at the end of
* each test run, collected deprecations are checked against the expected
* ones.
*
* @param string $message
* The expected deprecation message.
*/
public static function expectDeprecation(string $message) : void {
if (!self::isEnabled()) {
return;
}
self::$expectedDeprecations[] = $message;
}
/**
* Returns all expected deprecations.
*
* @return list<string>
* The expected deprecation messages.
*/
public static function getExpectedDeprecations() : array {
if (!self::isEnabled()) {
throw new \LogicException(__CLASS__ . ' is not initialized');
}
return self::$expectedDeprecations;
}
/**
* Collects an actual deprecation.
*
* Tests will expect deprecations during the test execution; at the end of
* each test run, collected deprecations are checked against the expected
* ones.
*
* @param string $message
* The actual deprecation message triggered via trigger_error().
*/
public static function collectActualDeprecation(string $message) : void {
if (!self::isEnabled()) {
return;
}
self::$collectedDeprecations[] = $message;
}
/**
* Returns all collected deprecations.
*
* @return list<string>
* The collected deprecation messages.
*/
public static function getCollectedDeprecations() : array {
if (!self::isEnabled()) {
throw new \LogicException(__CLASS__ . ' is not initialized');
}
return self::$collectedDeprecations;
}
/**
* Determines if an actual deprecation should be ignored.
*
* Deprecations that match the patterns included in the ignore file should
* be ignored.
*
* @param string $deprecationMessage
* The actual deprecation message triggered via trigger_error().
*/
public static function isIgnoredDeprecation(string $deprecationMessage) : bool {
if (!self::$deprecationIgnorePatterns) {
return FALSE;
}
$result = @preg_filter(self::$deprecationIgnorePatterns, '$0', $deprecationMessage);
if (preg_last_error() !== \PREG_NO_ERROR) {
throw new \RuntimeException(preg_last_error_msg());
}
return (bool) $result;
}
/**
* Determines if a test case is a deprecation test.
*
* Deprecation tests are those that are annotated with '@group legacy' or
* that have a '#[IgnoreDeprecations]' attribute.
*
* @param \PHPUnit\Framework\TestCase $testCase
* The test case being executed.
*/
public static function isDeprecationTest(TestCase $testCase) : bool {
return $testCase->valueObjectForEvents()
->metadata()
->isIgnoreDeprecations()
->isNotEmpty() || self::isTestInLegacyGroup($testCase);
}
/**
* Determines if a test case is part of the 'legacy' group.
*
* @param \PHPUnit\Framework\TestCase $testCase
* The test case being executed.
*/
private static function isTestInLegacyGroup(TestCase $testCase) : bool {
$groups = [];
foreach ($testCase->valueObjectForEvents()
->metadata()
->isGroup() as $metadata) {
$groups[] = $metadata->groupName();
}
return in_array('legacy', $groups, TRUE);
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
DeprecationHandler | Drupal's PHPUnit extension to manage code deprecation. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.