@@ -20,6 +20,7 @@ class DeprecationErrorHandler
20
20
{
21
21
const MODE_WEAK = 'weak ' ;
22
22
const MODE_WEAK_VENDORS = 'weak_vendors ' ;
23
+ const MODE_WEAK_LAGGING_VENDORS = 'weak_lagging_vendors ' ;
23
24
const MODE_DISABLED = 'disabled ' ;
24
25
25
26
private static $ isRegistered = false ;
@@ -30,6 +31,8 @@ class DeprecationErrorHandler
30
31
* The following reporting modes are supported:
31
32
* - use "weak" to hide the deprecation report but keep a global count;
32
33
* - use "weak_vendors" to act as "weak" but only for vendors;
34
+ * - use "weak_lagging_vendors" to act as "weak" but only for vendors that
35
+ * failed to keep up with their upstream dependencies deprecations;
33
36
* - use "/some-regexp/" to stop the test suite whenever a deprecation
34
37
* message matches the given regular expression;
35
38
* - use a number to define the upper bound of allowed deprecations,
@@ -54,7 +57,11 @@ public static function register($mode = 0)
54
57
if (false === $ mode ) {
55
58
$ mode = getenv ('SYMFONY_DEPRECATIONS_HELPER ' );
56
59
}
57
- if (DeprecationErrorHandler::MODE_WEAK !== $ mode && DeprecationErrorHandler::MODE_WEAK_VENDORS !== $ mode && (!isset ($ mode [0 ]) || '/ ' !== $ mode [0 ])) {
60
+ if (!in_array ($ mode , array (
61
+ DeprecationErrorHandler::MODE_WEAK ,
62
+ DeprecationErrorHandler::MODE_WEAK_VENDORS ,
63
+ DeprecationErrorHandler::MODE_WEAK_LAGGING_VENDORS ,
64
+ ), true ) && (!isset ($ mode [0 ]) || '/ ' !== $ mode [0 ])) {
58
65
$ mode = preg_match ('/^[1-9][0-9]*$/ ' , $ mode ) ? (int ) $ mode : 0 ;
59
66
}
60
67
@@ -66,11 +73,13 @@ public static function register($mode = 0)
66
73
'remainingCount ' => 0 ,
67
74
'legacyCount ' => 0 ,
68
75
'otherCount ' => 0 ,
76
+ 'lagging vendorCount ' => 0 ,
69
77
'remaining vendorCount ' => 0 ,
70
78
'unsilenced ' => array (),
71
79
'remaining ' => array (),
72
80
'legacy ' => array (),
73
81
'other ' => array (),
82
+ 'lagging vendor ' => array (),
74
83
'remaining vendor ' => array (),
75
84
);
76
85
$ deprecationHandler = function ($ type , $ msg , $ file , $ line , $ context = array ()) use (&$ deprecations , $ getMode , $ UtilPrefix ) {
@@ -84,8 +93,9 @@ public static function register($mode = 0)
84
93
$ trace = debug_backtrace (true );
85
94
$ group = 'other ' ;
86
95
$ isVendor = false ;
87
- $ isWeak = DeprecationErrorHandler::MODE_WEAK === $ mode || (DeprecationErrorHandler::MODE_WEAK_VENDORS === $ mode && $ isVendor = self ::inVendors ($ file ));
88
-
96
+ $ isWeak = DeprecationErrorHandler::MODE_WEAK === $ mode ||
97
+ (DeprecationErrorHandler::MODE_WEAK_VENDORS === $ mode && $ isVendor = self ::inVendors ($ file )) ||
98
+ (DeprecationErrorHandler::MODE_WEAK_LAGGING_VENDORS === $ mode && $ isLaggingVendor = self ::isLaggingVendor ($ trace ));
89
99
$ i = count ($ trace );
90
100
while (1 < $ i && (!isset ($ trace [--$ i ]['class ' ]) || ('ReflectionMethod ' === $ trace [$ i ]['class ' ] || 0 === strpos ($ trace [$ i ]['class ' ], 'PHPUnit_ ' ) || 0 === strpos ($ trace [$ i ]['class ' ], 'PHPUnit \\' )))) {
91
101
// No-op
@@ -101,7 +111,9 @@ public static function register($mode = 0)
101
111
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
102
112
// then we need to use the serialized information to determine
103
113
// if the error has been triggered from vendor code.
104
- $ isWeak = DeprecationErrorHandler::MODE_WEAK === $ mode || (DeprecationErrorHandler::MODE_WEAK_VENDORS === $ mode && $ isVendor = isset ($ parsedMsg ['triggering_file ' ]) && self ::inVendors ($ parsedMsg ['triggering_file ' ]));
114
+ $ isWeak = DeprecationErrorHandler::MODE_WEAK === $ mode ||
115
+ (DeprecationErrorHandler::MODE_WEAK_VENDORS === $ mode && $ isVendor = isset ($ parsedMsg ['triggering_file ' ]) && self ::inVendors ($ parsedMsg ['triggering_file ' ])) ||
116
+ (DeprecationErrorHandler::MODE_WEAK_LAGGING_VENDORS === $ mode ); // not enough information to make the right call, so let's be lenient
105
117
} else {
106
118
$ class = isset ($ trace [$ i ]['object ' ]) ? get_class ($ trace [$ i ]['object ' ]) : $ trace [$ i ]['class ' ];
107
119
$ method = $ trace [$ i ]['function ' ];
@@ -118,6 +130,8 @@ public static function register($mode = 0)
118
130
|| in_array ('legacy ' , $ Test ::getGroups ($ class , $ method ), true )
119
131
) {
120
132
$ group = 'legacy ' ;
133
+ } elseif (DeprecationErrorHandler::MODE_WEAK_LAGGING_VENDORS === $ mode && $ isLaggingVendor ) {
134
+ $ group = 'lagging vendor ' ;
121
135
} elseif (DeprecationErrorHandler::MODE_WEAK_VENDORS === $ mode && $ isVendor ) {
122
136
$ group = 'remaining vendor ' ;
123
137
} else {
@@ -192,13 +206,16 @@ public static function register($mode = 0)
192
206
if (DeprecationErrorHandler::MODE_WEAK_VENDORS === $ mode ) {
193
207
$ groups [] = 'remaining vendor ' ;
194
208
}
209
+ if (DeprecationErrorHandler::MODE_WEAK_LAGGING_VENDORS === $ mode ) {
210
+ $ groups [] = 'lagging vendor ' ;
211
+ }
195
212
array_push ($ groups , 'legacy ' , 'other ' );
196
213
197
214
foreach ($ groups as $ group ) {
198
215
if ($ deprecations [$ group .'Count ' ]) {
199
216
echo "\n" , $ colorize (
200
217
sprintf ('%s deprecation notices (%d) ' , ucfirst ($ group ), $ deprecations [$ group .'Count ' ]),
201
- ' legacy ' !== $ group && 'remaining vendor ' !== $ group
218
+ ! in_array ( $ group, array ( ' legacy ' , 'remaining vendor ' , ' lagging vendor ' ), true )
202
219
), "\n" ;
203
220
204
221
uasort ($ deprecations [$ group ], $ cmp );
@@ -227,11 +244,58 @@ public static function register($mode = 0)
227
244
}
228
245
}
229
246
230
- private static function inVendors (string $ path ): bool
247
+ private static function isLaggingVendor (array $ trace ): bool
248
+ {
249
+ $ erroringFile = $ erroringPackage = null ;
250
+ foreach ($ trace as $ line ) {
251
+ if (!isset ($ line ['file ' ])) {
252
+ continue ;
253
+ }
254
+ $ file = $ line ['file ' ];
255
+ if ('- ' === $ file ) {
256
+ continue ;
257
+ }
258
+ if (!self ::inVendors ($ file )) {
259
+ return false ;
260
+ }
261
+ if (null !== $ erroringFile && null !== $ erroringPackage ) {
262
+ if (self ::getPackage ($ file ) !== $ erroringPackage ) {
263
+ return true ;
264
+ }
265
+ } else {
266
+ $ erroringFile = $ file ;
267
+ $ erroringPackage = self ::getPackage ($ file );
268
+ }
269
+ }
270
+
271
+ return false ;
272
+ }
273
+
274
+ /**
275
+ * inVendors() should always be called prior to calling this method.
276
+ */
277
+ private static function getPackage (string $ path ): string
278
+ {
279
+ $ path = realpath ($ path ) ?: $ path ;
280
+ foreach (self ::getVendors () as $ vendorRoot ) {
281
+ if (0 === strpos ($ path , $ vendorRoot )) {
282
+ $ relativePath = substr ($ path , strlen ($ vendorRoot ) + 1 );
283
+ $ vendor = strstr ($ relativePath , DIRECTORY_SEPARATOR , true );
284
+
285
+ return $ vendor .'/ ' .strstr (substr ($ relativePath , strlen ($ vendor ) + 1 ), DIRECTORY_SEPARATOR , true );
286
+ }
287
+ }
288
+
289
+ throw new \RuntimeException ('No vendors found ' );
290
+ }
291
+
292
+ private static function getVendors (): array
231
293
{
232
294
/** @var string[] absolute paths to vendor directories */
233
295
static $ vendors ;
296
+
234
297
if (null === $ vendors ) {
298
+ $ vendors = array ();
235
299
foreach (get_declared_classes () as $ class ) {
236
300
if ('C ' === $ class [0 ] && 0 === strpos ($ class , 'ComposerAutoloaderInit ' )) {
237
301
$ r = new \ReflectionClass ($ class );
@@ -242,11 +306,17 @@ private static function inVendors(string $path): bool
242
306
}
243
307
}
244
308
}
309
+
310
+ return $ vendors ;
311
+ }
312
+
313
+ private static function inVendors (string $ path ): bool
314
+ {
245
315
$ realPath = realpath ($ path );
246
316
if (false === $ realPath && '- ' !== $ path && 'Standard input code ' !== $ path ) {
247
317
return true ;
248
318
}
249
- foreach ($ vendors as $ vendor ) {
319
+ foreach (self :: getVendors () as $ vendor ) {
250
320
if (0 === strpos ($ realPath , $ vendor ) && false !== strpbrk (substr ($ realPath , strlen ($ vendor ), 1 ), '/ ' .DIRECTORY_SEPARATOR )) {
251
321
return true ;
252
322
}
0 commit comments