annotate vendor/symfony/phpunit-bridge/DeprecationErrorHandler.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents a9cd425dd02b
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of the Symfony package.
Chris@0 5 *
Chris@0 6 * (c) Fabien Potencier <fabien@symfony.com>
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Symfony\Bridge\PhpUnit;
Chris@0 13
Chris@0 14 /**
Chris@0 15 * Catch deprecation notices and print a summary report at the end of the test suite.
Chris@0 16 *
Chris@0 17 * @author Nicolas Grekas <p@tchwork.com>
Chris@0 18 */
Chris@0 19 class DeprecationErrorHandler
Chris@0 20 {
Chris@0 21 const MODE_WEAK = 'weak';
Chris@0 22 const MODE_WEAK_VENDORS = 'weak_vendors';
Chris@0 23 const MODE_DISABLED = 'disabled';
Chris@0 24
Chris@0 25 private static $isRegistered = false;
Chris@0 26
Chris@0 27 /**
Chris@0 28 * Registers and configures the deprecation handler.
Chris@0 29 *
Chris@0 30 * The following reporting modes are supported:
Chris@0 31 * - use "weak" to hide the deprecation report but keep a global count;
Chris@0 32 * - use "weak_vendors" to act as "weak" but only for vendors;
Chris@0 33 * - use "/some-regexp/" to stop the test suite whenever a deprecation
Chris@0 34 * message matches the given regular expression;
Chris@0 35 * - use a number to define the upper bound of allowed deprecations,
Chris@4 36 * making the test suite fail whenever more notices are triggered.
Chris@0 37 *
Chris@0 38 * @param int|string|false $mode The reporting mode, defaults to not allowing any deprecations
Chris@0 39 */
Chris@0 40 public static function register($mode = 0)
Chris@0 41 {
Chris@0 42 if (self::$isRegistered) {
Chris@0 43 return;
Chris@0 44 }
Chris@0 45
Chris@0 46 $UtilPrefix = class_exists('PHPUnit_Util_ErrorHandler') ? 'PHPUnit_Util_' : 'PHPUnit\Util\\';
Chris@0 47
Chris@0 48 $getMode = function () use ($mode) {
Chris@0 49 static $memoizedMode = false;
Chris@0 50
Chris@0 51 if (false !== $memoizedMode) {
Chris@0 52 return $memoizedMode;
Chris@0 53 }
Chris@0 54 if (false === $mode) {
Chris@0 55 $mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
Chris@0 56 }
Chris@5 57 if (DeprecationErrorHandler::MODE_DISABLED !== $mode
Chris@5 58 && DeprecationErrorHandler::MODE_WEAK !== $mode
Chris@5 59 && DeprecationErrorHandler::MODE_WEAK_VENDORS !== $mode
Chris@4 60 && (!isset($mode[0]) || '/' !== $mode[0])
Chris@4 61 ) {
Chris@0 62 $mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0;
Chris@0 63 }
Chris@0 64
Chris@0 65 return $memoizedMode = $mode;
Chris@0 66 };
Chris@0 67
Chris@0 68 $inVendors = function ($path) {
Chris@0 69 /** @var string[] absolute paths to vendor directories */
Chris@0 70 static $vendors;
Chris@0 71 if (null === $vendors) {
Chris@0 72 foreach (get_declared_classes() as $class) {
Chris@0 73 if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
Chris@0 74 $r = new \ReflectionClass($class);
Chris@4 75 $v = \dirname(\dirname($r->getFileName()));
Chris@0 76 if (file_exists($v.'/composer/installed.json')) {
Chris@0 77 $vendors[] = $v;
Chris@0 78 }
Chris@0 79 }
Chris@0 80 }
Chris@0 81 }
Chris@0 82 $realPath = realpath($path);
Chris@0 83 if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) {
Chris@0 84 return true;
Chris@0 85 }
Chris@0 86 foreach ($vendors as $vendor) {
Chris@4 87 if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
Chris@0 88 return true;
Chris@0 89 }
Chris@0 90 }
Chris@0 91
Chris@0 92 return false;
Chris@0 93 };
Chris@0 94
Chris@0 95 $deprecations = array(
Chris@0 96 'unsilencedCount' => 0,
Chris@0 97 'remainingCount' => 0,
Chris@0 98 'legacyCount' => 0,
Chris@0 99 'otherCount' => 0,
Chris@0 100 'remaining vendorCount' => 0,
Chris@0 101 'unsilenced' => array(),
Chris@0 102 'remaining' => array(),
Chris@0 103 'legacy' => array(),
Chris@0 104 'other' => array(),
Chris@0 105 'remaining vendor' => array(),
Chris@0 106 );
Chris@0 107 $deprecationHandler = function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) {
Chris@5 108 if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || DeprecationErrorHandler::MODE_DISABLED === $mode = $getMode()) {
Chris@0 109 $ErrorHandler = $UtilPrefix.'ErrorHandler';
Chris@0 110
Chris@0 111 return $ErrorHandler::handleError($type, $msg, $file, $line, $context);
Chris@0 112 }
Chris@0 113
Chris@4 114 $trace = debug_backtrace();
Chris@0 115 $group = 'other';
Chris@5 116 $isVendor = DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $inVendors($file);
Chris@0 117
Chris@4 118 $i = \count($trace);
Chris@0 119 while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_') || 0 === strpos($trace[$i]['class'], 'PHPUnit\\')))) {
Chris@0 120 // No-op
Chris@0 121 }
Chris@0 122
Chris@0 123 if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) {
Chris@0 124 if (isset($trace[$i]['class']) && 0 === strpos($trace[$i]['class'], 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor')) {
Chris@0 125 $parsedMsg = unserialize($msg);
Chris@0 126 $msg = $parsedMsg['deprecation'];
Chris@0 127 $class = $parsedMsg['class'];
Chris@0 128 $method = $parsedMsg['method'];
Chris@0 129 // If the deprecation has been triggered via
Chris@0 130 // \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
Chris@0 131 // then we need to use the serialized information to determine
Chris@0 132 // if the error has been triggered from vendor code.
Chris@5 133 $isVendor = DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && isset($parsedMsg['triggering_file']) && $inVendors($parsedMsg['triggering_file']);
Chris@0 134 } else {
Chris@4 135 $class = isset($trace[$i]['object']) ? \get_class($trace[$i]['object']) : $trace[$i]['class'];
Chris@0 136 $method = $trace[$i]['function'];
Chris@0 137 }
Chris@0 138
Chris@0 139 $Test = $UtilPrefix.'Test';
Chris@0 140
Chris@0 141 if (0 !== error_reporting()) {
Chris@0 142 $group = 'unsilenced';
Chris@0 143 } elseif (0 === strpos($method, 'testLegacy')
Chris@0 144 || 0 === strpos($method, 'provideLegacy')
Chris@0 145 || 0 === strpos($method, 'getLegacy')
Chris@0 146 || strpos($class, '\Legacy')
Chris@4 147 || \in_array('legacy', $Test::getGroups($class, $method), true)
Chris@0 148 ) {
Chris@0 149 $group = 'legacy';
Chris@0 150 } elseif ($isVendor) {
Chris@0 151 $group = 'remaining vendor';
Chris@0 152 } else {
Chris@0 153 $group = 'remaining';
Chris@0 154 }
Chris@0 155
Chris@0 156 if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $msg)) {
Chris@0 157 $e = new \Exception($msg);
Chris@0 158 $r = new \ReflectionProperty($e, 'trace');
Chris@0 159 $r->setAccessible(true);
Chris@4 160 $r->setValue($e, \array_slice($trace, 1, $i));
Chris@0 161
Chris@0 162 echo "\n".ucfirst($group).' deprecation triggered by '.$class.'::'.$method.':';
Chris@0 163 echo "\n".$msg;
Chris@0 164 echo "\nStack trace:";
Chris@4 165 echo "\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString());
Chris@0 166 echo "\n";
Chris@0 167
Chris@0 168 exit(1);
Chris@0 169 }
Chris@5 170 if ('legacy' !== $group && DeprecationErrorHandler::MODE_WEAK !== $mode) {
Chris@0 171 $ref = &$deprecations[$group][$msg]['count'];
Chris@0 172 ++$ref;
Chris@0 173 $ref = &$deprecations[$group][$msg][$class.'::'.$method];
Chris@0 174 ++$ref;
Chris@0 175 }
Chris@5 176 } elseif (DeprecationErrorHandler::MODE_WEAK !== $mode) {
Chris@0 177 $ref = &$deprecations[$group][$msg]['count'];
Chris@0 178 ++$ref;
Chris@0 179 }
Chris@0 180 ++$deprecations[$group.'Count'];
Chris@0 181 };
Chris@0 182 $oldErrorHandler = set_error_handler($deprecationHandler);
Chris@0 183
Chris@0 184 if (null !== $oldErrorHandler) {
Chris@0 185 restore_error_handler();
Chris@0 186 if (array($UtilPrefix.'ErrorHandler', 'handleError') === $oldErrorHandler) {
Chris@0 187 restore_error_handler();
Chris@0 188 self::register($mode);
Chris@0 189 }
Chris@0 190 } else {
Chris@0 191 self::$isRegistered = true;
Chris@0 192 if (self::hasColorSupport()) {
Chris@0 193 $colorize = function ($str, $red) {
Chris@0 194 $color = $red ? '41;37' : '43;30';
Chris@0 195
Chris@0 196 return "\x1B[{$color}m{$str}\x1B[0m";
Chris@0 197 };
Chris@0 198 } else {
Chris@0 199 $colorize = function ($str) { return $str; };
Chris@0 200 }
Chris@0 201 register_shutdown_function(function () use ($getMode, &$deprecations, $deprecationHandler, $colorize) {
Chris@0 202 $mode = $getMode();
Chris@0 203 if (isset($mode[0]) && '/' === $mode[0]) {
Chris@0 204 return;
Chris@0 205 }
Chris@0 206 $currErrorHandler = set_error_handler('var_dump');
Chris@0 207 restore_error_handler();
Chris@0 208
Chris@5 209 if (DeprecationErrorHandler::MODE_WEAK === $mode) {
Chris@0 210 $colorize = function ($str) { return $str; };
Chris@0 211 }
Chris@0 212 if ($currErrorHandler !== $deprecationHandler) {
Chris@0 213 echo "\n", $colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n";
Chris@0 214 }
Chris@0 215
Chris@0 216 $cmp = function ($a, $b) {
Chris@0 217 return $b['count'] - $a['count'];
Chris@0 218 };
Chris@0 219
Chris@0 220 $groups = array('unsilenced', 'remaining');
Chris@5 221 if (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode) {
Chris@0 222 $groups[] = 'remaining vendor';
Chris@0 223 }
Chris@0 224 array_push($groups, 'legacy', 'other');
Chris@0 225
Chris@0 226 $displayDeprecations = function ($deprecations) use ($colorize, $cmp, $groups) {
Chris@0 227 foreach ($groups as $group) {
Chris@0 228 if ($deprecations[$group.'Count']) {
Chris@0 229 echo "\n", $colorize(
Chris@0 230 sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']),
Chris@0 231 'legacy' !== $group && 'remaining vendor' !== $group
Chris@0 232 ), "\n";
Chris@0 233
Chris@0 234 uasort($deprecations[$group], $cmp);
Chris@0 235
Chris@0 236 foreach ($deprecations[$group] as $msg => $notices) {
Chris@0 237 echo "\n ", $notices['count'], 'x: ', $msg, "\n";
Chris@0 238
Chris@0 239 arsort($notices);
Chris@0 240
Chris@0 241 foreach ($notices as $method => $count) {
Chris@0 242 if ('count' !== $method) {
Chris@0 243 echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n";
Chris@0 244 }
Chris@0 245 }
Chris@0 246 }
Chris@0 247 }
Chris@0 248 }
Chris@0 249 if (!empty($notices)) {
Chris@0 250 echo "\n";
Chris@0 251 }
Chris@0 252 };
Chris@0 253
Chris@0 254 $displayDeprecations($deprecations);
Chris@0 255
Chris@0 256 // store failing status
Chris@5 257 $isFailing = DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount'];
Chris@0 258
Chris@0 259 // reset deprecations array
Chris@0 260 foreach ($deprecations as $group => $arrayOrInt) {
Chris@4 261 $deprecations[$group] = \is_int($arrayOrInt) ? 0 : array();
Chris@0 262 }
Chris@0 263
Chris@0 264 register_shutdown_function(function () use (&$deprecations, $isFailing, $displayDeprecations, $mode) {
Chris@0 265 foreach ($deprecations as $group => $arrayOrInt) {
Chris@4 266 if (0 < (\is_int($arrayOrInt) ? $arrayOrInt : \count($arrayOrInt))) {
Chris@0 267 echo "Shutdown-time deprecations:\n";
Chris@0 268 break;
Chris@0 269 }
Chris@0 270 }
Chris@0 271 $displayDeprecations($deprecations);
Chris@5 272 if ($isFailing || DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) {
Chris@0 273 exit(1);
Chris@0 274 }
Chris@0 275 });
Chris@0 276 });
Chris@0 277 }
Chris@0 278 }
Chris@0 279
Chris@0 280 public static function collectDeprecations($outputFile)
Chris@0 281 {
Chris@0 282 $deprecations = array();
Chris@0 283 $previousErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, &$previousErrorHandler) {
Chris@0 284 if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
Chris@0 285 if ($previousErrorHandler) {
Chris@0 286 return $previousErrorHandler($type, $msg, $file, $line, $context);
Chris@0 287 }
Chris@0 288 static $autoload = true;
Chris@0 289
Chris@0 290 $ErrorHandler = class_exists('PHPUnit_Util_ErrorHandler', $autoload) ? 'PHPUnit_Util_ErrorHandler' : 'PHPUnit\Util\ErrorHandler';
Chris@0 291 $autoload = false;
Chris@0 292
Chris@0 293 return $ErrorHandler::handleError($type, $msg, $file, $line, $context);
Chris@0 294 }
Chris@0 295 $deprecations[] = array(error_reporting(), $msg, $file);
Chris@0 296 });
Chris@0 297
Chris@0 298 register_shutdown_function(function () use ($outputFile, &$deprecations) {
Chris@0 299 file_put_contents($outputFile, serialize($deprecations));
Chris@0 300 });
Chris@0 301 }
Chris@0 302
Chris@0 303 /**
Chris@0 304 * Returns true if STDOUT is defined and supports colorization.
Chris@0 305 *
Chris@0 306 * Reference: Composer\XdebugHandler\Process::supportsColor
Chris@0 307 * https://github.com/composer/xdebug-handler
Chris@0 308 *
Chris@0 309 * @return bool
Chris@0 310 */
Chris@0 311 private static function hasColorSupport()
Chris@0 312 {
Chris@4 313 if (!\defined('STDOUT')) {
Chris@0 314 return false;
Chris@0 315 }
Chris@0 316
Chris@4 317 if ('Hyper' === getenv('TERM_PROGRAM')) {
Chris@4 318 return true;
Chris@4 319 }
Chris@4 320
Chris@4 321 if (\DIRECTORY_SEPARATOR === '\\') {
Chris@4 322 return (\function_exists('sapi_windows_vt100_support')
Chris@0 323 && sapi_windows_vt100_support(STDOUT))
Chris@0 324 || false !== getenv('ANSICON')
Chris@0 325 || 'ON' === getenv('ConEmuANSI')
Chris@0 326 || 'xterm' === getenv('TERM');
Chris@0 327 }
Chris@0 328
Chris@4 329 if (\function_exists('stream_isatty')) {
Chris@0 330 return stream_isatty(STDOUT);
Chris@0 331 }
Chris@0 332
Chris@4 333 if (\function_exists('posix_isatty')) {
Chris@0 334 return posix_isatty(STDOUT);
Chris@0 335 }
Chris@0 336
Chris@0 337 $stat = fstat(STDOUT);
Chris@0 338 // Check if formatted mode is S_IFCHR
Chris@0 339 return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
Chris@0 340 }
Chris@0 341 }