[PhpUnitBridge] Url encoded deprecations helper config
This commit is contained in:
parent
e0c4528237
commit
1c73f9cfed
@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
namespace Symfony\Bridge\PhpUnit;
|
namespace Symfony\Bridge\PhpUnit;
|
||||||
|
|
||||||
|
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Configuration;
|
||||||
|
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Catch deprecation notices and print a summary report at the end of the test suite.
|
* Catch deprecation notices and print a summary report at the end of the test suite.
|
||||||
*
|
*
|
||||||
@ -18,23 +21,30 @@ namespace Symfony\Bridge\PhpUnit;
|
|||||||
*/
|
*/
|
||||||
class DeprecationErrorHandler
|
class DeprecationErrorHandler
|
||||||
{
|
{
|
||||||
const MODE_WEAK = 'weak';
|
/**
|
||||||
|
* @deprecated since Symfony 4.3, use max[self]=0 instead
|
||||||
|
*/
|
||||||
const MODE_WEAK_VENDORS = 'weak_vendors';
|
const MODE_WEAK_VENDORS = 'weak_vendors';
|
||||||
const MODE_DISABLED = 'disabled';
|
|
||||||
|
|
||||||
private $mode = false;
|
const MODE_DISABLED = 'disabled';
|
||||||
private $resolvedMode = false;
|
const MODE_WEAK = 'max[total]=999999&verbose=0';
|
||||||
|
const MODE_STRICT = 'max[total]=0';
|
||||||
|
|
||||||
|
private $mode;
|
||||||
|
private $configuration;
|
||||||
private $deprecations = [
|
private $deprecations = [
|
||||||
'unsilencedCount' => 0,
|
'unsilencedCount' => 0,
|
||||||
'remainingCount' => 0,
|
'remaining selfCount' => 0,
|
||||||
'legacyCount' => 0,
|
'legacyCount' => 0,
|
||||||
'otherCount' => 0,
|
'otherCount' => 0,
|
||||||
'remaining vendorCount' => 0,
|
'remaining directCount' => 0,
|
||||||
|
'remaining indirectCount' => 0,
|
||||||
'unsilenced' => [],
|
'unsilenced' => [],
|
||||||
'remaining' => [],
|
'remaining self' => [],
|
||||||
'legacy' => [],
|
'legacy' => [],
|
||||||
'other' => [],
|
'other' => [],
|
||||||
'remaining vendor' => [],
|
'remaining direct' => [],
|
||||||
|
'remaining indirect' => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $isRegistered = false;
|
private static $isRegistered = false;
|
||||||
@ -43,13 +53,16 @@ class DeprecationErrorHandler
|
|||||||
/**
|
/**
|
||||||
* Registers and configures the deprecation handler.
|
* Registers and configures the deprecation handler.
|
||||||
*
|
*
|
||||||
* The following reporting modes are supported:
|
* The mode is a query string with options:
|
||||||
* - use "weak" to hide the deprecation report but keep a global count;
|
* - "disabled" to disable the deprecation handler
|
||||||
* - use "weak_vendors" to fail only on deprecations triggered in your own code;
|
* - "verbose" to enable/disable displaying the deprecation report
|
||||||
* - use "/some-regexp/" to stop the test suite whenever a deprecation
|
* - "max" to configure the number of deprecations to allow before exiting with a non-zero
|
||||||
* message matches the given regular expression;
|
* status code; it's an array with keys "total", "self", "direct" and "indirect"
|
||||||
* - use a number to define the upper bound of allowed deprecations,
|
*
|
||||||
* making the test suite fail whenever more notices are triggered.
|
* The default mode is "max[total]=0&verbose=1".
|
||||||
|
*
|
||||||
|
* The mode can alternatively be "/some-regexp/" to stop the test suite whenever
|
||||||
|
* a deprecation message matches the given regular expression.
|
||||||
*
|
*
|
||||||
* @param int|string|false $mode The reporting mode, defaults to not allowing any deprecations
|
* @param int|string|false $mode The reporting mode, defaults to not allowing any deprecations
|
||||||
*/
|
*/
|
||||||
@ -108,76 +121,41 @@ class DeprecationErrorHandler
|
|||||||
*/
|
*/
|
||||||
public function handleError($type, $msg, $file, $line, $context = [])
|
public function handleError($type, $msg, $file, $line, $context = [])
|
||||||
{
|
{
|
||||||
if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || self::MODE_DISABLED === $mode = $this->getMode()) {
|
if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || !$this->getConfiguration()->isEnabled()) {
|
||||||
$ErrorHandler = self::$utilPrefix.'ErrorHandler';
|
$ErrorHandler = self::$utilPrefix.'ErrorHandler';
|
||||||
|
|
||||||
return $ErrorHandler::handleError($type, $msg, $file, $line, $context);
|
return $ErrorHandler::handleError($type, $msg, $file, $line, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
$trace = debug_backtrace();
|
$deprecation = new Deprecation($msg, debug_backtrace(), $file);
|
||||||
$group = 'other';
|
$group = 'other';
|
||||||
$isVendor = self::MODE_WEAK_VENDORS === $mode && self::inVendors($file);
|
|
||||||
|
|
||||||
$i = \count($trace);
|
if ($deprecation->originatesFromAnObject()) {
|
||||||
while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_') || 0 === strpos($trace[$i]['class'], 'PHPUnit\\')))) {
|
$class = $deprecation->originatingClass();
|
||||||
// No-op
|
$method = $deprecation->originatingMethod();
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) {
|
|
||||||
if (isset($trace[$i]['class']) && 0 === strpos($trace[$i]['class'], 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor')) {
|
|
||||||
$parsedMsg = unserialize($msg);
|
|
||||||
$msg = $parsedMsg['deprecation'];
|
|
||||||
$class = $parsedMsg['class'];
|
|
||||||
$method = $parsedMsg['method'];
|
|
||||||
// If the deprecation has been triggered via
|
|
||||||
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
|
|
||||||
// then we need to use the serialized information to determine
|
|
||||||
// if the error has been triggered from vendor code.
|
|
||||||
$isVendor = self::MODE_WEAK_VENDORS === $mode && isset($parsedMsg['triggering_file']) && self::inVendors($parsedMsg['triggering_file']);
|
|
||||||
} else {
|
|
||||||
$class = isset($trace[$i]['object']) ? \get_class($trace[$i]['object']) : $trace[$i]['class'];
|
|
||||||
$method = $trace[$i]['function'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$Test = self::$utilPrefix.'Test';
|
|
||||||
|
|
||||||
if (0 !== error_reporting()) {
|
if (0 !== error_reporting()) {
|
||||||
$group = 'unsilenced';
|
$group = 'unsilenced';
|
||||||
} elseif (0 === strpos($method, 'testLegacy')
|
} elseif ($deprecation->isLegacy(self::$utilPrefix)) {
|
||||||
|| 0 === strpos($method, 'provideLegacy')
|
|
||||||
|| 0 === strpos($method, 'getLegacy')
|
|
||||||
|| strpos($class, '\Legacy')
|
|
||||||
|| \in_array('legacy', $Test::getGroups($class, $method), true)
|
|
||||||
) {
|
|
||||||
$group = 'legacy';
|
$group = 'legacy';
|
||||||
} elseif ($isVendor) {
|
} elseif (!$deprecation->isSelf()) {
|
||||||
$group = 'remaining vendor';
|
$group = $deprecation->isIndirect() ? 'remaining indirect' : 'remaining direct';
|
||||||
} else {
|
} else {
|
||||||
$group = 'remaining';
|
$group = 'remaining self';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $msg)) {
|
if ($this->getConfiguration()->shouldDisplayStackTrace($msg)) {
|
||||||
$e = new \Exception($msg);
|
echo "\n".ucfirst($group).' '.$deprecation->toString();
|
||||||
$r = new \ReflectionProperty($e, 'trace');
|
|
||||||
$r->setAccessible(true);
|
|
||||||
$r->setValue($e, \array_slice($trace, 1, $i));
|
|
||||||
|
|
||||||
echo "\n".ucfirst($group).' deprecation triggered by '.$class.'::'.$method.':';
|
|
||||||
echo "\n".$msg;
|
|
||||||
echo "\nStack trace:";
|
|
||||||
echo "\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString());
|
|
||||||
echo "\n";
|
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if ('legacy' !== $group) {
|
||||||
if ('legacy' !== $group && self::MODE_WEAK !== $mode) {
|
|
||||||
$ref = &$this->deprecations[$group][$msg]['count'];
|
$ref = &$this->deprecations[$group][$msg]['count'];
|
||||||
++$ref;
|
++$ref;
|
||||||
$ref = &$this->deprecations[$group][$msg][$class.'::'.$method];
|
$ref = &$this->deprecations[$group][$msg][$class.'::'.$method];
|
||||||
++$ref;
|
++$ref;
|
||||||
}
|
}
|
||||||
} elseif (self::MODE_WEAK !== $mode) {
|
} else {
|
||||||
$ref = &$this->deprecations[$group][$msg]['count'];
|
$ref = &$this->deprecations[$group][$msg]['count'];
|
||||||
++$ref;
|
++$ref;
|
||||||
}
|
}
|
||||||
@ -190,9 +168,9 @@ class DeprecationErrorHandler
|
|||||||
*/
|
*/
|
||||||
public function shutdown()
|
public function shutdown()
|
||||||
{
|
{
|
||||||
$mode = $this->getMode();
|
$configuration = $this->getConfiguration();
|
||||||
|
|
||||||
if (isset($mode[0]) && '/' === $mode[0]) {
|
if ($configuration->isInRegexMode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,28 +178,22 @@ class DeprecationErrorHandler
|
|||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
|
||||||
if ($currErrorHandler !== [$this, 'handleError']) {
|
if ($currErrorHandler !== [$this, 'handleError']) {
|
||||||
echo "\n", self::colorize('THE ERROR HANDLER HAS CHANGED!', true, $mode), "\n";
|
echo "\n", self::colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$groups = ['unsilenced', 'remaining'];
|
$groups = ['unsilenced', 'remaining self', 'remaining direct', 'remaining indirect', 'legacy', 'other'];
|
||||||
|
|
||||||
if (self::MODE_WEAK_VENDORS === $mode) {
|
$this->displayDeprecations($groups, $configuration);
|
||||||
$groups[] = 'remaining vendor';
|
|
||||||
}
|
|
||||||
|
|
||||||
array_push($groups, 'legacy', 'other');
|
|
||||||
|
|
||||||
$this->displayDeprecations($groups, $mode);
|
|
||||||
|
|
||||||
// store failing status
|
// store failing status
|
||||||
$isFailing = self::MODE_WEAK !== $mode && $mode < $this->deprecations['unsilencedCount'] + $this->deprecations['remainingCount'] + $this->deprecations['otherCount'];
|
$isFailing = !$configuration->tolerates($this->deprecations);
|
||||||
|
|
||||||
// reset deprecations array
|
// reset deprecations array
|
||||||
foreach ($this->deprecations as $group => $arrayOrInt) {
|
foreach ($this->deprecations as $group => $arrayOrInt) {
|
||||||
$this->deprecations[$group] = \is_int($arrayOrInt) ? 0 : [];
|
$this->deprecations[$group] = \is_int($arrayOrInt) ? 0 : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
register_shutdown_function(function () use ($isFailing, $groups, $mode) {
|
register_shutdown_function(function () use ($isFailing, $groups, $configuration) {
|
||||||
foreach ($this->deprecations as $group => $arrayOrInt) {
|
foreach ($this->deprecations as $group => $arrayOrInt) {
|
||||||
if (0 < (\is_int($arrayOrInt) ? $arrayOrInt : \count($arrayOrInt))) {
|
if (0 < (\is_int($arrayOrInt) ? $arrayOrInt : \count($arrayOrInt))) {
|
||||||
echo "Shutdown-time deprecations:\n";
|
echo "Shutdown-time deprecations:\n";
|
||||||
@ -229,85 +201,55 @@ class DeprecationErrorHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->displayDeprecations($groups, $mode);
|
$this->displayDeprecations($groups, $configuration);
|
||||||
|
|
||||||
if ($isFailing || self::MODE_WEAK !== $mode && $mode < $this->deprecations['unsilencedCount'] + $this->deprecations['remainingCount'] + $this->deprecations['otherCount']) {
|
if ($isFailing || !$configuration->tolerates($this->deprecations)) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getMode()
|
private function getConfiguration()
|
||||||
{
|
{
|
||||||
if (false !== $this->resolvedMode) {
|
if (null !== $this->configuration) {
|
||||||
return $this->resolvedMode;
|
return $this->configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false === $mode = $this->mode) {
|
if (false === $mode = $this->mode) {
|
||||||
$mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
|
$mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
|
||||||
}
|
}
|
||||||
|
if ('strict' === $mode) {
|
||||||
if (self::MODE_DISABLED !== $mode
|
return $this->configuration = Configuration::inStrictMode();
|
||||||
&& self::MODE_WEAK !== $mode
|
}
|
||||||
&& self::MODE_WEAK_VENDORS !== $mode
|
if (self::MODE_DISABLED === $mode) {
|
||||||
&& (!isset($mode[0]) || '/' !== $mode[0])
|
return $this->configuration = Configuration::inDisabledMode();
|
||||||
) {
|
}
|
||||||
$mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0;
|
if ('weak' === $mode) {
|
||||||
|
return $this->configuration = Configuration::inWeakMode();
|
||||||
|
}
|
||||||
|
if (self::MODE_WEAK_VENDORS === $mode) {
|
||||||
|
echo sprintf('Setting SYMFONY_DEPRECATIONS_HELPER to "%s" is deprecated in favor of "max[self]=0"', $mode).PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (isset($mode[0]) && '/' === $mode[0]) {
|
||||||
|
return $this->configuration = Configuration::fromRegex($mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolvedMode = $mode;
|
if (preg_match('/^[1-9][0-9]*$/', (string) $mode)) {
|
||||||
}
|
return $this->configuration = Configuration::fromNumber($mode);
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $path
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private static function inVendors($path)
|
|
||||||
{
|
|
||||||
/** @var string[] absolute paths to vendor directories */
|
|
||||||
static $vendors;
|
|
||||||
|
|
||||||
if (null === $vendors) {
|
|
||||||
foreach (get_declared_classes() as $class) {
|
|
||||||
if ('C' !== $class[0] || 0 !== strpos($class, 'ComposerAutoloaderInit')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$r = new \ReflectionClass($class);
|
|
||||||
$v = \dirname(\dirname($r->getFileName()));
|
|
||||||
|
|
||||||
if (file_exists($v.'/composer/installed.json')) {
|
|
||||||
$vendors[] = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$realPath = realpath($path);
|
return $this->configuration = Configuration::fromUrlEncodedString((string) $mode);
|
||||||
|
|
||||||
if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($vendors as $vendor) {
|
|
||||||
if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $str
|
* @param string $str
|
||||||
* @param bool $red
|
* @param bool $red
|
||||||
* @param mixed $mode
|
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private static function colorize($str, $red, $mode)
|
private static function colorize($str, $red)
|
||||||
{
|
{
|
||||||
if (!self::hasColorSupport() || self::MODE_WEAK === $mode) {
|
if (!self::hasColorSupport()) {
|
||||||
return $str;
|
return $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,36 +259,36 @@ class DeprecationErrorHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string[] $groups
|
* @param string[] $groups
|
||||||
* @param mixed $mode
|
* @param Configuration $configuration
|
||||||
*/
|
*/
|
||||||
private function displayDeprecations($groups, $mode)
|
private function displayDeprecations($groups, $configuration)
|
||||||
{
|
{
|
||||||
$cmp = function ($a, $b) {
|
$cmp = function ($a, $b) {
|
||||||
return $b['count'] - $a['count'];
|
return $b['count'] - $a['count'];
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach ($groups as $group) {
|
foreach ($groups as $group) {
|
||||||
if (!$this->deprecations[$group.'Count']) {
|
if ($this->deprecations[$group.'Count']) {
|
||||||
continue;
|
echo "\n", self::colorize(
|
||||||
}
|
sprintf('%s deprecation notices (%d)', ucfirst($group), $this->deprecations[$group.'Count']),
|
||||||
|
'legacy' !== $group && 'remaining indirect' !== $group
|
||||||
|
), "\n";
|
||||||
|
|
||||||
echo "\n", self::colorize(
|
if (!$configuration->verboseOutput()) {
|
||||||
sprintf('%s deprecation notices (%d)', ucfirst($group), $this->deprecations[$group.'Count']),
|
continue;
|
||||||
'legacy' !== $group && 'remaining vendor' !== $group,
|
}
|
||||||
$mode
|
uasort($this->deprecations[$group], $cmp);
|
||||||
), "\n";
|
|
||||||
|
|
||||||
uasort($this->deprecations[$group], $cmp);
|
foreach ($this->deprecations[$group] as $msg => $notices) {
|
||||||
|
echo "\n ", $notices['count'], 'x: ', $msg, "\n";
|
||||||
|
|
||||||
foreach ($this->deprecations[$group] as $msg => $notices) {
|
arsort($notices);
|
||||||
echo "\n ", $notices['count'], 'x: ', $msg, "\n";
|
|
||||||
|
|
||||||
arsort($notices);
|
foreach ($notices as $method => $count) {
|
||||||
|
if ('count' !== $method) {
|
||||||
foreach ($notices as $method => $count) {
|
echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n";
|
||||||
if ('count' !== $method) {
|
}
|
||||||
echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,208 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class Configuration
|
||||||
|
{
|
||||||
|
const GROUPS = ['total', 'indirect', 'direct', 'self'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int[]
|
||||||
|
*/
|
||||||
|
private $thresholds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $regex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $enabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $verboseOutput = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int[] $thresholds A hash associating groups to thresholds
|
||||||
|
* @param string $regex Will be matched against messages, to decide
|
||||||
|
* whether to display a stack trace
|
||||||
|
* @param bool $verboseOutput
|
||||||
|
*/
|
||||||
|
private function __construct(array $thresholds = [], $regex = '', $verboseOutput = true)
|
||||||
|
{
|
||||||
|
foreach ($thresholds as $group => $threshold) {
|
||||||
|
if (!\in_array($group, self::GROUPS, true)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Unrecognized threshold "%s", expected one of "%s"', $group, implode('", "', self::GROUPS)));
|
||||||
|
}
|
||||||
|
if (!is_numeric($threshold)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Threshold for group "%s" has invalid value "%s"', $group, $threshold));
|
||||||
|
}
|
||||||
|
$this->thresholds[$group] = (int) $threshold;
|
||||||
|
}
|
||||||
|
if (isset($this->thresholds['direct'])) {
|
||||||
|
$this->thresholds += [
|
||||||
|
'self' => $this->thresholds['direct'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (isset($this->thresholds['indirect'])) {
|
||||||
|
$this->thresholds += [
|
||||||
|
'direct' => $this->thresholds['indirect'],
|
||||||
|
'self' => $this->thresholds['indirect'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
foreach (self::GROUPS as $group) {
|
||||||
|
if (!isset($this->thresholds[$group])) {
|
||||||
|
$this->thresholds[$group] = 999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->regex = $regex;
|
||||||
|
$this->verboseOutput = $verboseOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEnabled()
|
||||||
|
{
|
||||||
|
return $this->enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $deprecations
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function tolerates(array $deprecations)
|
||||||
|
{
|
||||||
|
$deprecationCounts = array_filter($deprecations, function ($key) {
|
||||||
|
return false !== strpos($key, 'Count') && false === strpos($key, 'legacy');
|
||||||
|
}, ARRAY_FILTER_USE_KEY);
|
||||||
|
|
||||||
|
if (array_sum($deprecationCounts) > $this->thresholds['total']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach (['self', 'direct', 'indirect'] as $deprecationType) {
|
||||||
|
if ($deprecationCounts['remaining '.$deprecationType.'Count'] > $this->thresholds[$deprecationType]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $message
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function shouldDisplayStackTrace($message)
|
||||||
|
{
|
||||||
|
return '' !== $this->regex && preg_match($this->regex, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isInRegexMode()
|
||||||
|
{
|
||||||
|
return '' !== $this->regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function verboseOutput()
|
||||||
|
{
|
||||||
|
return $this->verboseOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $serializedConfiguration an encoded string, for instance
|
||||||
|
* max[total]=1234&max[indirect]=42
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function fromUrlEncodedString(string $serializedConfiguration)
|
||||||
|
{
|
||||||
|
parse_str($serializedConfiguration, $normalizedConfiguration);
|
||||||
|
foreach (array_keys($normalizedConfiguration) as $key) {
|
||||||
|
if (!\in_array($key, ['max', 'disabled', 'verbose'], true)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Unknown configuration option "%s"', $key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($normalizedConfiguration['disabled'])) {
|
||||||
|
return self::inDisabledMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
$verboseOutput = true;
|
||||||
|
if (isset($normalizedConfiguration['verbose'])) {
|
||||||
|
$verboseOutput = (bool) $normalizedConfiguration['verbose'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new self(
|
||||||
|
$normalizedConfiguration['max'] ?? [],
|
||||||
|
'',
|
||||||
|
$verboseOutput
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function inDisabledMode()
|
||||||
|
{
|
||||||
|
$configuration = new self();
|
||||||
|
$configuration->enabled = false;
|
||||||
|
|
||||||
|
return $configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function inStrictMode()
|
||||||
|
{
|
||||||
|
return new self(['total' => 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function inWeakMode()
|
||||||
|
{
|
||||||
|
return new self([], '', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function fromNumber(int $upperBound)
|
||||||
|
{
|
||||||
|
return new self(['total' => $upperBound]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function fromRegex(string $regex)
|
||||||
|
{
|
||||||
|
return new self([], $regex);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,291 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
|
||||||
|
|
||||||
|
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class Deprecation
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $trace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ?string
|
||||||
|
*/
|
||||||
|
private $originClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ?string
|
||||||
|
*/
|
||||||
|
private $originMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $self;
|
||||||
|
|
||||||
|
/** @var string[] absolute paths to vendor directories */
|
||||||
|
private static $vendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $message
|
||||||
|
* @param string $file
|
||||||
|
*/
|
||||||
|
public function __construct($message, array $trace, $file)
|
||||||
|
{
|
||||||
|
$this->trace = $trace;
|
||||||
|
$this->message = $message;
|
||||||
|
$i = \count($trace);
|
||||||
|
while (1 < $i && $this->lineShouldBeSkipped($trace[--$i])) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
$line = $trace[$i];
|
||||||
|
$this->self = !$this->pathOriginatesFromVendor($file);
|
||||||
|
if (isset($line['object']) || isset($line['class'])) {
|
||||||
|
if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) {
|
||||||
|
$parsedMsg = unserialize($this->message);
|
||||||
|
$this->message = $parsedMsg['deprecation'];
|
||||||
|
$this->originClass = $parsedMsg['class'];
|
||||||
|
$this->originMethod = $parsedMsg['method'];
|
||||||
|
// If the deprecation has been triggered via
|
||||||
|
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
|
||||||
|
// then we need to use the serialized information to determine
|
||||||
|
// if the error has been triggered from vendor code.
|
||||||
|
$this->self = isset($parsedMsg['triggering_file'])
|
||||||
|
&& $this->pathOriginatesFromVendor($parsedMsg['triggering_file']);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->originClass = isset($line['object']) ? \get_class($line['object']) : $line['class'];
|
||||||
|
$this->originMethod = $line['function'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function lineShouldBeSkipped(array $line)
|
||||||
|
{
|
||||||
|
if (!isset($line['class'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$class = $line['class'];
|
||||||
|
|
||||||
|
return 'ReflectionMethod' === $class || 0 === strpos($class, 'PHPUnit_') || 0 === strpos($class, 'PHPUnit\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function originatesFromAnObject()
|
||||||
|
{
|
||||||
|
return isset($this->originClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isSelf()
|
||||||
|
{
|
||||||
|
return $this->self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function originatingClass()
|
||||||
|
{
|
||||||
|
if (null === $this->originClass) {
|
||||||
|
throw new \LogicException('Check with originatesFromAnObject() before calling this method');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->originClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function originatingMethod()
|
||||||
|
{
|
||||||
|
if (null === $this->originMethod) {
|
||||||
|
throw new \LogicException('Check with originatesFromAnObject() before calling this method');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->originMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $utilPrefix
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isLegacy($utilPrefix)
|
||||||
|
{
|
||||||
|
$test = $utilPrefix.'Test';
|
||||||
|
$class = $this->originatingClass();
|
||||||
|
$method = $this->originatingMethod();
|
||||||
|
|
||||||
|
return 0 === strpos($method, 'testLegacy')
|
||||||
|
|| 0 === strpos($method, 'provideLegacy')
|
||||||
|
|| 0 === strpos($method, 'getLegacy')
|
||||||
|
|| strpos($class, '\Legacy')
|
||||||
|
|| \in_array('legacy', $test::getGroups($class, $method), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether both the calling package and the called package are vendor
|
||||||
|
* packages.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isIndirect()
|
||||||
|
{
|
||||||
|
$erroringFile = $erroringPackage = null;
|
||||||
|
foreach ($this->trace as $line) {
|
||||||
|
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!isset($line['file'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$file = $line['file'];
|
||||||
|
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$this->pathOriginatesFromVendor($file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $erroringFile && null !== $erroringPackage) {
|
||||||
|
$package = $this->getPackage($file);
|
||||||
|
if ('composer' !== $package && $package !== $erroringPackage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$erroringFile = $file;
|
||||||
|
$erroringPackage = $this->getPackage($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pathOriginatesFromVendor() should always be called prior to calling this method.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getPackage($path)
|
||||||
|
{
|
||||||
|
$path = realpath($path) ?: $path;
|
||||||
|
foreach (self::getVendors() as $vendorRoot) {
|
||||||
|
if (0 === strpos($path, $vendorRoot)) {
|
||||||
|
$relativePath = substr($path, \strlen($vendorRoot) + 1);
|
||||||
|
$vendor = strstr($relativePath, \DIRECTORY_SEPARATOR, true);
|
||||||
|
if (false === $vendor) {
|
||||||
|
throw new \RuntimeException(sprintf('Could not find directory separator "%s" in path "%s"', \DIRECTORY_SEPARATOR, $relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrim($vendor.'/'.strstr(substr(
|
||||||
|
$relativePath,
|
||||||
|
\strlen($vendor) + 1
|
||||||
|
), \DIRECTORY_SEPARATOR, true), '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException(sprintf('No vendors found for path "%s"', $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[] an array of paths
|
||||||
|
*/
|
||||||
|
private static function getVendors()
|
||||||
|
{
|
||||||
|
if (null === self::$vendors) {
|
||||||
|
self::$vendors = [];
|
||||||
|
foreach (get_declared_classes() as $class) {
|
||||||
|
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
|
||||||
|
$r = new \ReflectionClass($class);
|
||||||
|
$v = \dirname(\dirname($r->getFileName()));
|
||||||
|
if (file_exists($v.'/composer/installed.json')) {
|
||||||
|
self::$vendors[] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$vendors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function pathOriginatesFromVendor($path)
|
||||||
|
{
|
||||||
|
$realPath = realpath($path);
|
||||||
|
if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
foreach (self::getVendors() as $vendor) {
|
||||||
|
if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function toString()
|
||||||
|
{
|
||||||
|
$exception = new \Exception($this->message);
|
||||||
|
$reflection = new \ReflectionProperty($exception, 'trace');
|
||||||
|
$reflection->setAccessible(true);
|
||||||
|
$reflection->setValue($exception, $this->trace);
|
||||||
|
|
||||||
|
return 'deprecation triggered by '.$this->originatingClass().'::'.$this->originatingMethod().':'.
|
||||||
|
"\n".$this->message.
|
||||||
|
"\nStack trace:".
|
||||||
|
"\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $exception->getTraceAsString()).
|
||||||
|
"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPackageFromLine(array $line)
|
||||||
|
{
|
||||||
|
if (!isset($line['file'])) {
|
||||||
|
return 'internal function';
|
||||||
|
}
|
||||||
|
if (!$this->pathOriginatesFromVendor($line['file'])) {
|
||||||
|
return 'source code';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return $this->getPackage($line['file']);
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,195 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Configuration;
|
||||||
|
|
||||||
|
class ConfigurationTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testItThrowsOnStringishValue()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage('hi');
|
||||||
|
Configuration::fromUrlEncodedString('hi');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItThrowsOnUnknownConfigurationOption()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage('min');
|
||||||
|
Configuration::fromUrlEncodedString('min[total]=42');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItThrowsOnUnknownThreshold()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage('deep');
|
||||||
|
Configuration::fromUrlEncodedString('max[deep]=42');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItThrowsOnStringishThreshold()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage('forty-two');
|
||||||
|
Configuration::fromUrlEncodedString('max[total]=forty-two');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItNoticesExceededTotalThreshold()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::fromUrlEncodedString('max[total]=3');
|
||||||
|
$this->assertTrue($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 1,
|
||||||
|
'remaining selfCount' => 0,
|
||||||
|
'legacyCount' => 1,
|
||||||
|
'otherCount' => 0,
|
||||||
|
'remaining directCount' => 1,
|
||||||
|
'remaining indirectCount' => 1,
|
||||||
|
]));
|
||||||
|
$this->assertFalse($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 1,
|
||||||
|
'remaining selfCount' => 1,
|
||||||
|
'legacyCount' => 1,
|
||||||
|
'otherCount' => 0,
|
||||||
|
'remaining directCount' => 1,
|
||||||
|
'remaining indirectCount' => 1,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItNoticesExceededSelfThreshold()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::fromUrlEncodedString('max[self]=1');
|
||||||
|
$this->assertTrue($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 1234,
|
||||||
|
'remaining selfCount' => 1,
|
||||||
|
'legacyCount' => 23,
|
||||||
|
'otherCount' => 13,
|
||||||
|
'remaining directCount' => 124,
|
||||||
|
'remaining indirectCount' => 3244,
|
||||||
|
]));
|
||||||
|
$this->assertFalse($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 1234,
|
||||||
|
'remaining selfCount' => 2,
|
||||||
|
'legacyCount' => 23,
|
||||||
|
'otherCount' => 13,
|
||||||
|
'remaining directCount' => 124,
|
||||||
|
'remaining indirectCount' => 3244,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItNoticesExceededDirectThreshold()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::fromUrlEncodedString('max[direct]=1&max[self]=999999');
|
||||||
|
$this->assertTrue($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 1234,
|
||||||
|
'remaining selfCount' => 123,
|
||||||
|
'legacyCount' => 23,
|
||||||
|
'otherCount' => 13,
|
||||||
|
'remaining directCount' => 1,
|
||||||
|
'remaining indirectCount' => 3244,
|
||||||
|
]));
|
||||||
|
$this->assertFalse($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 1234,
|
||||||
|
'remaining selfCount' => 124,
|
||||||
|
'legacyCount' => 23,
|
||||||
|
'otherCount' => 13,
|
||||||
|
'remaining directCount' => 2,
|
||||||
|
'remaining indirectCount' => 3244,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItNoticesExceededIndirectThreshold()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::fromUrlEncodedString('max[indirect]=1&max[direct]=999999&max[self]=999999');
|
||||||
|
$this->assertTrue($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 1234,
|
||||||
|
'remaining selfCount' => 123,
|
||||||
|
'legacyCount' => 23,
|
||||||
|
'otherCount' => 13,
|
||||||
|
'remaining directCount' => 1234,
|
||||||
|
'remaining indirectCount' => 1,
|
||||||
|
]));
|
||||||
|
$this->assertFalse($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 1234,
|
||||||
|
'remaining selfCount' => 124,
|
||||||
|
'legacyCount' => 23,
|
||||||
|
'otherCount' => 13,
|
||||||
|
'remaining directCount' => 2324,
|
||||||
|
'remaining indirectCount' => 2,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIndirectThresholdIsUsedAsADefaultForDirectAndSelfThreshold()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::fromUrlEncodedString('max[indirect]=1');
|
||||||
|
$this->assertTrue($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 0,
|
||||||
|
'remaining selfCount' => 1,
|
||||||
|
'legacyCount' => 0,
|
||||||
|
'otherCount' => 0,
|
||||||
|
'remaining directCount' => 0,
|
||||||
|
'remaining indirectCount' => 0,
|
||||||
|
]));
|
||||||
|
$this->assertFalse($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 0,
|
||||||
|
'remaining selfCount' => 2,
|
||||||
|
'legacyCount' => 0,
|
||||||
|
'otherCount' => 0,
|
||||||
|
'remaining directCount' => 0,
|
||||||
|
'remaining indirectCount' => 0,
|
||||||
|
]));
|
||||||
|
$this->assertTrue($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 0,
|
||||||
|
'remaining selfCount' => 0,
|
||||||
|
'legacyCount' => 0,
|
||||||
|
'otherCount' => 0,
|
||||||
|
'remaining directCount' => 1,
|
||||||
|
'remaining indirectCount' => 0,
|
||||||
|
]));
|
||||||
|
$this->assertFalse($configuration->tolerates([
|
||||||
|
'unsilencedCount' => 0,
|
||||||
|
'remaining selfCount' => 0,
|
||||||
|
'legacyCount' => 0,
|
||||||
|
'otherCount' => 0,
|
||||||
|
'remaining directCount' => 2,
|
||||||
|
'remaining indirectCount' => 0,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItCanTellWhetherToDisplayAStackTrace()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::fromUrlEncodedString('');
|
||||||
|
$this->assertFalse($configuration->shouldDisplayStackTrace('interesting'));
|
||||||
|
|
||||||
|
$configuration = Configuration::fromRegex('/^interesting/');
|
||||||
|
$this->assertFalse($configuration->shouldDisplayStackTrace('uninteresting'));
|
||||||
|
$this->assertTrue($configuration->shouldDisplayStackTrace('interesting'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItCanBeDisabled()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::fromUrlEncodedString('disabled');
|
||||||
|
$this->assertFalse($configuration->isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItCanBeShushed()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::fromUrlEncodedString('verbose');
|
||||||
|
$this->assertFalse($configuration->verboseOutput());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOutputIsNotVerboseInWeakMode()
|
||||||
|
{
|
||||||
|
$configuration = Configuration::inWeakMode();
|
||||||
|
$this->assertFalse($configuration->verboseOutput());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
|
||||||
|
|
||||||
|
class DeprecationTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testItCanDetermineTheClassWhereTheDeprecationHappened()
|
||||||
|
{
|
||||||
|
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||||
|
$this->assertTrue($deprecation->originatesFromAnObject());
|
||||||
|
$this->assertSame(self::class, $deprecation->originatingClass());
|
||||||
|
$this->assertSame(__FUNCTION__, $deprecation->originatingMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItCanTellWhetherItIsInternal()
|
||||||
|
{
|
||||||
|
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||||
|
$this->assertTrue($deprecation->isSelf());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLegacyTestMethodIsDetectedAsSuch()
|
||||||
|
{
|
||||||
|
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||||
|
$this->assertTrue($deprecation->isLegacy('whatever'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItCanBeConvertedToAString()
|
||||||
|
{
|
||||||
|
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||||
|
$this->assertContains('💩', $deprecation->toString());
|
||||||
|
$this->assertContains(__FUNCTION__, $deprecation->toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItRulesOutFilesOutsideVendorsAsIndirect()
|
||||||
|
{
|
||||||
|
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||||
|
$this->assertFalse($deprecation->isIndirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is here to simulate the extra level from the piece of code
|
||||||
|
* triggering an error to the error handler
|
||||||
|
*/
|
||||||
|
public function debugBacktrace(): array
|
||||||
|
{
|
||||||
|
return debug_backtrace();
|
||||||
|
}
|
||||||
|
}
|
@ -73,7 +73,7 @@ Unsilenced deprecation notices (3)
|
|||||||
1x: unsilenced bar deprecation
|
1x: unsilenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
Remaining deprecation notices (1)
|
Remaining self deprecation notices (1)
|
||||||
|
|
||||||
1x: silenced bar deprecation
|
1x: silenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
--TEST--
|
||||||
|
Test eval()'d deprecation is not considered as self
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||||
|
putenv('ANSICON');
|
||||||
|
putenv('ConEmuANSI');
|
||||||
|
putenv('TERM');
|
||||||
|
|
||||||
|
$vendor = __DIR__;
|
||||||
|
while (!file_exists($vendor.'/vendor')) {
|
||||||
|
$vendor = dirname($vendor);
|
||||||
|
}
|
||||||
|
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
|
||||||
|
require PHPUNIT_COMPOSER_INSTALL;
|
||||||
|
require_once __DIR__.'/../../bootstrap.php';
|
||||||
|
eval("@trigger_error('who knows where I come from?', E_USER_DEPRECATED);");
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Other deprecation notices (1)
|
||||||
|
|
||||||
|
1x: who knows where I come from?
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace acme\lib;
|
||||||
|
|
||||||
|
class SomeService
|
||||||
|
{
|
||||||
|
public function deprecatedApi()
|
||||||
|
{
|
||||||
|
@trigger_error(
|
||||||
|
__FUNCTION__.' is deprecated! You should stop relying on it!',
|
||||||
|
E_USER_DEPRECATED
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
/* We have not caught up on the deprecations yet and still call the other lib
|
||||||
|
in a deprecated way. */
|
||||||
|
|
||||||
|
include __DIR__.'/../lib/SomeService.php';
|
||||||
|
$defraculator = new \acme\lib\SomeService();
|
||||||
|
$defraculator->deprecatedApi();
|
@ -0,0 +1,38 @@
|
|||||||
|
--TEST--
|
||||||
|
Test DeprecationErrorHandler in weak vendors mode on vendor file
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0&max[direct]=0');
|
||||||
|
putenv('ANSICON');
|
||||||
|
putenv('ConEmuANSI');
|
||||||
|
putenv('TERM');
|
||||||
|
|
||||||
|
$vendor = __DIR__;
|
||||||
|
while (!file_exists($vendor.'/vendor')) {
|
||||||
|
$vendor = dirname($vendor);
|
||||||
|
}
|
||||||
|
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
|
||||||
|
require PHPUNIT_COMPOSER_INSTALL;
|
||||||
|
require_once __DIR__.'/../../bootstrap.php';
|
||||||
|
eval(<<<'EOPHP'
|
||||||
|
namespace PHPUnit\Util;
|
||||||
|
|
||||||
|
class Test
|
||||||
|
{
|
||||||
|
public static function getGroups()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOPHP
|
||||||
|
);
|
||||||
|
require __DIR__.'/fake_vendor/autoload.php';
|
||||||
|
require __DIR__.'/fake_vendor/acme/outdated-lib/outdated_file.php';
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Remaining indirect deprecation notices (1)
|
||||||
|
|
||||||
|
1x: deprecatedApi is deprecated! You should stop relying on it!
|
||||||
|
1x in SomeService::deprecatedApi from acme\lib
|
@ -0,0 +1,39 @@
|
|||||||
|
--TEST--
|
||||||
|
Test DeprecationErrorHandler with quiet output
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
putenv('SYMFONY_DEPRECATIONS_HELPER=verbose=0');
|
||||||
|
putenv('ANSICON');
|
||||||
|
putenv('ConEmuANSI');
|
||||||
|
putenv('TERM');
|
||||||
|
|
||||||
|
$vendor = __DIR__;
|
||||||
|
while (!file_exists($vendor.'/vendor')) {
|
||||||
|
$vendor = dirname($vendor);
|
||||||
|
}
|
||||||
|
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
|
||||||
|
require PHPUNIT_COMPOSER_INSTALL;
|
||||||
|
require_once __DIR__.'/../../bootstrap.php';
|
||||||
|
|
||||||
|
@trigger_error('root deprecation', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
class FooTestCase
|
||||||
|
{
|
||||||
|
public function testLegacyFoo()
|
||||||
|
{
|
||||||
|
@trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
|
||||||
|
trigger_error('unsilenced foo deprecation', E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new FooTestCase();
|
||||||
|
$foo->testLegacyFoo();
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Unsilenced deprecation notices (1)
|
||||||
|
|
||||||
|
Legacy deprecation notices (1)
|
||||||
|
|
||||||
|
Other deprecation notices (1)
|
@ -0,0 +1,74 @@
|
|||||||
|
--TEST--
|
||||||
|
Test DeprecationErrorHandler with no self deprecations on self deprecation
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||||
|
putenv('ANSICON');
|
||||||
|
putenv('ConEmuANSI');
|
||||||
|
putenv('TERM');
|
||||||
|
|
||||||
|
$vendor = __DIR__;
|
||||||
|
while (!file_exists($vendor.'/vendor')) {
|
||||||
|
$vendor = dirname($vendor);
|
||||||
|
}
|
||||||
|
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
|
||||||
|
require PHPUNIT_COMPOSER_INSTALL;
|
||||||
|
require_once __DIR__.'/../../bootstrap.php';
|
||||||
|
|
||||||
|
@trigger_error('root deprecation', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
eval(<<<'EOPHP'
|
||||||
|
namespace PHPUnit\Util;
|
||||||
|
|
||||||
|
class Test
|
||||||
|
{
|
||||||
|
public static function getGroups()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOPHP
|
||||||
|
);
|
||||||
|
|
||||||
|
class FooTestCase
|
||||||
|
{
|
||||||
|
public function testLegacyFoo()
|
||||||
|
{
|
||||||
|
@trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
|
||||||
|
trigger_error('unsilenced foo deprecation', E_USER_DEPRECATED);
|
||||||
|
trigger_error('unsilenced foo deprecation', E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNonLegacyBar()
|
||||||
|
{
|
||||||
|
@trigger_error('silenced bar deprecation', E_USER_DEPRECATED);
|
||||||
|
trigger_error('unsilenced bar deprecation', E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new FooTestCase();
|
||||||
|
$foo->testLegacyFoo();
|
||||||
|
$foo->testNonLegacyBar();
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Unsilenced deprecation notices (3)
|
||||||
|
|
||||||
|
2x: unsilenced foo deprecation
|
||||||
|
2x in FooTestCase::testLegacyFoo
|
||||||
|
|
||||||
|
1x: unsilenced bar deprecation
|
||||||
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
|
Remaining self deprecation notices (1)
|
||||||
|
|
||||||
|
1x: silenced bar deprecation
|
||||||
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
|
Legacy deprecation notices (1)
|
||||||
|
|
||||||
|
Other deprecation notices (1)
|
||||||
|
|
||||||
|
1x: root deprecation
|
||||||
|
|
@ -73,7 +73,7 @@ Unsilenced deprecation notices (3)
|
|||||||
1x: unsilenced bar deprecation
|
1x: unsilenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
Remaining deprecation notices (1)
|
Remaining self deprecation notices (1)
|
||||||
|
|
||||||
1x: silenced bar deprecation
|
1x: silenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
@ -3,7 +3,7 @@ Test DeprecationErrorHandler in weak vendors mode on eval()'d deprecation
|
|||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
|
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||||
putenv('ANSICON');
|
putenv('ANSICON');
|
||||||
putenv('ConEmuANSI');
|
putenv('ConEmuANSI');
|
||||||
putenv('TERM');
|
putenv('TERM');
|
||||||
@ -19,7 +19,6 @@ eval("@trigger_error('who knows where I come from?', E_USER_DEPRECATED);");
|
|||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECTF--
|
||||||
|
|
||||||
Other deprecation notices (1)
|
Other deprecation notices (1)
|
||||||
|
|
||||||
1x: who knows where I come from?
|
1x: who knows where I come from?
|
||||||
|
@ -3,7 +3,7 @@ Test DeprecationErrorHandler in weak vendors mode on a non vendor file
|
|||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
|
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||||
putenv('ANSICON');
|
putenv('ANSICON');
|
||||||
putenv('ConEmuANSI');
|
putenv('ConEmuANSI');
|
||||||
putenv('TERM');
|
putenv('TERM');
|
||||||
@ -61,7 +61,7 @@ Unsilenced deprecation notices (3)
|
|||||||
1x: unsilenced bar deprecation
|
1x: unsilenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
Remaining deprecation notices (1)
|
Remaining self deprecation notices (1)
|
||||||
|
|
||||||
1x: silenced bar deprecation
|
1x: silenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
--TEST--
|
--TEST--
|
||||||
Test DeprecationErrorHandler in weak vendors mode on eval()'d deprecation
|
Test deprecations coming from a phar are not considered self deprecations
|
||||||
The phar can be regenerated by running php src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/generate_phar.php
|
The phar can be regenerated by running php src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/generate_phar.php
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
|
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||||
putenv('ANSICON');
|
putenv('ANSICON');
|
||||||
putenv('ConEmuANSI');
|
putenv('ConEmuANSI');
|
||||||
putenv('TERM');
|
putenv('TERM');
|
||||||
@ -21,7 +21,6 @@ include 'phar://deprecation.phar/deprecation.php';
|
|||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECTF--
|
||||||
|
|
||||||
Other deprecation notices (1)
|
Other deprecation notices (1)
|
||||||
|
|
||||||
1x: I come from… afar! :D
|
1x: I come from… afar! :D
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
--TEST--
|
--TEST--
|
||||||
Test DeprecationErrorHandler in weak vendors mode on vendor file
|
Test DeprecationErrorHandler with no self deprecations on vendor deprecation
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
|
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||||
putenv('ANSICON');
|
putenv('ANSICON');
|
||||||
putenv('ConEmuANSI');
|
putenv('ConEmuANSI');
|
||||||
putenv('TERM');
|
putenv('TERM');
|
||||||
@ -28,7 +28,7 @@ Unsilenced deprecation notices (3)
|
|||||||
1x: unsilenced bar deprecation
|
1x: unsilenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
|
||||||
Remaining vendor deprecation notices (1)
|
Remaining direct deprecation notices (1)
|
||||||
|
|
||||||
1x: silenced bar deprecation
|
1x: silenced bar deprecation
|
||||||
1x in FooTestCase::testNonLegacyBar
|
1x in FooTestCase::testNonLegacyBar
|
||||||
|
Reference in New Issue
Block a user