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_DISABLED = 'disabled';
|
Chris@0
|
23
|
Chris@0
|
24 private static $isRegistered = false;
|
Chris@0
|
25
|
Chris@0
|
26 /**
|
Chris@0
|
27 * Registers and configures the deprecation handler.
|
Chris@0
|
28 *
|
Chris@0
|
29 * The following reporting modes are supported:
|
Chris@0
|
30 * - use "weak" to hide the deprecation report but keep a global count;
|
Chris@0
|
31 * - use "/some-regexp/" to stop the test suite whenever a deprecation
|
Chris@0
|
32 * message matches the given regular expression;
|
Chris@0
|
33 * - use a number to define the upper bound of allowed deprecations,
|
Chris@0
|
34 * making the test suite fail whenever more notices are trigerred.
|
Chris@0
|
35 *
|
Chris@0
|
36 * @param int|string|false $mode The reporting mode, defaults to not allowing any deprecations
|
Chris@0
|
37 */
|
Chris@0
|
38 public static function register($mode = 0)
|
Chris@0
|
39 {
|
Chris@0
|
40 if (self::$isRegistered) {
|
Chris@0
|
41 return;
|
Chris@0
|
42 }
|
Chris@0
|
43
|
Chris@0
|
44 $getMode = function () use ($mode) {
|
Chris@0
|
45 static $memoizedMode = false;
|
Chris@0
|
46
|
Chris@0
|
47 if (false !== $memoizedMode) {
|
Chris@0
|
48 return $memoizedMode;
|
Chris@0
|
49 }
|
Chris@0
|
50 if (false === $mode) {
|
Chris@0
|
51 $mode = getenv('SYMFONY_DEPRECATIONS_HELPER');
|
Chris@0
|
52 }
|
Chris@0
|
53 if (DeprecationErrorHandler::MODE_WEAK !== $mode && (!isset($mode[0]) || '/' !== $mode[0])) {
|
Chris@0
|
54 $mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0;
|
Chris@0
|
55 }
|
Chris@0
|
56
|
Chris@0
|
57 return $memoizedMode = $mode;
|
Chris@0
|
58 };
|
Chris@0
|
59
|
Chris@0
|
60 $deprecations = array(
|
Chris@0
|
61 'unsilencedCount' => 0,
|
Chris@0
|
62 'remainingCount' => 0,
|
Chris@0
|
63 'legacyCount' => 0,
|
Chris@0
|
64 'otherCount' => 0,
|
Chris@0
|
65 'unsilenced' => array(),
|
Chris@0
|
66 'remaining' => array(),
|
Chris@0
|
67 'legacy' => array(),
|
Chris@0
|
68 'other' => array(),
|
Chris@0
|
69 );
|
Chris@0
|
70 $deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations, $getMode) {
|
Chris@0
|
71 $mode = $getMode();
|
Chris@0
|
72 if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || DeprecationErrorHandler::MODE_DISABLED === $mode) {
|
Chris@0
|
73 return \PHPUnit_Util_ErrorHandler::handleError($type, $msg, $file, $line, $context);
|
Chris@0
|
74 }
|
Chris@0
|
75
|
Chris@0
|
76 $trace = debug_backtrace(true);
|
Chris@0
|
77 $group = 'other';
|
Chris@0
|
78
|
Chris@0
|
79 $i = count($trace);
|
Chris@0
|
80 while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_')))) {
|
Chris@0
|
81 // No-op
|
Chris@0
|
82 }
|
Chris@0
|
83
|
Chris@0
|
84 if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) {
|
Chris@0
|
85 $class = isset($trace[$i]['object']) ? get_class($trace[$i]['object']) : $trace[$i]['class'];
|
Chris@0
|
86 $method = $trace[$i]['function'];
|
Chris@0
|
87
|
Chris@0
|
88 if (0 !== error_reporting()) {
|
Chris@0
|
89 $group = 'unsilenced';
|
Chris@0
|
90 } elseif (0 === strpos($method, 'testLegacy')
|
Chris@0
|
91 || 0 === strpos($method, 'provideLegacy')
|
Chris@0
|
92 || 0 === strpos($method, 'getLegacy')
|
Chris@0
|
93 || strpos($class, '\Legacy')
|
Chris@0
|
94 || in_array('legacy', \PHPUnit_Util_Test::getGroups($class, $method), true)
|
Chris@0
|
95 ) {
|
Chris@0
|
96 $group = 'legacy';
|
Chris@0
|
97 } else {
|
Chris@0
|
98 $group = 'remaining';
|
Chris@0
|
99 }
|
Chris@0
|
100
|
Chris@0
|
101 if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $msg)) {
|
Chris@0
|
102 $e = new \Exception($msg);
|
Chris@0
|
103 $r = new \ReflectionProperty($e, 'trace');
|
Chris@0
|
104 $r->setAccessible(true);
|
Chris@0
|
105 $r->setValue($e, array_slice($trace, 1, $i));
|
Chris@0
|
106
|
Chris@0
|
107 echo "\n".ucfirst($group).' deprecation triggered by '.$class.'::'.$method.':';
|
Chris@0
|
108 echo "\n".$msg;
|
Chris@0
|
109 echo "\nStack trace:";
|
Chris@0
|
110 echo "\n".str_replace(' '.getcwd().DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString());
|
Chris@0
|
111 echo "\n";
|
Chris@0
|
112
|
Chris@0
|
113 exit(1);
|
Chris@0
|
114 }
|
Chris@0
|
115 if ('legacy' !== $group && DeprecationErrorHandler::MODE_WEAK !== $mode) {
|
Chris@0
|
116 $ref = &$deprecations[$group][$msg]['count'];
|
Chris@0
|
117 ++$ref;
|
Chris@0
|
118 $ref = &$deprecations[$group][$msg][$class.'::'.$method];
|
Chris@0
|
119 ++$ref;
|
Chris@0
|
120 }
|
Chris@0
|
121 } elseif (DeprecationErrorHandler::MODE_WEAK !== $mode) {
|
Chris@0
|
122 $ref = &$deprecations[$group][$msg]['count'];
|
Chris@0
|
123 ++$ref;
|
Chris@0
|
124 }
|
Chris@0
|
125 ++$deprecations[$group.'Count'];
|
Chris@0
|
126 };
|
Chris@0
|
127 $oldErrorHandler = set_error_handler($deprecationHandler);
|
Chris@0
|
128
|
Chris@0
|
129 if (null !== $oldErrorHandler) {
|
Chris@0
|
130 restore_error_handler();
|
Chris@0
|
131 if (array('PHPUnit_Util_ErrorHandler', 'handleError') === $oldErrorHandler) {
|
Chris@0
|
132 restore_error_handler();
|
Chris@0
|
133 self::register($mode);
|
Chris@0
|
134 }
|
Chris@0
|
135 } else {
|
Chris@0
|
136 self::$isRegistered = true;
|
Chris@0
|
137 if (self::hasColorSupport()) {
|
Chris@0
|
138 $colorize = function ($str, $red) {
|
Chris@0
|
139 $color = $red ? '41;37' : '43;30';
|
Chris@0
|
140
|
Chris@0
|
141 return "\x1B[{$color}m{$str}\x1B[0m";
|
Chris@0
|
142 };
|
Chris@0
|
143 } else {
|
Chris@0
|
144 $colorize = function ($str) { return $str; };
|
Chris@0
|
145 }
|
Chris@0
|
146 register_shutdown_function(function () use ($getMode, &$deprecations, $deprecationHandler, $colorize) {
|
Chris@0
|
147 $mode = $getMode();
|
Chris@0
|
148 if (isset($mode[0]) && '/' === $mode[0]) {
|
Chris@0
|
149 return;
|
Chris@0
|
150 }
|
Chris@0
|
151 $currErrorHandler = set_error_handler('var_dump');
|
Chris@0
|
152 restore_error_handler();
|
Chris@0
|
153
|
Chris@0
|
154 if (DeprecationErrorHandler::MODE_WEAK === $mode) {
|
Chris@0
|
155 $colorize = function ($str) { return $str; };
|
Chris@0
|
156 }
|
Chris@0
|
157 if ($currErrorHandler !== $deprecationHandler) {
|
Chris@0
|
158 echo "\n", $colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n";
|
Chris@0
|
159 }
|
Chris@0
|
160
|
Chris@0
|
161 $cmp = function ($a, $b) {
|
Chris@0
|
162 return $b['count'] - $a['count'];
|
Chris@0
|
163 };
|
Chris@0
|
164
|
Chris@0
|
165 foreach (array('unsilenced', 'remaining', 'legacy', 'other') as $group) {
|
Chris@0
|
166 if ($deprecations[$group.'Count']) {
|
Chris@0
|
167 echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), 'legacy' !== $group), "\n";
|
Chris@0
|
168
|
Chris@0
|
169 uasort($deprecations[$group], $cmp);
|
Chris@0
|
170
|
Chris@0
|
171 foreach ($deprecations[$group] as $msg => $notices) {
|
Chris@0
|
172 echo "\n", rtrim($msg, '.'), ': ', $notices['count'], "x\n";
|
Chris@0
|
173
|
Chris@0
|
174 arsort($notices);
|
Chris@0
|
175
|
Chris@0
|
176 foreach ($notices as $method => $count) {
|
Chris@0
|
177 if ('count' !== $method) {
|
Chris@0
|
178 echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n";
|
Chris@0
|
179 }
|
Chris@0
|
180 }
|
Chris@0
|
181 }
|
Chris@0
|
182 }
|
Chris@0
|
183 }
|
Chris@0
|
184 if (!empty($notices)) {
|
Chris@0
|
185 echo "\n";
|
Chris@0
|
186 }
|
Chris@0
|
187
|
Chris@0
|
188 if (DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) {
|
Chris@0
|
189 exit(1);
|
Chris@0
|
190 }
|
Chris@0
|
191 });
|
Chris@0
|
192 }
|
Chris@0
|
193 }
|
Chris@0
|
194
|
Chris@0
|
195 private static function hasColorSupport()
|
Chris@0
|
196 {
|
Chris@0
|
197 if ('\\' === DIRECTORY_SEPARATOR) {
|
Chris@0
|
198 return
|
Chris@0
|
199 '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD
|
Chris@0
|
200 || false !== getenv('ANSICON')
|
Chris@0
|
201 || 'ON' === getenv('ConEmuANSI')
|
Chris@0
|
202 || 'xterm' === getenv('TERM');
|
Chris@0
|
203 }
|
Chris@0
|
204
|
Chris@0
|
205 return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT);
|
Chris@0
|
206 }
|
Chris@0
|
207 }
|