11
11
12
12
namespace Symfony \Bridge \PhpUnit ;
13
13
14
+ use Symfony \Bridge \PhpUnit \DeprecationErrorHandler \Configuration ;
15
+ use Symfony \Bridge \PhpUnit \DeprecationErrorHandler \Deprecation ;
16
+
14
17
/**
15
18
* Catch deprecation notices and print a summary report at the end of the test suite.
16
19
*
17
20
* @author Nicolas Grekas <[email protected] >
18
21
*/
19
22
class DeprecationErrorHandler
20
23
{
21
- const MODE_WEAK = 'weak ' ;
24
+ /**
25
+ * @deprecated since Symfony 4.3, use max[self]=0 instead
26
+ */
22
27
const MODE_WEAK_VENDORS = 'weak_vendors ' ;
28
+
23
29
const MODE_DISABLED = 'disabled ' ;
30
+ const MODE_WEAK = 'max[total]=999999&verbose=0 ' ;
31
+ const MODE_STRICT = 'max[total]=0 ' ;
24
32
25
- private $ mode = false ;
26
- private $ resolvedMode = false ;
33
+ private $ mode ;
34
+ private $ configuration ;
27
35
private $ deprecations = [
28
36
'unsilencedCount ' => 0 ,
29
- 'remainingCount ' => 0 ,
37
+ 'remaining selfCount ' => 0 ,
30
38
'legacyCount ' => 0 ,
31
39
'otherCount ' => 0 ,
32
- 'remaining vendorCount ' => 0 ,
40
+ 'remaining directCount ' => 0 ,
41
+ 'remaining indirectCount ' => 0 ,
33
42
'unsilenced ' => [],
34
- 'remaining ' => [],
43
+ 'remaining self ' => [],
35
44
'legacy ' => [],
36
45
'other ' => [],
37
- 'remaining vendor ' => [],
46
+ 'remaining direct ' => [],
47
+ 'remaining indirect ' => [],
38
48
];
39
49
40
50
private static $ isRegistered = false ;
@@ -43,13 +53,16 @@ class DeprecationErrorHandler
43
53
/**
44
54
* Registers and configures the deprecation handler.
45
55
*
46
- * The following reporting modes are supported:
47
- * - use "weak" to hide the deprecation report but keep a global count;
48
- * - use "weak_vendors" to fail only on deprecations triggered in your own code;
49
- * - use "/some-regexp/" to stop the test suite whenever a deprecation
50
- * message matches the given regular expression;
51
- * - use a number to define the upper bound of allowed deprecations,
52
- * making the test suite fail whenever more notices are triggered.
56
+ * The mode is a query string with options:
57
+ * - "disabled" to disable the deprecation handler
58
+ * - "verbose" to enable/disable displaying the deprecation report
59
+ * - "max" to configure the number of deprecations to allow before exiting with a non-zero
60
+ * status code; it's an array with keys "total", "self", "direct" and "indirect"
61
+ *
62
+ * The default mode is "max[total]=0&verbose=1".
63
+ *
64
+ * The mode can alternatively be "/some-regexp/" to stop the test suite whenever
65
+ * a deprecation message matches the given regular expression.
53
66
*
54
67
* @param int|string|false $mode The reporting mode, defaults to not allowing any deprecations
55
68
*/
@@ -108,76 +121,41 @@ public static function collectDeprecations($outputFile)
108
121
*/
109
122
public function handleError ($ type , $ msg , $ file , $ line , $ context = [])
110
123
{
111
- if ((E_USER_DEPRECATED !== $ type && E_DEPRECATED !== $ type ) || self :: MODE_DISABLED === $ mode = $ this ->getMode ()) {
124
+ if ((E_USER_DEPRECATED !== $ type && E_DEPRECATED !== $ type ) || ! $ this ->getConfiguration ()-> isEnabled ()) {
112
125
$ ErrorHandler = self ::$ utilPrefix .'ErrorHandler ' ;
113
126
114
127
return $ ErrorHandler ::handleError ($ type , $ msg , $ file , $ line , $ context );
115
128
}
116
129
117
- $ trace = debug_backtrace ();
130
+ $ deprecation = new Deprecation ( $ msg , debug_backtrace (), $ file );
118
131
$ group = 'other ' ;
119
- $ isVendor = self ::MODE_WEAK_VENDORS === $ mode && self ::inVendors ($ file );
120
132
121
- $ i = \count ($ trace );
122
- while (1 < $ i && (!isset ($ trace [--$ i ]['class ' ]) || ('ReflectionMethod ' === $ trace [$ i ]['class ' ] || 0 === strpos ($ trace [$ i ]['class ' ], 'PHPUnit_ ' ) || 0 === strpos ($ trace [$ i ]['class ' ], 'PHPUnit \\' )))) {
123
- // No-op
124
- }
125
-
126
- if (isset ($ trace [$ i ]['object ' ]) || isset ($ trace [$ i ]['class ' ])) {
127
- if (isset ($ trace [$ i ]['class ' ]) && 0 === strpos ($ trace [$ i ]['class ' ], 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor ' )) {
128
- $ parsedMsg = unserialize ($ msg );
129
- $ msg = $ parsedMsg ['deprecation ' ];
130
- $ class = $ parsedMsg ['class ' ];
131
- $ method = $ parsedMsg ['method ' ];
132
- // If the deprecation has been triggered via
133
- // \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
134
- // then we need to use the serialized information to determine
135
- // if the error has been triggered from vendor code.
136
- $ isVendor = self ::MODE_WEAK_VENDORS === $ mode && isset ($ parsedMsg ['triggering_file ' ]) && self ::inVendors ($ parsedMsg ['triggering_file ' ]);
137
- } else {
138
- $ class = isset ($ trace [$ i ]['object ' ]) ? \get_class ($ trace [$ i ]['object ' ]) : $ trace [$ i ]['class ' ];
139
- $ method = $ trace [$ i ]['function ' ];
140
- }
141
-
142
- $ Test = self ::$ utilPrefix .'Test ' ;
133
+ if ($ deprecation ->originatesFromAnObject ()) {
134
+ $ class = $ deprecation ->originatingClass ();
135
+ $ method = $ deprecation ->originatingMethod ();
143
136
144
137
if (0 !== error_reporting ()) {
145
138
$ group = 'unsilenced ' ;
146
- } elseif (0 === strpos ($ method , 'testLegacy ' )
147
- || 0 === strpos ($ method , 'provideLegacy ' )
148
- || 0 === strpos ($ method , 'getLegacy ' )
149
- || strpos ($ class , '\Legacy ' )
150
- || \in_array ('legacy ' , $ Test ::getGroups ($ class , $ method ), true )
151
- ) {
139
+ } elseif ($ deprecation ->isLegacy (self ::$ utilPrefix )) {
152
140
$ group = 'legacy ' ;
153
- } elseif ($ isVendor ) {
154
- $ group = 'remaining vendor ' ;
141
+ } elseif (! $ deprecation -> isSelf () ) {
142
+ $ group = $ deprecation -> isIndirect () ? 'remaining indirect ' : ' remaining direct ' ;
155
143
} else {
156
- $ group = 'remaining ' ;
144
+ $ group = 'remaining self ' ;
157
145
}
158
146
159
- if (isset ($ mode [0 ]) && '/ ' === $ mode [0 ] && preg_match ($ mode , $ msg )) {
160
- $ e = new \Exception ($ msg );
161
- $ r = new \ReflectionProperty ($ e , 'trace ' );
162
- $ r ->setAccessible (true );
163
- $ r ->setValue ($ e , \array_slice ($ trace , 1 , $ i ));
164
-
165
- echo "\n" .ucfirst ($ group ).' deprecation triggered by ' .$ class .':: ' .$ method .': ' ;
166
- echo "\n" .$ msg ;
167
- echo "\nStack trace: " ;
168
- echo "\n" .str_replace (' ' .getcwd ().\DIRECTORY_SEPARATOR , ' ' , $ e ->getTraceAsString ());
169
- echo "\n" ;
147
+ if ($ this ->getConfiguration ()->shouldDisplayStackTrace ($ msg )) {
148
+ echo "\n" .ucfirst ($ group ).' ' .$ deprecation ->toString ();
170
149
171
150
exit (1 );
172
151
}
173
-
174
- if ('legacy ' !== $ group && self ::MODE_WEAK !== $ mode ) {
152
+ if ('legacy ' !== $ group ) {
175
153
$ ref = &$ this ->deprecations [$ group ][$ msg ]['count ' ];
176
154
++$ ref ;
177
155
$ ref = &$ this ->deprecations [$ group ][$ msg ][$ class .':: ' .$ method ];
178
156
++$ ref ;
179
157
}
180
- } elseif ( self :: MODE_WEAK !== $ mode ) {
158
+ } else {
181
159
$ ref = &$ this ->deprecations [$ group ][$ msg ]['count ' ];
182
160
++$ ref ;
183
161
}
@@ -190,124 +168,88 @@ public function handleError($type, $msg, $file, $line, $context = [])
190
168
*/
191
169
public function shutdown ()
192
170
{
193
- $ mode = $ this ->getMode ();
171
+ $ configuration = $ this ->getConfiguration ();
194
172
195
- if (isset ( $ mode [ 0 ]) && ' / ' === $ mode [ 0 ] ) {
173
+ if ($ configuration -> isInRegexMode () ) {
196
174
return ;
197
175
}
198
176
199
177
$ currErrorHandler = set_error_handler ('var_dump ' );
200
178
restore_error_handler ();
201
179
202
180
if ($ currErrorHandler !== [$ this , 'handleError ' ]) {
203
- echo "\n" , self ::colorize ('THE ERROR HANDLER HAS CHANGED! ' , true , $ mode ), "\n" ;
204
- }
205
-
206
- $ groups = ['unsilenced ' , 'remaining ' ];
207
-
208
- if (self ::MODE_WEAK_VENDORS === $ mode ) {
209
- $ groups [] = 'remaining vendor ' ;
181
+ echo "\n" , self ::colorize ('THE ERROR HANDLER HAS CHANGED! ' , true ), "\n" ;
210
182
}
211
183
212
- array_push ( $ groups , 'legacy ' , 'other ' ) ;
184
+ $ groups = [ ' unsilenced ' , 'remaining self ' , ' remaining direct ' , ' remaining indirect ' , ' legacy ' , 'other ' ] ;
213
185
214
- $ this ->displayDeprecations ($ groups , $ mode );
186
+ $ this ->displayDeprecations ($ groups , $ configuration );
215
187
216
188
// store failing status
217
- $ isFailing = self :: MODE_WEAK !== $ mode && $ mode < $ this -> deprecations [ ' unsilencedCount ' ] + $ this ->deprecations [ ' remainingCount ' ] + $ this -> deprecations [ ' otherCount ' ] ;
189
+ $ isFailing = ! $ configuration -> tolerates ( $ this ->deprecations ) ;
218
190
219
191
// reset deprecations array
220
192
foreach ($ this ->deprecations as $ group => $ arrayOrInt ) {
221
193
$ this ->deprecations [$ group ] = \is_int ($ arrayOrInt ) ? 0 : [];
222
194
}
223
195
224
- register_shutdown_function (function () use ($ isFailing , $ groups , $ mode ) {
196
+ register_shutdown_function (function () use ($ isFailing , $ groups , $ configuration ) {
225
197
foreach ($ this ->deprecations as $ group => $ arrayOrInt ) {
226
198
if (0 < (\is_int ($ arrayOrInt ) ? $ arrayOrInt : \count ($ arrayOrInt ))) {
227
199
echo "Shutdown-time deprecations: \n" ;
228
200
break ;
229
201
}
230
202
}
231
203
232
- $ this ->displayDeprecations ($ groups , $ mode );
204
+ $ this ->displayDeprecations ($ groups , $ configuration );
233
205
234
- if ($ isFailing || self :: MODE_WEAK !== $ mode && $ mode < $ this -> deprecations [ ' unsilencedCount ' ] + $ this ->deprecations [ ' remainingCount ' ] + $ this -> deprecations [ ' otherCount ' ] ) {
206
+ if ($ isFailing || ! $ configuration -> tolerates ( $ this ->deprecations ) ) {
235
207
exit (1 );
236
208
}
237
209
});
238
210
}
239
211
240
- private function getMode ()
212
+ private function getConfiguration ()
241
213
{
242
- if (false !== $ this ->resolvedMode ) {
243
- return $ this ->resolvedMode ;
214
+ if (null !== $ this ->configuration ) {
215
+ return $ this ->configuration ;
244
216
}
245
-
246
217
if (false === $ mode = $ this ->mode ) {
247
218
$ mode = getenv ('SYMFONY_DEPRECATIONS_HELPER ' );
248
219
}
249
-
250
- if (self ::MODE_DISABLED !== $ mode
251
- && self ::MODE_WEAK !== $ mode
252
- && self ::MODE_WEAK_VENDORS !== $ mode
253
- && (!isset ($ mode [0 ]) || '/ ' !== $ mode [0 ])
254
- ) {
255
- $ mode = preg_match ('/^[1-9][0-9]*$/ ' , $ mode ) ? (int ) $ mode : 0 ;
220
+ if ('strict ' === $ mode ) {
221
+ return $ this ->configuration = Configuration::inStrictMode ();
256
222
}
257
-
258
- return $ this ->resolvedMode = $ mode ;
259
- }
260
-
261
- /**
262
- * @param string $path
263
- *
264
- * @return bool
265
- */
266
- private static function inVendors ($ path )
267
- {
268
- /** @var string[] absolute paths to vendor directories */
269
- static $ vendors ;
270
-
271
- if (null === $ vendors ) {
272
- foreach (get_declared_classes () as $ class ) {
273
- if ('C ' !== $ class [0 ] || 0 !== strpos ($ class , 'ComposerAutoloaderInit ' )) {
274
- continue ;
275
- }
276
-
277
- $ r = new \ReflectionClass ($ class );
278
- $ v = \dirname (\dirname ($ r ->getFileName ()));
279
-
280
- if (file_exists ($ v .'/composer/installed.json ' )) {
281
- $ vendors [] = $ v ;
282
- }
283
- }
223
+ if (self ::MODE_DISABLED === $ mode ) {
224
+ return $ this ->configuration = Configuration::inDisabledMode ();
284
225
}
285
-
286
- $ realPath = realpath ($ path );
287
-
288
- if (false === $ realPath && '- ' !== $ path && 'Standard input code ' !== $ path ) {
289
- return true ;
226
+ if ('weak ' === $ mode ) {
227
+ return $ this ->configuration = Configuration::inWeakMode ();
228
+ }
229
+ if (self ::MODE_WEAK_VENDORS === $ mode ) {
230
+ echo sprintf ('Setting SYMFONY_DEPRECATIONS_HELPER to "%s" is deprecated in favor of "max[self]=0" ' , $ mode ).PHP_EOL ;
231
+ exit (1 );
232
+ }
233
+ if (isset ($ mode [0 ]) && '/ ' === $ mode [0 ]) {
234
+ return $ this ->configuration = Configuration::fromRegex ($ mode );
290
235
}
291
236
292
- foreach ($ vendors as $ vendor ) {
293
- if (0 === strpos ($ realPath , $ vendor ) && false !== strpbrk (substr ($ realPath , \strlen ($ vendor ), 1 ), '/ ' .\DIRECTORY_SEPARATOR )) {
294
- return true ;
295
- }
237
+ if (preg_match ('/^[1-9][0-9]*$/ ' , (string ) $ mode )) {
238
+ return $ this ->configuration = Configuration::fromNumber ($ mode );
296
239
}
297
240
298
- return false ;
241
+ return $ this -> configuration = Configuration:: fromUrlEncodedString (( string ) $ mode ) ;
299
242
}
300
243
301
244
/**
302
245
* @param string $str
303
246
* @param bool $red
304
- * @param mixed $mode
305
247
*
306
248
* @return string
307
249
*/
308
- private static function colorize ($ str , $ red, $ mode )
250
+ private static function colorize ($ str , $ red )
309
251
{
310
- if (!self ::hasColorSupport () || self :: MODE_WEAK === $ mode ) {
252
+ if (!self ::hasColorSupport ()) {
311
253
return $ str ;
312
254
}
313
255
@@ -317,36 +259,36 @@ private static function colorize($str, $red, $mode)
317
259
}
318
260
319
261
/**
320
- * @param string[] $groups
321
- * @param mixed $mode
262
+ * @param string[] $groups
263
+ * @param Configuration $configuration
322
264
*/
323
- private function displayDeprecations ($ groups , $ mode )
265
+ private function displayDeprecations ($ groups , $ configuration )
324
266
{
325
267
$ cmp = function ($ a , $ b ) {
326
268
return $ b ['count ' ] - $ a ['count ' ];
327
269
};
328
270
329
271
foreach ($ groups as $ group ) {
330
- if (!$ this ->deprecations [$ group .'Count ' ]) {
331
- continue ;
332
- }
272
+ if ($ this ->deprecations [$ group .'Count ' ]) {
273
+ echo "\n" , self ::colorize (
274
+ sprintf ('%s deprecation notices (%d) ' , ucfirst ($ group ), $ this ->deprecations [$ group .'Count ' ]),
275
+ 'legacy ' !== $ group && 'remaining indirect ' !== $ group
276
+ ), "\n" ;
333
277
334
- echo "\n" , self ::colorize (
335
- sprintf ('%s deprecation notices (%d) ' , ucfirst ($ group ), $ this ->deprecations [$ group .'Count ' ]),
336
- 'legacy ' !== $ group && 'remaining vendor ' !== $ group ,
337
- $ mode
338
- ), "\n" ;
339
-
340
- uasort ($ this ->deprecations [$ group ], $ cmp );
278
+ if (!$ configuration ->verboseOutput ()) {
279
+ continue ;
280
+ }
281
+ uasort ($ this ->deprecations [$ group ], $ cmp );
341
282
342
- foreach ($ this ->deprecations [$ group ] as $ msg => $ notices ) {
343
- echo "\n " , $ notices ['count ' ], 'x: ' , $ msg , "\n" ;
283
+ foreach ($ this ->deprecations [$ group ] as $ msg => $ notices ) {
284
+ echo "\n " , $ notices ['count ' ], 'x: ' , $ msg , "\n" ;
344
285
345
- arsort ($ notices );
286
+ arsort ($ notices );
346
287
347
- foreach ($ notices as $ method => $ count ) {
348
- if ('count ' !== $ method ) {
349
- echo ' ' , $ count , 'x in ' , preg_replace ('/(.*) \\\\(.*?::.*?)$/ ' , '$2 from $1 ' , $ method ), "\n" ;
288
+ foreach ($ notices as $ method => $ count ) {
289
+ if ('count ' !== $ method ) {
290
+ echo ' ' , $ count , 'x in ' , preg_replace ('/(.*) \\\\(.*?::.*?)$/ ' , '$2 from $1 ' , $ method ), "\n" ;
291
+ }
350
292
}
351
293
}
352
294
}
0 commit comments