2012-02-07 10:09:38 +00:00
|
|
|
<?php
|
|
|
|
|
2015-01-13 14:17:46 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2012-11-19 11:48:33 +00:00
|
|
|
|
2014-12-18 18:37:23 +00:00
|
|
|
/**
|
2015-06-18 19:45:22 +01:00
|
|
|
* Catch deprecation notices and print a summary report at the end of the test suite.
|
2014-12-18 18:37:23 +00:00
|
|
|
*
|
2015-01-13 14:17:46 +00:00
|
|
|
* @author Nicolas Grekas <p@tchwork.com>
|
2014-12-18 18:37:23 +00:00
|
|
|
*/
|
|
|
|
class DeprecationErrorHandler
|
|
|
|
{
|
2015-12-01 19:49:56 +00:00
|
|
|
const MODE_WEAK = 'weak';
|
2017-02-05 17:06:12 +00:00
|
|
|
const MODE_WEAK_VENDORS = 'weak_vendors';
|
2016-03-19 11:01:40 +00:00
|
|
|
const MODE_DISABLED = 'disabled';
|
2015-12-01 19:49:56 +00:00
|
|
|
|
2014-12-18 18:37:23 +00:00
|
|
|
private static $isRegistered = false;
|
|
|
|
|
2015-12-10 08:43:06 +00:00
|
|
|
/**
|
|
|
|
* Registers and configures the deprecation handler.
|
|
|
|
*
|
|
|
|
* The following reporting modes are supported:
|
|
|
|
* - use "weak" to hide the deprecation report but keep a global count;
|
2017-02-05 17:06:12 +00:00
|
|
|
* - use "weak_vendors" to act as "weak" but only for vendors;
|
2015-12-10 08:43:06 +00:00
|
|
|
* - 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 trigerred.
|
|
|
|
*
|
2016-06-29 06:31:50 +01:00
|
|
|
* @param int|string|false $mode The reporting mode, defaults to not allowing any deprecations
|
2015-12-10 08:43:06 +00:00
|
|
|
*/
|
|
|
|
public static function register($mode = 0)
|
2014-12-18 18:37:23 +00:00
|
|
|
{
|
|
|
|
if (self::$isRegistered) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-23 12:13:39 +00:00
|
|
|
|
2017-02-20 14:27:51 +00:00
|
|
|
$UtilPrefix = class_exists('PHPUnit_Util_ErrorHandler') ? 'PHPUnit_Util_' : 'PHPUnit\Util\\';
|
|
|
|
|
2016-03-23 12:13:39 +00:00
|
|
|
$getMode = function () use ($mode) {
|
|
|
|
static $memoizedMode = false;
|
|
|
|
|
|
|
|
if (false !== $memoizedMode) {
|
|
|
|
return $memoizedMode;
|
|
|
|
}
|
|
|
|
if (false === $mode) {
|
2016-03-23 13:11:46 +00:00
|
|
|
$mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
|
|
|
|
}
|
2017-02-05 17:06:12 +00:00
|
|
|
if (DeprecationErrorHandler::MODE_WEAK !== $mode && DeprecationErrorHandler::MODE_WEAK_VENDORS !== $mode && (!isset($mode[0]) || '/' !== $mode[0])) {
|
2016-03-23 13:11:46 +00:00
|
|
|
$mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0;
|
2016-03-23 12:13:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $memoizedMode = $mode;
|
|
|
|
};
|
|
|
|
|
2017-02-05 17:06:12 +00:00
|
|
|
$inVendors = function ($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')) {
|
|
|
|
$r = new \ReflectionClass($class);
|
|
|
|
$v = dirname(dirname($r->getFileName()));
|
|
|
|
if (file_exists($v.'/composer/installed.json')) {
|
|
|
|
$vendors[] = $v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$path = realpath($path) ?: $path;
|
|
|
|
foreach ($vendors as $vendor) {
|
|
|
|
if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, strlen($vendor), 1), '/'.DIRECTORY_SEPARATOR)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2015-01-05 19:35:39 +00:00
|
|
|
$deprecations = array(
|
2015-06-18 19:45:22 +01:00
|
|
|
'unsilencedCount' => 0,
|
2015-01-05 19:35:39 +00:00
|
|
|
'remainingCount' => 0,
|
|
|
|
'legacyCount' => 0,
|
|
|
|
'otherCount' => 0,
|
2017-03-13 11:11:00 +00:00
|
|
|
'remaining vendorCount' => 0,
|
2015-06-18 19:45:22 +01:00
|
|
|
'unsilenced' => array(),
|
2015-01-05 19:35:39 +00:00
|
|
|
'remaining' => array(),
|
|
|
|
'legacy' => array(),
|
|
|
|
'other' => array(),
|
2017-03-13 11:11:00 +00:00
|
|
|
'remaining vendor' => array(),
|
2015-01-05 19:35:39 +00:00
|
|
|
);
|
2017-05-21 10:48:35 +01:00
|
|
|
$deprecationHandler = function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) {
|
2016-10-28 08:07:59 +01:00
|
|
|
$mode = $getMode();
|
2016-10-28 09:25:06 +01:00
|
|
|
if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || DeprecationErrorHandler::MODE_DISABLED === $mode) {
|
2017-02-20 14:27:51 +00:00
|
|
|
$ErrorHandler = $UtilPrefix.'ErrorHandler';
|
|
|
|
|
|
|
|
return $ErrorHandler::handleError($type, $msg, $file, $line, $context);
|
2014-12-18 18:37:23 +00:00
|
|
|
}
|
|
|
|
|
2016-03-23 12:13:39 +00:00
|
|
|
$trace = debug_backtrace(true);
|
|
|
|
$group = 'other';
|
2014-12-18 18:37:23 +00:00
|
|
|
|
2017-02-05 17:06:12 +00:00
|
|
|
$isWeak = DeprecationErrorHandler::MODE_WEAK === $mode || (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $isVendor = $inVendors($file));
|
|
|
|
|
2014-12-18 18:37:23 +00:00
|
|
|
$i = count($trace);
|
2017-02-20 14:27:51 +00:00
|
|
|
while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_') || 0 === strpos($trace[$i]['class'], 'PHPUnit\\')))) {
|
2014-12-18 18:37:23 +00:00
|
|
|
// No-op
|
|
|
|
}
|
|
|
|
|
2015-11-27 13:36:40 +00:00
|
|
|
if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) {
|
2017-10-17 18:11:27 +01:00
|
|
|
if (isset($trace[$i]['class']) && in_array($trace[$i]['class'], array('Symfony\Bridge\PhpUnit\SymfonyTestsListener', 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListener'), true)) {
|
|
|
|
$parsedMsg = unserialize($msg);
|
|
|
|
$msg = $parsedMsg['deprecation'];
|
|
|
|
$class = $parsedMsg['class'];
|
|
|
|
$method = $parsedMsg['method'];
|
|
|
|
} else {
|
|
|
|
$class = isset($trace[$i]['object']) ? get_class($trace[$i]['object']) : $trace[$i]['class'];
|
|
|
|
$method = $trace[$i]['function'];
|
|
|
|
}
|
|
|
|
|
2017-02-20 14:27:51 +00:00
|
|
|
$Test = $UtilPrefix.'Test';
|
2015-01-05 19:35:39 +00:00
|
|
|
|
2015-11-27 13:36:40 +00:00
|
|
|
if (0 !== error_reporting()) {
|
|
|
|
$group = 'unsilenced';
|
|
|
|
} elseif (0 === strpos($method, 'testLegacy')
|
|
|
|
|| 0 === strpos($method, 'provideLegacy')
|
|
|
|
|| 0 === strpos($method, 'getLegacy')
|
|
|
|
|| strpos($class, '\Legacy')
|
2017-02-20 14:27:51 +00:00
|
|
|
|| in_array('legacy', $Test::getGroups($class, $method), true)
|
2015-11-27 13:36:40 +00:00
|
|
|
) {
|
|
|
|
$group = 'legacy';
|
2017-02-05 17:06:12 +00:00
|
|
|
} elseif (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $isVendor) {
|
|
|
|
$group = 'remaining vendor';
|
2015-11-27 13:36:40 +00:00
|
|
|
} else {
|
|
|
|
$group = 'remaining';
|
|
|
|
}
|
|
|
|
|
2015-12-01 19:49:56 +00:00
|
|
|
if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $msg)) {
|
2015-11-27 13:36:40 +00:00
|
|
|
$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";
|
2015-01-05 19:35:39 +00:00
|
|
|
|
2015-11-27 13:36:40 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
2017-02-05 17:06:12 +00:00
|
|
|
if ('legacy' !== $group && !$isWeak) {
|
2015-06-18 19:45:22 +01:00
|
|
|
$ref = &$deprecations[$group][$msg]['count'];
|
2015-01-13 14:17:46 +00:00
|
|
|
++$ref;
|
2015-06-18 19:45:22 +01:00
|
|
|
$ref = &$deprecations[$group][$msg][$class.'::'.$method];
|
2015-01-13 14:17:46 +00:00
|
|
|
++$ref;
|
2014-12-18 18:37:23 +00:00
|
|
|
}
|
2017-02-05 17:06:12 +00:00
|
|
|
} elseif (!$isWeak) {
|
2015-06-18 19:45:22 +01:00
|
|
|
$ref = &$deprecations[$group][$msg]['count'];
|
2015-01-13 14:17:46 +00:00
|
|
|
++$ref;
|
|
|
|
}
|
|
|
|
++$deprecations[$group.'Count'];
|
2015-01-05 19:35:39 +00:00
|
|
|
};
|
|
|
|
$oldErrorHandler = set_error_handler($deprecationHandler);
|
2014-12-18 18:37:23 +00:00
|
|
|
|
|
|
|
if (null !== $oldErrorHandler) {
|
|
|
|
restore_error_handler();
|
2017-02-20 14:27:51 +00:00
|
|
|
if (array($UtilPrefix.'ErrorHandler', 'handleError') === $oldErrorHandler) {
|
2014-12-18 18:37:23 +00:00
|
|
|
restore_error_handler();
|
2015-02-22 08:55:44 +00:00
|
|
|
self::register($mode);
|
2014-12-18 18:37:23 +00:00
|
|
|
}
|
2016-03-23 13:45:24 +00:00
|
|
|
} else {
|
2014-12-18 18:37:23 +00:00
|
|
|
self::$isRegistered = true;
|
2015-01-13 14:17:46 +00:00
|
|
|
if (self::hasColorSupport()) {
|
|
|
|
$colorize = function ($str, $red) {
|
|
|
|
$color = $red ? '41;37' : '43;30';
|
2015-01-05 19:35:39 +00:00
|
|
|
|
2015-01-13 14:17:46 +00:00
|
|
|
return "\x1B[{$color}m{$str}\x1B[0m";
|
|
|
|
};
|
|
|
|
} else {
|
2017-01-20 15:16:44 +00:00
|
|
|
$colorize = function ($str) { return $str; };
|
2015-01-13 14:17:46 +00:00
|
|
|
}
|
2016-03-23 12:13:39 +00:00
|
|
|
register_shutdown_function(function () use ($getMode, &$deprecations, $deprecationHandler, $colorize) {
|
|
|
|
$mode = $getMode();
|
2016-03-23 13:45:24 +00:00
|
|
|
if (isset($mode[0]) && '/' === $mode[0]) {
|
|
|
|
return;
|
|
|
|
}
|
2015-01-05 19:35:39 +00:00
|
|
|
$currErrorHandler = set_error_handler('var_dump');
|
|
|
|
restore_error_handler();
|
2014-12-18 18:37:23 +00:00
|
|
|
|
2016-03-23 13:11:46 +00:00
|
|
|
if (DeprecationErrorHandler::MODE_WEAK === $mode) {
|
2017-01-20 15:16:44 +00:00
|
|
|
$colorize = function ($str) { return $str; };
|
2016-03-23 12:13:39 +00:00
|
|
|
}
|
2015-01-05 19:35:39 +00:00
|
|
|
if ($currErrorHandler !== $deprecationHandler) {
|
|
|
|
echo "\n", $colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
$cmp = function ($a, $b) {
|
|
|
|
return $b['count'] - $a['count'];
|
|
|
|
};
|
|
|
|
|
2017-02-05 17:06:12 +00:00
|
|
|
$groups = array('unsilenced', 'remaining');
|
|
|
|
if (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode) {
|
|
|
|
$groups[] = 'remaining vendor';
|
|
|
|
}
|
|
|
|
array_push($groups, 'legacy', 'other');
|
|
|
|
|
|
|
|
foreach ($groups as $group) {
|
2015-04-05 09:39:53 +01:00
|
|
|
if ($deprecations[$group.'Count']) {
|
2017-02-05 17:06:12 +00:00
|
|
|
echo "\n", $colorize(
|
|
|
|
sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']),
|
|
|
|
'legacy' !== $group && 'remaining vendor' !== $group
|
|
|
|
), "\n";
|
2015-01-05 19:35:39 +00:00
|
|
|
|
2015-01-13 14:17:46 +00:00
|
|
|
uasort($deprecations[$group], $cmp);
|
2015-01-05 19:35:39 +00:00
|
|
|
|
2015-01-13 14:17:46 +00:00
|
|
|
foreach ($deprecations[$group] as $msg => $notices) {
|
2015-03-29 11:32:48 +01:00
|
|
|
echo "\n", rtrim($msg, '.'), ': ', $notices['count'], "x\n";
|
2015-01-05 19:35:39 +00:00
|
|
|
|
|
|
|
arsort($notices);
|
|
|
|
|
|
|
|
foreach ($notices as $method => $count) {
|
|
|
|
if ('count' !== $method) {
|
|
|
|
echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n";
|
2014-12-18 18:37:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-01-05 19:35:39 +00:00
|
|
|
}
|
|
|
|
if (!empty($notices)) {
|
|
|
|
echo "\n";
|
2014-12-18 18:37:23 +00:00
|
|
|
}
|
2016-03-23 12:13:39 +00:00
|
|
|
|
2015-12-10 08:43:06 +00:00
|
|
|
if (DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) {
|
2015-04-05 09:39:53 +01:00
|
|
|
exit(1);
|
2015-02-22 08:55:44 +00:00
|
|
|
}
|
2014-12-18 18:37:23 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-02 20:44:10 +01:00
|
|
|
public static function collectDeprecations($outputFile)
|
|
|
|
{
|
|
|
|
$deprecations = array();
|
|
|
|
$previousErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, &$previousErrorHandler) {
|
|
|
|
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
|
|
|
|
return $previousErrorHandler ? $previousErrorHandler($type, $msg, $file, $line, $context) : false;
|
|
|
|
}
|
|
|
|
$deprecations[] = array(error_reporting(), $msg);
|
|
|
|
});
|
2017-10-16 14:30:39 +01:00
|
|
|
// This can be registered before the PHPUnit error handler.
|
|
|
|
if (!$previousErrorHandler) {
|
|
|
|
$UtilPrefix = class_exists('PHPUnit_Util_ErrorHandler') ? 'PHPUnit_Util_' : 'PHPUnit\Util\\';
|
|
|
|
$previousErrorHandler = $UtilPrefix.'ErrorHandler::handleError';
|
|
|
|
}
|
|
|
|
|
2017-06-02 20:44:10 +01:00
|
|
|
register_shutdown_function(function () use ($outputFile, &$deprecations) {
|
|
|
|
file_put_contents($outputFile, serialize($deprecations));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-01-13 14:17:46 +00:00
|
|
|
private static function hasColorSupport()
|
|
|
|
{
|
|
|
|
if ('\\' === DIRECTORY_SEPARATOR) {
|
2016-04-01 18:41:59 +01:00
|
|
|
return
|
2016-08-18 13:32:44 +01:00
|
|
|
'10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD
|
2016-04-01 18:41:59 +01:00
|
|
|
|| false !== getenv('ANSICON')
|
|
|
|
|| 'ON' === getenv('ConEmuANSI')
|
|
|
|
|| 'xterm' === getenv('TERM');
|
2015-01-13 14:17:46 +00:00
|
|
|
}
|
2013-05-09 08:12:11 +01:00
|
|
|
|
2015-01-13 14:17:46 +00:00
|
|
|
return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT);
|
|
|
|
}
|
|
|
|
}
|