feature #29211 [PhpUnitBridge] Url encoded deprecations helper config (greg0ire)
This PR was merged into the 4.3-dev branch.
Discussion
----------
[PhpUnitBridge] Url encoded deprecations helper config
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | yes
| Tests pass? | yes
| Fixed tickets | #28048
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/pull/10701
First stab at implementing a new way of configuring the deprecation error handler. Includes a refactoring to keep things manageable.
Rework of #24867, blocked by #29718
TODO:
- [x] make the code 5.5 compatible 😢
- [x] add more tests
- [x] deprecate modes (using echo :P)
- [x] test this on real life projects and add some screenshots
- [x] docs PR
- [x] handle `strict`
- [x] adapt existing CI config
# Quiet configuration
![quiet](https://user-images.githubusercontent.com/657779/49341318-fa78c900-f64b-11e8-9504-a8a9eac4baf8.png)
# Default configuration
![verbose](https://user-images.githubusercontent.com/657779/49341322-10868980-f64c-11e8-9d90-dc3f6a18c335.png)
Commits
-------
1c73f9cfed
[PhpUnitBridge] Url encoded deprecations helper config
This commit is contained in:
commit
a36fbe3d38
@ -11,6 +11,9 @@
|
||||
|
||||
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.
|
||||
*
|
||||
@ -18,23 +21,30 @@ namespace Symfony\Bridge\PhpUnit;
|
||||
*/
|
||||
class DeprecationErrorHandler
|
||||
{
|
||||
const MODE_WEAK = 'weak';
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use max[self]=0 instead
|
||||
*/
|
||||
const MODE_WEAK_VENDORS = 'weak_vendors';
|
||||
const MODE_DISABLED = 'disabled';
|
||||
|
||||
private $mode = false;
|
||||
private $resolvedMode = false;
|
||||
const MODE_DISABLED = 'disabled';
|
||||
const MODE_WEAK = 'max[total]=999999&verbose=0';
|
||||
const MODE_STRICT = 'max[total]=0';
|
||||
|
||||
private $mode;
|
||||
private $configuration;
|
||||
private $deprecations = [
|
||||
'unsilencedCount' => 0,
|
||||
'remainingCount' => 0,
|
||||
'remaining selfCount' => 0,
|
||||
'legacyCount' => 0,
|
||||
'otherCount' => 0,
|
||||
'remaining vendorCount' => 0,
|
||||
'remaining directCount' => 0,
|
||||
'remaining indirectCount' => 0,
|
||||
'unsilenced' => [],
|
||||
'remaining' => [],
|
||||
'remaining self' => [],
|
||||
'legacy' => [],
|
||||
'other' => [],
|
||||
'remaining vendor' => [],
|
||||
'remaining direct' => [],
|
||||
'remaining indirect' => [],
|
||||
];
|
||||
|
||||
private static $isRegistered = false;
|
||||
@ -43,13 +53,16 @@ class DeprecationErrorHandler
|
||||
/**
|
||||
* Registers and configures the deprecation handler.
|
||||
*
|
||||
* The following reporting modes are supported:
|
||||
* - use "weak" to hide the deprecation report but keep a global count;
|
||||
* - use "weak_vendors" to fail only on deprecations triggered in your own code;
|
||||
* - use "/some-regexp/" to stop the test suite whenever a deprecation
|
||||
* message matches the given regular expression;
|
||||
* - use a number to define the upper bound of allowed deprecations,
|
||||
* making the test suite fail whenever more notices are triggered.
|
||||
* The mode is a query string with options:
|
||||
* - "disabled" to disable the deprecation handler
|
||||
* - "verbose" to enable/disable displaying the deprecation report
|
||||
* - "max" to configure the number of deprecations to allow before exiting with a non-zero
|
||||
* status code; it's an array with keys "total", "self", "direct" and "indirect"
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
@ -108,76 +121,41 @@ class DeprecationErrorHandler
|
||||
*/
|
||||
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';
|
||||
|
||||
return $ErrorHandler::handleError($type, $msg, $file, $line, $context);
|
||||
}
|
||||
|
||||
$trace = debug_backtrace();
|
||||
$deprecation = new Deprecation($msg, debug_backtrace(), $file);
|
||||
$group = 'other';
|
||||
$isVendor = self::MODE_WEAK_VENDORS === $mode && self::inVendors($file);
|
||||
|
||||
$i = \count($trace);
|
||||
while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_') || 0 === strpos($trace[$i]['class'], 'PHPUnit\\')))) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
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 ($deprecation->originatesFromAnObject()) {
|
||||
$class = $deprecation->originatingClass();
|
||||
$method = $deprecation->originatingMethod();
|
||||
|
||||
if (0 !== error_reporting()) {
|
||||
$group = 'unsilenced';
|
||||
} elseif (0 === strpos($method, 'testLegacy')
|
||||
|| 0 === strpos($method, 'provideLegacy')
|
||||
|| 0 === strpos($method, 'getLegacy')
|
||||
|| strpos($class, '\Legacy')
|
||||
|| \in_array('legacy', $Test::getGroups($class, $method), true)
|
||||
) {
|
||||
} elseif ($deprecation->isLegacy(self::$utilPrefix)) {
|
||||
$group = 'legacy';
|
||||
} elseif ($isVendor) {
|
||||
$group = 'remaining vendor';
|
||||
} elseif (!$deprecation->isSelf()) {
|
||||
$group = $deprecation->isIndirect() ? 'remaining indirect' : 'remaining direct';
|
||||
} else {
|
||||
$group = 'remaining';
|
||||
$group = 'remaining self';
|
||||
}
|
||||
|
||||
if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $msg)) {
|
||||
$e = new \Exception($msg);
|
||||
$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";
|
||||
if ($this->getConfiguration()->shouldDisplayStackTrace($msg)) {
|
||||
echo "\n".ucfirst($group).' '.$deprecation->toString();
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ('legacy' !== $group && self::MODE_WEAK !== $mode) {
|
||||
if ('legacy' !== $group) {
|
||||
$ref = &$this->deprecations[$group][$msg]['count'];
|
||||
++$ref;
|
||||
$ref = &$this->deprecations[$group][$msg][$class.'::'.$method];
|
||||
++$ref;
|
||||
}
|
||||
} elseif (self::MODE_WEAK !== $mode) {
|
||||
} else {
|
||||
$ref = &$this->deprecations[$group][$msg]['count'];
|
||||
++$ref;
|
||||
}
|
||||
@ -190,9 +168,9 @@ class DeprecationErrorHandler
|
||||
*/
|
||||
public function shutdown()
|
||||
{
|
||||
$mode = $this->getMode();
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
if (isset($mode[0]) && '/' === $mode[0]) {
|
||||
if ($configuration->isInRegexMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -200,28 +178,22 @@ class DeprecationErrorHandler
|
||||
restore_error_handler();
|
||||
|
||||
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) {
|
||||
$groups[] = 'remaining vendor';
|
||||
}
|
||||
|
||||
array_push($groups, 'legacy', 'other');
|
||||
|
||||
$this->displayDeprecations($groups, $mode);
|
||||
$this->displayDeprecations($groups, $configuration);
|
||||
|
||||
// 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
|
||||
foreach ($this->deprecations as $group => $arrayOrInt) {
|
||||
$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) {
|
||||
if (0 < (\is_int($arrayOrInt) ? $arrayOrInt : \count($arrayOrInt))) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function getMode()
|
||||
private function getConfiguration()
|
||||
{
|
||||
if (false !== $this->resolvedMode) {
|
||||
return $this->resolvedMode;
|
||||
if (null !== $this->configuration) {
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
if (false === $mode = $this->mode) {
|
||||
$mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
|
||||
}
|
||||
|
||||
if (self::MODE_DISABLED !== $mode
|
||||
&& self::MODE_WEAK !== $mode
|
||||
&& self::MODE_WEAK_VENDORS !== $mode
|
||||
&& (!isset($mode[0]) || '/' !== $mode[0])
|
||||
) {
|
||||
$mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0;
|
||||
if ('strict' === $mode) {
|
||||
return $this->configuration = Configuration::inStrictMode();
|
||||
}
|
||||
if (self::MODE_DISABLED === $mode) {
|
||||
return $this->configuration = Configuration::inDisabledMode();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
if (preg_match('/^[1-9][0-9]*$/', (string) $mode)) {
|
||||
return $this->configuration = Configuration::fromNumber($mode);
|
||||
}
|
||||
|
||||
$realPath = realpath($path);
|
||||
|
||||
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;
|
||||
return $this->configuration = Configuration::fromUrlEncodedString((string) $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @param bool $red
|
||||
* @param mixed $mode
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
@ -317,36 +259,36 @@ class DeprecationErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $groups
|
||||
* @param mixed $mode
|
||||
* @param string[] $groups
|
||||
* @param Configuration $configuration
|
||||
*/
|
||||
private function displayDeprecations($groups, $mode)
|
||||
private function displayDeprecations($groups, $configuration)
|
||||
{
|
||||
$cmp = function ($a, $b) {
|
||||
return $b['count'] - $a['count'];
|
||||
};
|
||||
|
||||
foreach ($groups as $group) {
|
||||
if (!$this->deprecations[$group.'Count']) {
|
||||
continue;
|
||||
}
|
||||
if ($this->deprecations[$group.'Count']) {
|
||||
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(
|
||||
sprintf('%s deprecation notices (%d)', ucfirst($group), $this->deprecations[$group.'Count']),
|
||||
'legacy' !== $group && 'remaining vendor' !== $group,
|
||||
$mode
|
||||
), "\n";
|
||||
if (!$configuration->verboseOutput()) {
|
||||
continue;
|
||||
}
|
||||
uasort($this->deprecations[$group], $cmp);
|
||||
|
||||
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) {
|
||||
echo "\n ", $notices['count'], 'x: ', $msg, "\n";
|
||||
arsort($notices);
|
||||
|
||||
arsort($notices);
|
||||
|
||||
foreach ($notices as $method => $count) {
|
||||
if ('count' !== $method) {
|
||||
echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n";
|
||||
foreach ($notices as $method => $count) {
|
||||
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 in FooTestCase::testNonLegacyBar
|
||||
|
||||
Remaining deprecation notices (1)
|
||||
Remaining self deprecation notices (1)
|
||||
|
||||
1x: silenced bar deprecation
|
||||
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 in FooTestCase::testNonLegacyBar
|
||||
|
||||
Remaining deprecation notices (1)
|
||||
Remaining self deprecation notices (1)
|
||||
|
||||
1x: silenced bar deprecation
|
||||
1x in FooTestCase::testNonLegacyBar
|
||||
|
@ -3,7 +3,7 @@ Test DeprecationErrorHandler in weak vendors mode on eval()'d deprecation
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||
putenv('ANSICON');
|
||||
putenv('ConEmuANSI');
|
||||
putenv('TERM');
|
||||
@ -19,7 +19,6 @@ eval("@trigger_error('who knows where I come from?', E_USER_DEPRECATED);");
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
|
||||
Other deprecation notices (1)
|
||||
|
||||
1x: who knows where I come from?
|
||||
|
@ -3,7 +3,7 @@ Test DeprecationErrorHandler in weak vendors mode on a non vendor file
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||
putenv('ANSICON');
|
||||
putenv('ConEmuANSI');
|
||||
putenv('TERM');
|
||||
@ -61,7 +61,7 @@ Unsilenced deprecation notices (3)
|
||||
1x: unsilenced bar deprecation
|
||||
1x in FooTestCase::testNonLegacyBar
|
||||
|
||||
Remaining deprecation notices (1)
|
||||
Remaining self deprecation notices (1)
|
||||
|
||||
1x: silenced bar deprecation
|
||||
1x in FooTestCase::testNonLegacyBar
|
||||
|
@ -1,10 +1,10 @@
|
||||
--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
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||
putenv('ANSICON');
|
||||
putenv('ConEmuANSI');
|
||||
putenv('TERM');
|
||||
@ -21,7 +21,6 @@ include 'phar://deprecation.phar/deprecation.php';
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
|
||||
Other deprecation notices (1)
|
||||
|
||||
1x: I come from… afar! :D
|
||||
|
@ -1,9 +1,9 @@
|
||||
--TEST--
|
||||
Test DeprecationErrorHandler in weak vendors mode on vendor file
|
||||
Test DeprecationErrorHandler with no self deprecations on vendor deprecation
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=max[self]=0');
|
||||
putenv('ANSICON');
|
||||
putenv('ConEmuANSI');
|
||||
putenv('TERM');
|
||||
@ -28,7 +28,7 @@ Unsilenced deprecation notices (3)
|
||||
1x: unsilenced bar deprecation
|
||||
1x in FooTestCase::testNonLegacyBar
|
||||
|
||||
Remaining vendor deprecation notices (1)
|
||||
Remaining direct deprecation notices (1)
|
||||
|
||||
1x: silenced bar deprecation
|
||||
1x in FooTestCase::testNonLegacyBar
|
||||
|
Reference in New Issue
Block a user