• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCompatibility / PHPCompatibility / 18730626143

22 Oct 2025 09:37PM UTC coverage: 98.401%. Remained the same
18730626143

push

github

web-flow
Merge pull request #1937 from PHPCompatibility/php-83/newclasses-detect-new-datetime-errors-and-exceptions

PHP 8.3 | NewClasses: detect new DateTime Error + Exception classes (RFC)

9228 of 9378 relevant lines covered (98.4%)

37.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.75
/PHPCompatibility/Sniffs/Classes/NewClassesSniff.php
1
<?php
2
/**
3
 * PHPCompatibility, an external standard for PHP_CodeSniffer.
4
 *
5
 * @package   PHPCompatibility
6
 * @copyright 2012-2020 PHPCompatibility Contributors
7
 * @license   https://opensource.org/licenses/LGPL-3.0 LGPL3
8
 * @link      https://github.com/PHPCompatibility/PHPCompatibility
9
 */
10

11
namespace PHPCompatibility\Sniffs\Classes;
12

13
use PHPCompatibility\Helpers\ComplexVersionNewFeatureTrait;
14
use PHPCompatibility\Helpers\ResolveHelper;
15
use PHPCompatibility\Helpers\ScannedCode;
16
use PHPCompatibility\Sniff;
17
use PHP_CodeSniffer\Files\File;
18
use PHPCSUtils\Exceptions\ValueError;
19
use PHPCSUtils\Tokens\Collections;
20
use PHPCSUtils\Utils\Constants;
21
use PHPCSUtils\Utils\ControlStructures;
22
use PHPCSUtils\Utils\FunctionDeclarations;
23
use PHPCSUtils\Utils\ObjectDeclarations;
24
use PHPCSUtils\Utils\Parentheses;
25
use PHPCSUtils\Utils\TypeString;
26
use PHPCSUtils\Utils\UseStatements;
27
use PHPCSUtils\Utils\Variables;
28

29
/**
30
 * Detect use of new PHP native classes.
31
 *
32
 * The sniff analyses the following constructs to find usage of new classes:
33
 * - Class instantiation using the `new` keyword.
34
 * - (Anonymous) Class declarations to detect new classes being extended by userland classes.
35
 * - Static use of class properties, constants or functions using the double colon.
36
 * - Function/closure declarations to detect new classes used as parameter type declarations.
37
 * - Function/closure declarations to detect new classes used as return type declarations.
38
 * - Property declarations to detect new classes used as property type declarations.
39
 * - Try/catch statements to detect new exception classes being caught.
40
 *
41
 * PHP version All
42
 *
43
 * @since 5.5
44
 * @since 5.6    Now extends the base `Sniff` class.
45
 * @since 7.1.0  Now extends the `AbstractNewFeatureSniff` class.
46
 * @since 10.0.0 - Now extends the base `Sniff` class and uses the `ComplexVersionNewFeatureTrait`.
47
 *               - This class is now `final`.
48
 */
49
final class NewClassesSniff extends Sniff
50
{
51
    use ComplexVersionNewFeatureTrait;
52

53
    /**
54
     * A list of new classes, not present in older versions.
55
     *
56
     * The array lists : version number with false (not present) or true (present).
57
     * If's sufficient to list the first version where the class appears.
58
     *
59
     * @since 5.5
60
     *
61
     * @var array<string, array<string, bool|string>>
62
     */
63
    protected $newClasses = [
64
        'ArrayObject' => [
65
            '4.4'       => false,
66
            '5.0'       => true,
67
            'extension' => 'spl',
68
        ],
69
        'ArrayIterator' => [
70
            '4.4'       => false,
71
            '5.0'       => true,
72
            'extension' => 'spl',
73
        ],
74
        'CachingIterator' => [
75
            '4.4'       => false,
76
            '5.0'       => true,
77
            'extension' => 'spl',
78
        ],
79
        'DirectoryIterator' => [
80
            '4.4'       => false,
81
            '5.0'       => true,
82
            'extension' => 'spl',
83
        ],
84
        'RecursiveDirectoryIterator' => [
85
            '4.4'       => false,
86
            '5.0'       => true,
87
            'extension' => 'spl',
88
        ],
89
        'RecursiveIteratorIterator' => [
90
            '4.4'       => false,
91
            '5.0'       => true,
92
            'extension' => 'spl',
93
        ],
94
        'php_user_filter' => [
95
            '4.4' => false,
96
            '5.0' => true,
97
        ],
98
        'tidy' => [
99
            '4.4'       => false,
100
            '5.0'       => true,
101
            'extension' => 'tidy',
102
        ],
103
        'tidyNode' => [
104
            '4.4'       => false,
105
            '5.0'       => true,
106
            'extension' => 'tidy',
107
        ],
108
        'Reflection' => [
109
            '4.4'       => false,
110
            '5.0'       => true,
111
            'extension' => 'reflection',
112
        ],
113
        'ReflectionClass' => [
114
            '4.4'       => false,
115
            '5.0'       => true,
116
            'extension' => 'reflection',
117
        ],
118
        'ReflectionExtension' => [
119
            '4.4'       => false,
120
            '5.0'       => true,
121
            'extension' => 'reflection',
122
        ],
123
        'ReflectionFunction' => [
124
            '4.4'       => false,
125
            '5.0'       => true,
126
            'extension' => 'reflection',
127
        ],
128
        'ReflectionMethod' => [
129
            '4.4'       => false,
130
            '5.0'       => true,
131
            'extension' => 'reflection',
132
        ],
133
        'ReflectionObject' => [
134
            '4.4'       => false,
135
            '5.0'       => true,
136
            'extension' => 'reflection',
137
        ],
138
        'ReflectionParameter' => [
139
            '4.4'       => false,
140
            '5.0'       => true,
141
            'extension' => 'reflection',
142
        ],
143
        'ReflectionProperty' => [
144
            '4.4'       => false,
145
            '5.0'       => true,
146
            'extension' => 'reflection',
147
        ],
148
        'SoapClient' => [
149
            '4.4'       => false,
150
            '5.0'       => true,
151
            'extension' => 'soap',
152
        ],
153
        'SoapServer' => [
154
            '4.4'       => false,
155
            '5.0'       => true,
156
            'extension' => 'soap',
157
        ],
158
        'SoapHeader' => [
159
            '4.4'       => false,
160
            '5.0'       => true,
161
            'extension' => 'soap',
162
        ],
163
        'SoapParam' => [
164
            '4.4'       => false,
165
            '5.0'       => true,
166
            'extension' => 'soap',
167
        ],
168
        'SoapVar' => [
169
            '4.4'       => false,
170
            '5.0'       => true,
171
            'extension' => 'soap',
172
        ],
173
        'COMPersistHelper' => [
174
            '4.4' => false,
175
            '5.0' => true,
176
        ],
177
        'DOMAttr' => [
178
            '4.4'       => false,
179
            '5.0'       => true,
180
            'extension' => 'dom',
181
        ],
182
        'DOMCdataSection' => [
183
            '4.4'       => false,
184
            '5.0'       => true,
185
            'extension' => 'dom',
186
        ],
187
        'DOMCharacterData' => [
188
            '4.4'       => false,
189
            '5.0'       => true,
190
            'extension' => 'dom',
191
        ],
192
        'DOMComment' => [
193
            '4.4'       => false,
194
            '5.0'       => true,
195
            'extension' => 'dom',
196
        ],
197
        'DOMDocument' => [
198
            '4.4'       => false,
199
            '5.0'       => true,
200
            'extension' => 'dom',
201
        ],
202
        'DOMDocumentFragment' => [
203
            '4.4'       => false,
204
            '5.0'       => true,
205
            'extension' => 'dom',
206
        ],
207
        'DOMDocumentType' => [
208
            '4.4'       => false,
209
            '5.0'       => true,
210
            'extension' => 'dom',
211
        ],
212
        'DOMElement' => [
213
            '4.4'       => false,
214
            '5.0'       => true,
215
            'extension' => 'dom',
216
        ],
217
        'DOMEntity' => [
218
            '4.4'       => false,
219
            '5.0'       => true,
220
            'extension' => 'dom',
221
        ],
222
        'DOMEntityReference' => [
223
            '4.4'       => false,
224
            '5.0'       => true,
225
            'extension' => 'dom',
226
        ],
227
        'DOMImplementation' => [
228
            '4.4'       => false,
229
            '5.0'       => true,
230
            'extension' => 'dom',
231
        ],
232
        'DOMNamedNodeMap' => [
233
            '4.4'       => false,
234
            '5.0'       => true,
235
            'extension' => 'dom',
236
        ],
237
        'DOMNode' => [
238
            '4.4'       => false,
239
            '5.0'       => true,
240
            'extension' => 'dom',
241
        ],
242
        'DOMNodeList' => [
243
            '4.4'       => false,
244
            '5.0'       => true,
245
            'extension' => 'dom',
246
        ],
247
        'DOMNotation' => [
248
            '4.4'       => false,
249
            '5.0'       => true,
250
            'extension' => 'dom',
251
        ],
252
        'DOMProcessingInstruction' => [
253
            '4.4'       => false,
254
            '5.0'       => true,
255
            'extension' => 'dom',
256
        ],
257
        'DOMText' => [
258
            '4.4'       => false,
259
            '5.0'       => true,
260
            'extension' => 'dom',
261
        ],
262
        'DOMXPath' => [
263
            '4.4'       => false,
264
            '5.0'       => true,
265
            'extension' => 'dom',
266
        ],
267
        'SimpleXMLElement' => [
268
            '4.4'       => false,
269
            '5.0'       => true,
270
            'extension' => 'simplexml',
271
        ],
272
        'XSLTProcessor' => [
273
            '4.4'       => false,
274
            '5.0'       => true,
275
            'extension' => 'xsl',
276
        ],
277
        'SQLiteDatabase' => [
278
            '4.4'       => false,
279
            '5.0'       => true,
280
            'extension' => 'sqlite',
281
        ],
282
        'SQLiteResult' => [
283
            '4.4'       => false,
284
            '5.0'       => true,
285
            'extension' => 'sqlite',
286
        ],
287
        'SQLiteUnbuffered' => [
288
            '4.4'       => false,
289
            '5.0'       => true,
290
            'extension' => 'sqlite',
291
        ],
292
        'mysqli' => [
293
            '4.4'       => false,
294
            '5.0'       => true,
295
            'extension' => 'mysqli',
296
        ],
297
        'mysqli_stmt' => [
298
            '4.4'       => false,
299
            '5.0'       => true,
300
            'extension' => 'mysqli',
301
        ],
302
        'mysqli_result' => [
303
            '4.4'       => false,
304
            '5.0'       => true,
305
            'extension' => 'mysqli',
306
        ],
307
        'mysqli_driver' => [
308
            '4.4'       => false,
309
            '5.0'       => true,
310
            'extension' => 'mysqli',
311
        ],
312
        'mysqli_warning' => [
313
            '4.4'       => false,
314
            '5.0'       => true,
315
            'extension' => 'mysqli',
316
        ],
317
        /*
318
        See: https://bugs.php.net/bug.php?id=79625
319
        'OCI-Collection' => [
320
            '4.4' => false,
321
            '5.0' => true,
322
        ],
323
        'OCI-Lob' => [
324
            '4.4' => false,
325
            '5.0' => true,
326
        ],
327
        */
328

329
        'libXMLError' => [
330
            '5.0'       => false,
331
            '5.1'       => true,
332
            'extension' => 'libxml',
333
        ],
334
        'PDO' => [
335
            '5.0'       => false,
336
            '5.1'       => true,
337
            'extension' => 'pdo',
338
        ],
339
        'PDOStatement' => [
340
            '5.0'       => false,
341
            '5.1'       => true,
342
            'extension' => 'pdo',
343
        ],
344
        'AppendIterator' => [
345
            '5.0'       => false,
346
            '5.1'       => true,
347
            'extension' => 'spl',
348
        ],
349
        'EmptyIterator' => [
350
            '5.0'       => false,
351
            '5.1'       => true,
352
            'extension' => 'spl',
353
        ],
354
        'FilterIterator' => [
355
            '5.0'       => false,
356
            '5.1'       => true,
357
            'extension' => 'spl',
358
        ],
359
        'InfiniteIterator' => [
360
            '5.0'       => false,
361
            '5.1'       => true,
362
            'extension' => 'spl',
363
        ],
364
        'IteratorIterator' => [
365
            '5.0'       => false,
366
            '5.1'       => true,
367
            'extension' => 'spl',
368
        ],
369
        'LimitIterator' => [
370
            '5.0'       => false,
371
            '5.1'       => true,
372
            'extension' => 'spl',
373
        ],
374
        'NoRewindIterator' => [
375
            '5.0'       => false,
376
            '5.1'       => true,
377
            'extension' => 'spl',
378
        ],
379
        'ParentIterator' => [
380
            '5.0'       => false,
381
            '5.1'       => true,
382
            'extension' => 'spl',
383
        ],
384
        'RecursiveArrayIterator' => [
385
            '5.0'       => false,
386
            '5.1'       => true,
387
            'extension' => 'spl',
388
        ],
389
        'RecursiveCachingIterator' => [
390
            '5.0'       => false,
391
            '5.1'       => true,
392
            'extension' => 'spl',
393
        ],
394
        'RecursiveFilterIterator' => [
395
            '5.0'       => false,
396
            '5.1'       => true,
397
            'extension' => 'spl',
398
        ],
399
        'SimpleXMLIterator' => [
400
            '5.0'       => false,
401
            '5.1'       => true,
402
            'extension' => 'simplexml',
403
        ],
404
        'SplFileObject' => [
405
            '5.0'       => false,
406
            '5.1'       => true,
407
            'extension' => 'spl',
408
        ],
409
        'SplObjectStorage' => [
410
            '5.0'       => false,
411
            '5.1'       => true,
412
            'extension' => 'spl',
413
        ],
414
        'XMLReader' => [
415
            '5.0'       => false,
416
            '5.1'       => true,
417
            'extension' => 'xmlreader',
418
        ],
419

420
        'SplFileInfo' => [
421
            '5.1.1'     => false,
422
            '5.1.2'     => true,
423
            'extension' => 'spl',
424
        ],
425
        'SplTempFileObject' => [
426
            '5.1.1'     => false,
427
            '5.1.2'     => true,
428
            'extension' => 'spl',
429
        ],
430
        'XMLWriter' => [
431
            '5.1.1'     => false,
432
            '5.1.2'     => true,
433
            'extension' => 'xmlwriter',
434
        ],
435

436
        'DateTime' => [
437
            '5.1'       => false,
438
            '5.2'       => true,
439
            'extension' => 'datetime',
440
        ],
441
        'DateTimeZone' => [
442
            '5.1'       => false,
443
            '5.2'       => true,
444
            'extension' => 'datetime',
445
        ],
446
        'RegexIterator' => [
447
            '5.1'       => false,
448
            '5.2'       => true,
449
            'extension' => 'spl',
450
        ],
451
        'RecursiveRegexIterator' => [
452
            '5.1'       => false,
453
            '5.2'       => true,
454
            'extension' => 'spl',
455
        ],
456
        'ReflectionFunctionAbstract' => [
457
            '5.1'       => false,
458
            '5.2'       => true,
459
            'extension' => 'reflection',
460
        ],
461
        'ZipArchive' => [
462
            '5.1'       => false,
463
            '5.2'       => true,
464
            'extension' => 'zip',
465
        ],
466

467
        'Closure' => [
468
            '5.2' => false,
469
            '5.3' => true,
470
        ],
471
        'DateInterval' => [
472
            '5.2'       => false,
473
            '5.3'       => true,
474
            'extension' => 'datetime',
475
        ],
476
        'DatePeriod' => [
477
            '5.2'       => false,
478
            '5.3'       => true,
479
            'extension' => 'datetime',
480
        ],
481
        'finfo' => [
482
            '5.2'       => false,
483
            '5.3'       => true,
484
            'extension' => 'fileinfo',
485
        ],
486
        'Collator' => [
487
            '5.2'       => false,
488
            '5.3'       => true,
489
            'extension' => 'intl',
490
        ],
491
        'NumberFormatter' => [
492
            '5.2'       => false,
493
            '5.3'       => true,
494
            'extension' => 'intl',
495
        ],
496
        'Locale' => [
497
            '5.2'       => false,
498
            '5.3'       => true,
499
            'extension' => 'intl',
500
        ],
501
        'Normalizer' => [
502
            '5.2'       => false,
503
            '5.3'       => true,
504
            'extension' => 'intl',
505
        ],
506
        'MessageFormatter' => [
507
            '5.2'       => false,
508
            '5.3'       => true,
509
            'extension' => 'intl',
510
        ],
511
        'IntlDateFormatter' => [
512
            '5.2'       => false,
513
            '5.3'       => true,
514
            'extension' => 'intl',
515
        ],
516
        'Phar' => [
517
            '5.2'       => false,
518
            '5.3'       => true,
519
            'extension' => 'phar',
520
        ],
521
        'PharData' => [
522
            '5.2'       => false,
523
            '5.3'       => true,
524
            'extension' => 'phar',
525
        ],
526
        'PharFileInfo' => [
527
            '5.2'       => false,
528
            '5.3'       => true,
529
            'extension' => 'phar',
530
        ],
531
        'FilesystemIterator' => [
532
            '5.2'       => false,
533
            '5.3'       => true,
534
            'extension' => 'spl',
535
        ],
536
        'GlobIterator' => [
537
            '5.2'       => false,
538
            '5.3'       => true,
539
            'extension' => 'spl',
540
        ],
541
        'MultipleIterator' => [
542
            '5.2'       => false,
543
            '5.3'       => true,
544
            'extension' => 'spl',
545
        ],
546
        'RecursiveTreeIterator' => [
547
            '5.2'       => false,
548
            '5.3'       => true,
549
            'extension' => 'spl',
550
        ],
551
        'SplDoublyLinkedList' => [
552
            '5.2'       => false,
553
            '5.3'       => true,
554
            'extension' => 'spl',
555
        ],
556
        'SplFixedArray' => [
557
            '5.2'       => false,
558
            '5.3'       => true,
559
            'extension' => 'spl',
560
        ],
561
        'SplHeap' => [
562
            '5.2'       => false,
563
            '5.3'       => true,
564
            'extension' => 'spl',
565
        ],
566
        'SplMaxHeap' => [
567
            '5.2'       => false,
568
            '5.3'       => true,
569
            'extension' => 'spl',
570
        ],
571
        'SplMinHeap' => [
572
            '5.2'       => false,
573
            '5.3'       => true,
574
            'extension' => 'spl',
575
        ],
576
        'SplPriorityQueue' => [
577
            '5.2'       => false,
578
            '5.3'       => true,
579
            'extension' => 'spl',
580
        ],
581
        'SplQueue' => [
582
            '5.2'       => false,
583
            '5.3'       => true,
584
            'extension' => 'spl',
585
        ],
586
        'SplStack' => [
587
            '5.2'       => false,
588
            '5.3'       => true,
589
            'extension' => 'spl',
590
        ],
591
        'SQLite3' => [
592
            '5.2'       => false,
593
            '5.3'       => true,
594
            'extension' => 'sqlite3',
595
        ],
596
        'SQLite3Stmt' => [
597
            '5.2'       => false,
598
            '5.3'       => true,
599
            'extension' => 'sqlite3',
600
        ],
601
        'SQLite3Result' => [
602
            '5.2'       => false,
603
            '5.3'       => true,
604
            'extension' => 'sqlite3',
605
        ],
606

607
        'ResourceBundle' => [
608
            '5.3.1'     => false,
609
            '5.3.2'     => true,
610
            'extension' => 'intl',
611
        ],
612

613
        'CallbackFilterIterator' => [
614
            '5.3'       => false,
615
            '5.4'       => true,
616
            'extension' => 'spl',
617
        ],
618
        'RecursiveCallbackFilterIterator' => [
619
            '5.3'       => false,
620
            '5.4'       => true,
621
            'extension' => 'spl',
622
        ],
623
        'ReflectionZendExtension' => [
624
            '5.3'       => false,
625
            '5.4'       => true,
626
            'extension' => 'reflection',
627
        ],
628
        'SessionHandler' => [
629
            '5.3' => false,
630
            '5.4' => true,
631
        ],
632
        'SNMP' => [
633
            '5.3'       => false,
634
            '5.4'       => true,
635
            'extension' => 'snmp',
636
        ],
637
        'Transliterator' => [
638
            '5.3'       => false,
639
            '5.4'       => true,
640
            'extension' => 'intl',
641
        ],
642
        'Spoofchecker' => [
643
            '5.3'       => false,
644
            '5.4'       => true,
645
            'extension' => 'intl',
646
        ],
647

648
        'Generator' => [
649
            '5.4' => false,
650
            '5.5' => true,
651
        ],
652
        'CURLFile' => [
653
            '5.4'       => false,
654
            '5.5'       => true,
655
            'extension' => 'curl',
656
        ],
657
        'DateTimeImmutable' => [
658
            '5.4'       => false,
659
            '5.5'       => true,
660
            'extension' => 'datetime',
661
        ],
662
        'IntlCalendar' => [
663
            '5.4'       => false,
664
            '5.5'       => true,
665
            'extension' => 'intl',
666
        ],
667
        'IntlGregorianCalendar' => [
668
            '5.4'       => false,
669
            '5.5'       => true,
670
            'extension' => 'intl',
671
        ],
672
        'IntlTimeZone' => [
673
            '5.4'       => false,
674
            '5.5'       => true,
675
            'extension' => 'intl',
676
        ],
677
        'IntlBreakIterator' => [
678
            '5.4'       => false,
679
            '5.5'       => true,
680
            'extension' => 'intl',
681
        ],
682
        'IntlRuleBasedBreakIterator' => [
683
            '5.4'       => false,
684
            '5.5'       => true,
685
            'extension' => 'intl',
686
        ],
687
        'IntlCodePointBreakIterator' => [
688
            '5.4'       => false,
689
            '5.5'       => true,
690
            'extension' => 'intl',
691
        ],
692
        'IntlPartsIterator' => [
693
            '5.4'       => false,
694
            '5.5'       => true,
695
            'extension' => 'intl',
696
        ],
697
        'IntlIterator' => [
698
            '5.4'       => false,
699
            '5.5'       => true,
700
            'extension' => 'intl',
701
        ],
702
        'UConverter' => [
703
            '5.4'       => false,
704
            '5.5'       => true,
705
            'extension' => 'intl',
706
        ],
707

708
        'GMP' => [
709
            '5.5'       => false,
710
            '5.6'       => true,
711
            'extension' => 'gmp',
712
        ],
713

714
        'IntlChar' => [
715
            '5.6'       => false,
716
            '7.0'       => true,
717
            'extension' => 'intl',
718
        ],
719
        'ReflectionType' => [
720
            '5.6'       => false,
721
            '7.0'       => true,
722
            'extension' => 'reflection',
723
        ],
724
        'ReflectionGenerator' => [
725
            '5.6'       => false,
726
            '7.0'       => true,
727
            'extension' => 'reflection',
728
        ],
729

730
        'ReflectionClassConstant' => [
731
            '7.0'       => false,
732
            '7.1'       => true,
733
            'extension' => 'reflection',
734
        ],
735
        'ReflectionNamedType' => [
736
            '7.0'       => false,
737
            '7.1'       => true,
738
            'extension' => 'reflection',
739
        ],
740

741
        'HashContext' => [
742
            '7.1'       => false,
743
            '7.2'       => true,
744
            'extension' => 'hash',
745
        ],
746

747
        'FFI' => [
748
            '7.3'       => false,
749
            '7.4'       => true,
750
            'extension' => 'ffi',
751
        ],
752
        'FFI\CData' => [
753
            '7.3'       => false,
754
            '7.4'       => true,
755
            'extension' => 'ffi',
756
        ],
757
        'FFI\CType' => [
758
            '7.3'       => false,
759
            '7.4'       => true,
760
            'extension' => 'ffi',
761
        ],
762
        'ReflectionReference' => [
763
            '7.3'       => false,
764
            '7.4'       => true,
765
            'extension' => 'reflection',
766
        ],
767
        'WeakReference' => [
768
            '7.3' => false,
769
            '7.4' => true,
770
        ],
771

772
        'Attribute' => [
773
            '7.4' => false,
774
            '8.0' => true,
775
        ],
776
        'PhpToken' => [
777
            '7.4'       => false,
778
            '8.0'       => true,
779
            'extension' => 'tokenizer',
780
        ],
781
        'ReflectionUnionType' => [
782
            '7.4'       => false,
783
            '8.0'       => true,
784
            'extension' => 'reflection',
785
        ],
786
        'WeakMap' => [
787
            '7.4' => false,
788
            '8.0' => true,
789
        ],
790
        'OCICollection' => [
791
            '7.4'       => false,
792
            '8.0'       => true,
793
            'extension' => 'oci8',
794
        ],
795
        'OCILob' => [
796
            '7.4'       => false,
797
            '8.0'       => true,
798
            'extension' => 'oci8',
799
        ],
800
        'CurlHandle' => [
801
            '7.4'       => false,
802
            '8.0'       => true,
803
            'extension' => 'curl',
804
        ],
805
        'CurlMultiHandle' => [
806
            '7.4'       => false,
807
            '8.0'       => true,
808
            'extension' => 'curl',
809
        ],
810
        'CurlShareHandle' => [
811
            '7.4'       => false,
812
            '8.0'       => true,
813
            'extension' => 'curl',
814
        ],
815
        'EnchantBroker' => [
816
            '7.4'       => false,
817
            '8.0'       => true,
818
            'extension' => 'enchant',
819
        ],
820
        'EnchantDictionary' => [
821
            '7.4'       => false,
822
            '8.0'       => true,
823
            'extension' => 'enchant',
824
        ],
825
        'GdImage' => [
826
            '7.4'       => false,
827
            '8.0'       => true,
828
            'extension' => 'gd',
829
        ],
830
        'OpenSSLCertificate' => [
831
            '7.4'       => false,
832
            '8.0'       => true,
833
            'extension' => 'openssl',
834
        ],
835
        'OpenSSLCertificateSigningRequest' => [
836
            '7.4'       => false,
837
            '8.0'       => true,
838
            'extension' => 'openssl',
839
        ],
840
        'OpenSSLAsymmetricKey' => [
841
            '7.4'       => false,
842
            '8.0'       => true,
843
            'extension' => 'openssl',
844
        ],
845
        'Shmop' => [
846
            '7.4'       => false,
847
            '8.0'       => true,
848
            'extension' => 'shmop',
849
        ],
850
        'AddressInfo' => [
851
            '7.4'       => false,
852
            '8.0'       => true,
853
            'extension' => 'sockets',
854
        ],
855
        'Socket' => [
856
            '7.4'       => false,
857
            '8.0'       => true,
858
            'extension' => 'sockets',
859
        ],
860
        'SysvMessageQueue' => [
861
            '7.4'       => false,
862
            '8.0'       => true,
863
            'extension' => 'sem',
864
        ],
865
        'SysvSemaphore' => [
866
            '7.4'       => false,
867
            '8.0'       => true,
868
            'extension' => 'sem',
869
        ],
870
        'SysvSharedMemory' => [
871
            '7.4'       => false,
872
            '8.0'       => true,
873
            'extension' => 'sem',
874
        ],
875
        'XMLParser' => [
876
            '7.4'       => false,
877
            '8.0'       => true,
878
            'extension' => 'xml',
879
        ],
880
        'DeflateContext' => [
881
            '7.4'       => false,
882
            '8.0'       => true,
883
            'extension' => 'zlib',
884
        ],
885
        'InflateContext' => [
886
            '7.4'       => false,
887
            '8.0'       => true,
888
            'extension' => 'zlib',
889
        ],
890

891
        'IntlDatePatternGenerator' => [
892
            '8.0'       => false,
893
            '8.1'       => true,
894
            'extension' => 'intl',
895
        ],
896
        'Fiber' => [
897
            '8.0'       => false,
898
            '8.1'       => true,
899
            'extension' => 'fibers',
900
        ],
901
        'ReflectionEnum' => [
902
            '8.0'       => false,
903
            '8.1'       => true,
904
            'extension' => 'reflection',
905
        ],
906
        'ReflectionEnumBackedCase' => [
907
            '8.0'       => false,
908
            '8.1'       => true,
909
            'extension' => 'reflection',
910
        ],
911
        'ReflectionEnumUnitCase' => [
912
            '8.0'       => false,
913
            '8.1'       => true,
914
            'extension' => 'reflection',
915
        ],
916
        'ReflectionFiber' => [
917
            '8.0'       => false,
918
            '8.1'       => true,
919
            'extension' => 'reflection',
920
        ],
921
        'ReflectionIntersectionType' => [
922
            '8.0'       => false,
923
            '8.1'       => true,
924
            'extension' => 'reflection',
925
        ],
926
        'CURLStringFile' => [
927
            '8.0'       => false,
928
            '8.1'       => true,
929
            'extension' => 'curl',
930
        ],
931
        'FTP\Connection' => [
932
            '8.0'       => false,
933
            '8.1'       => true,
934
            'extension' => 'ftp',
935
        ],
936
        'GdFont' => [
937
            '8.0'       => false,
938
            '8.1'       => true,
939
            'extension' => 'gd',
940
        ],
941
        'IMAP\Connection' => [
942
            '8.0'       => false,
943
            '8.1'       => true,
944
            'extension' => 'imap',
945
        ],
946
        'LDAP\Connection' => [
947
            '8.0'       => false,
948
            '8.1'       => true,
949
            'extension' => 'ldap',
950
        ],
951
        'LDAP\Result' => [
952
            '8.0'       => false,
953
            '8.1'       => true,
954
            'extension' => 'ldap',
955
        ],
956
        'LDAP\ResultEntry' => [
957
            '8.0'       => false,
958
            '8.1'       => true,
959
            'extension' => 'ldap',
960
        ],
961
        'PgSql\Connection' => [
962
            '8.0'       => false,
963
            '8.1'       => true,
964
            'extension' => 'pgsql',
965
        ],
966
        'PgSql\Lob' => [
967
            '8.0'       => false,
968
            '8.1'       => true,
969
            'extension' => 'pgsql',
970
        ],
971
        'PgSql\Result' => [
972
            '8.0'       => false,
973
            '8.1'       => true,
974
            'extension' => 'pgsql',
975
        ],
976
        'PSpell\Config' => [
977
            '8.0'       => false,
978
            '8.1'       => true,
979
            'extension' => 'pspell',
980
        ],
981
        'PSpell\Dictionary' => [
982
            '8.0'       => false,
983
            '8.1'       => true,
984
            'extension' => 'pspell',
985
        ],
986

987
        'Random\Randomizer' => [
988
            '8.1'       => false,
989
            '8.2'       => true,
990
            'extension' => 'random',
991
        ],
992
        'Random\Engine\Secure' => [
993
            '8.1'       => false,
994
            '8.2'       => true,
995
            'extension' => 'random',
996
        ],
997
        'Random\Engine\Mt19937' => [
998
            '8.1'       => false,
999
            '8.2'       => true,
1000
            'extension' => 'random',
1001
        ],
1002
        'Random\Engine\PcgOneseq128XslRr64' => [
1003
            '8.1'       => false,
1004
            '8.2'       => true,
1005
            'extension' => 'random',
1006
        ],
1007
        'Random\Engine\Xoshiro256StarStar' => [
1008
            '8.1'       => false,
1009
            '8.2'       => true,
1010
            'extension' => 'random',
1011
        ],
1012

1013
        'BcMath\Number' => [
1014
            '8.3'       => false,
1015
            '8.4'       => true,
1016
            'extension' => 'bcmath',
1017
        ],
1018
        'Dba\Connection' => [
1019
            '8.3'       => false,
1020
            '8.4'       => true,
1021
            'extension' => 'dba',
1022
        ],
1023
        'Odbc\Connection' => [
1024
            '8.3'       => false,
1025
            '8.4'       => true,
1026
            'extension' => 'odbc',
1027
        ],
1028
        'Odbc\Result' => [
1029
            '8.3'       => false,
1030
            '8.4'       => true,
1031
            'extension' => 'odbc',
1032
        ],
1033
        'Pdo\DbLib' => [
1034
            '8.3'       => false,
1035
            '8.4'       => true,
1036
            'extension' => 'pdo',
1037
        ],
1038
        'Pdo\Firebird' => [
1039
            '8.3'       => false,
1040
            '8.4'       => true,
1041
            'extension' => 'pdo',
1042
        ],
1043
        'Pdo\Mysql' => [
1044
            '8.3'       => false,
1045
            '8.4'       => true,
1046
            'extension' => 'pdo',
1047
        ],
1048
        'Pdo\Odbc' => [
1049
            '8.3'       => false,
1050
            '8.4'       => true,
1051
            'extension' => 'pdo',
1052
        ],
1053
        'Pdo\Pgsql' => [
1054
            '8.3'       => false,
1055
            '8.4'       => true,
1056
            'extension' => 'pdo',
1057
        ],
1058
        'Pdo\Sqlite' => [
1059
            '8.3'       => false,
1060
            '8.4'       => true,
1061
            'extension' => 'pdo',
1062
        ],
1063
        'ReflectionConstant' => [
1064
            '8.3'       => false,
1065
            '8.4'       => true,
1066
            'extension' => 'reflection',
1067
        ],
1068
        'Soap\Sdl' => [
1069
            '8.3'       => false,
1070
            '8.4'       => true,
1071
            'extension' => 'soap',
1072
        ],
1073
        'Soap\Url' => [
1074
            '8.3'       => false,
1075
            '8.4'       => true,
1076
            'extension' => 'soap',
1077
        ],
1078
        'StreamBucket' => [
1079
            '8.3'       => false,
1080
            '8.4'       => true,
1081
            'extension' => 'streams',
1082
        ],
1083
    ];
1084

1085
    /**
1086
     * A list of new Exception classes, not present in older versions.
1087
     *
1088
     * The array lists : version number with false (not present) or true (present).
1089
     * If's sufficient to list the first version where the class appears.
1090
     *
1091
     * {@internal Classes listed here do not need to be added to the $newClasses
1092
     *            property as well.
1093
     *            This list is automatically added to the $newClasses property
1094
     *            in the `register()` method.}
1095
     *
1096
     * {@internal Helper to update this list: https://3v4l.org/MhlUp}
1097
     *
1098
     * @since 7.1.4
1099
     *
1100
     * @var array<string, array<string, bool|string>>
1101
     */
1102
    protected $newExceptions = [
1103
        'com_exception' => [
1104
            '4.4' => false,
1105
            '5.0' => true,
1106
        ],
1107
        'DOMException' => [
1108
            '4.4'       => false,
1109
            '5.0'       => true,
1110
            'extension' => 'dom',
1111
        ],
1112
        'Exception' => [
1113
            // According to the docs introduced in PHP 5.1, but this appears to be.
1114
            // an error.  Class was introduced with try/catch keywords in PHP 5.0.
1115
            '4.4' => false,
1116
            '5.0' => true,
1117
        ],
1118
        'ReflectionException' => [
1119
            '4.4'       => false,
1120
            '5.0'       => true,
1121
            'extension' => 'reflection',
1122
        ],
1123
        'SoapFault' => [
1124
            '4.4'       => false,
1125
            '5.0'       => true,
1126
            'extension' => 'soap',
1127
        ],
1128
        'SQLiteException' => [
1129
            '4.4'       => false,
1130
            '5.0'       => true,
1131
            'extension' => 'sqlite',
1132
        ],
1133
        'mysqli_sql_exception' => [
1134
            '4.4'       => false,
1135
            '5.0'       => true,
1136
            'extension' => 'mysqli',
1137
        ],
1138

1139
        'ErrorException' => [
1140
            '5.0' => false,
1141
            '5.1' => true,
1142
        ],
1143
        'BadFunctionCallException' => [
1144
            '5.0'       => false,
1145
            '5.1'       => true,
1146
            'extension' => 'spl',
1147
        ],
1148
        'BadMethodCallException' => [
1149
            '5.0'       => false,
1150
            '5.1'       => true,
1151
            'extension' => 'spl',
1152
        ],
1153
        'DomainException' => [
1154
            '5.0'       => false,
1155
            '5.1'       => true,
1156
            'extension' => 'spl',
1157
        ],
1158
        'InvalidArgumentException' => [
1159
            '5.0'       => false,
1160
            '5.1'       => true,
1161
            'extension' => 'spl',
1162
        ],
1163
        'LengthException' => [
1164
            '5.0'       => false,
1165
            '5.1'       => true,
1166
            'extension' => 'spl',
1167
        ],
1168
        'LogicException' => [
1169
            '5.0'       => false,
1170
            '5.1'       => true,
1171
            'extension' => 'spl',
1172
        ],
1173
        'OutOfBoundsException' => [
1174
            '5.0'       => false,
1175
            '5.1'       => true,
1176
            'extension' => 'spl',
1177
        ],
1178
        'OutOfRangeException' => [
1179
            '5.0'       => false,
1180
            '5.1'       => true,
1181
            'extension' => 'spl',
1182
        ],
1183
        'OverflowException' => [
1184
            '5.0'       => false,
1185
            '5.1'       => true,
1186
            'extension' => 'spl',
1187
        ],
1188
        'PDOException' => [
1189
            '5.0'       => false,
1190
            '5.1'       => true,
1191
            'extension' => 'pdo',
1192
        ],
1193
        'RangeException' => [
1194
            '5.0'       => false,
1195
            '5.1'       => true,
1196
            'extension' => 'spl',
1197
        ],
1198
        'RuntimeException' => [
1199
            '5.0'       => false,
1200
            '5.1'       => true,
1201
            'extension' => 'spl',
1202
        ],
1203
        'UnderflowException' => [
1204
            '5.0'       => false,
1205
            '5.1'       => true,
1206
            'extension' => 'spl',
1207
        ],
1208
        'UnexpectedValueException' => [
1209
            '5.0'       => false,
1210
            '5.1'       => true,
1211
            'extension' => 'spl',
1212
        ],
1213

1214
        'PharException' => [
1215
            '5.2'       => false,
1216
            '5.3'       => true,
1217
            'extension' => 'phar',
1218
        ],
1219

1220
        'SNMPException' => [
1221
            '5.3'       => false,
1222
            '5.4'       => true,
1223
            'extension' => 'snmp',
1224
        ],
1225

1226
        'IntlException' => [
1227
            '5.4'       => false,
1228
            '5.5'       => true,
1229
            'extension' => 'intl',
1230
        ],
1231

1232
        'Error' => [
1233
            '5.6' => false,
1234
            '7.0' => true,
1235
        ],
1236
        'ArithmeticError' => [
1237
            '5.6' => false,
1238
            '7.0' => true,
1239
        ],
1240
        'AssertionError' => [
1241
            '5.6' => false,
1242
            '7.0' => true,
1243
        ],
1244
        'DivisionByZeroError' => [
1245
            '5.6' => false,
1246
            '7.0' => true,
1247
        ],
1248
        'ParseError' => [
1249
            '5.6' => false,
1250
            '7.0' => true,
1251
        ],
1252
        'TypeError' => [
1253
            '5.6' => false,
1254
            '7.0' => true,
1255
        ],
1256
        'ClosedGeneratorException' => [
1257
            '5.6' => false,
1258
            '7.0' => true,
1259
        ],
1260

1261
        'ArgumentCountError' => [
1262
            '7.0' => false,
1263
            '7.1' => true,
1264
        ],
1265

1266
        'SodiumException' => [
1267
            '7.1' => false,
1268
            '7.2' => true,
1269
        ],
1270

1271
        'CompileError' => [
1272
            '7.2' => false,
1273
            '7.3' => true,
1274
        ],
1275
        'JsonException' => [
1276
            '7.2'       => false,
1277
            '7.3'       => true,
1278
            'extension' => 'json',
1279
        ],
1280

1281
        'FFI\Exception' => [
1282
            '7.3'       => false,
1283
            '7.4'       => true,
1284
            'extension' => 'ffi',
1285
        ],
1286
        'FFI\ParserException' => [
1287
            '7.3'       => false,
1288
            '7.4'       => true,
1289
            'extension' => 'ffi',
1290
        ],
1291

1292
        'UnhandledMatchError' => [
1293
            '7.4' => false,
1294
            '8.0' => true,
1295
        ],
1296
        'ValueError' => [
1297
            '7.4' => false,
1298
            '8.0' => true,
1299
        ],
1300

1301
        'FiberError' => [
1302
            '8.0'       => false,
1303
            '8.1'       => true,
1304
            'extension' => 'fibers',
1305
        ],
1306

1307
        'Random\RandomError' => [
1308
            '8.1'       => false,
1309
            '8.2'       => true,
1310
            'extension' => 'random',
1311
        ],
1312
        'Random\BrokenRandomEngineError' => [
1313
            '8.1'       => false,
1314
            '8.2'       => true,
1315
            'extension' => 'random',
1316
        ],
1317
        'Random\RandomException' => [
1318
            '8.1'       => false,
1319
            '8.2'       => true,
1320
            'extension' => 'random',
1321
        ],
1322

1323
        'DateError' => [
1324
            '8.2'       => false,
1325
            '8.3'       => true,
1326
            'extension' => 'datetime',
1327
        ],
1328
        'DateObjectError' => [
1329
            '8.2'       => false,
1330
            '8.3'       => true,
1331
            'extension' => 'datetime',
1332
        ],
1333
        'DateRangeError' => [
1334
            '8.2'       => false,
1335
            '8.3'       => true,
1336
            'extension' => 'datetime',
1337
        ],
1338
        'DateException' => [
1339
            '8.2'       => false,
1340
            '8.3'       => true,
1341
            'extension' => 'datetime',
1342
        ],
1343
        'DateInvalidTimeZoneException' => [
1344
            '8.2'       => false,
1345
            '8.3'       => true,
1346
            'extension' => 'datetime',
1347
        ],
1348
        'DateInvalidOperationException' => [
1349
            '8.2'       => false,
1350
            '8.3'       => true,
1351
            'extension' => 'datetime',
1352
        ],
1353
        'DateMalformedStringException' => [
1354
            '8.2'       => false,
1355
            '8.3'       => true,
1356
            'extension' => 'datetime',
1357
        ],
1358
        'DateMalformedIntervalStringException' => [
1359
            '8.2'       => false,
1360
            '8.3'       => true,
1361
            'extension' => 'datetime',
1362
        ],
1363
        'DateMalformedPeriodStringException' => [
1364
            '8.2'       => false,
1365
            '8.3'       => true,
1366
            'extension' => 'datetime',
1367
        ],
1368
        'SQLite3Exception' => [
1369
            '8.2'       => false,
1370
            '8.3'       => true,
1371
            'extension' => 'sqlite3',
1372
        ],
1373

1374
        'RequestParseBodyException' => [
1375
            '8.3' => false,
1376
            '8.4' => true,
1377
        ],
1378
    ];
1379

1380
    /**
1381
     * Current file being scanned.
1382
     *
1383
     * @since 10.0.0
1384
     *
1385
     * @var string
1386
     */
1387
    private $currentFile = '';
1388

1389
    /**
1390
     * Stores information about imported, namespaced classes with names which are also in use by PHP.
1391
     *
1392
     * When those classes are used, they do not point to the PHP classes, but to the
1393
     * namespaced, imported class and those usages should be ignored by the sniff.
1394
     *
1395
     * The array is indexed by unqualified class names in lower case. The value is always true.
1396
     * It is structured this way to utilize the isset() function for faster lookups.
1397
     *
1398
     * @since 10.0.0
1399
     *
1400
     * @var array<string, true>
1401
     */
1402
    private $importedClasses = [];
1403

1404
    /**
1405
     * Returns an array of tokens this test wants to listen for.
1406
     *
1407
     * @since 5.5
1408
     * @since 7.0.3  - Now also targets the `class` keyword to detect extended classes.
1409
     *               - Now also targets double colons to detect static class use.
1410
     * @since 7.1.4  - Now also targets anonymous classes to detect extended classes.
1411
     *               - Now also targets functions/closures to detect new classes used
1412
     *                 as parameter type declarations.
1413
     *               - Now also targets the `catch` control structure to detect new
1414
     *                 exception classes being caught.
1415
     * @since 8.2.0  Now also targets the `T_RETURN_TYPE` token to detect new classes used
1416
     *               as return type declarations.
1417
     * @since 10.0.0 `T_RETURN_TYPE` token removed after PHPCS < 3.7.1 version drop.
1418
     *
1419
     * @return array<int|string>
1420
     */
1421
    public function register()
16✔
1422
    {
1423
        // Handle case-insensitivity of class names.
1424
        $this->newClasses    = \array_change_key_case($this->newClasses, \CASE_LOWER);
16✔
1425
        $this->newExceptions = \array_change_key_case($this->newExceptions, \CASE_LOWER);
16✔
1426

1427
        // Add the Exception classes to the Classes list.
1428
        $this->newClasses = \array_merge($this->newClasses, $this->newExceptions);
16✔
1429

1430
        $targets = [
8✔
1431
            \T_USE          => \T_USE,
16✔
1432
            \T_NEW          => \T_NEW,
12✔
1433
            \T_CLASS        => \T_CLASS,
12✔
1434
            \T_ANON_CLASS   => \T_ANON_CLASS,
12✔
1435
            \T_DOUBLE_COLON => \T_DOUBLE_COLON,
12✔
1436
            \T_CATCH        => \T_CATCH,
12✔
1437
            \T_CONST        => \T_CONST,
12✔
1438
        ];
12✔
1439

1440
        $targets += Collections::functionDeclarationTokens();
16✔
1441
        $targets += Collections::ooPropertyScopes();
16✔
1442

1443
        return $targets;
16✔
1444
    }
1445

1446

1447
    /**
1448
     * Processes this test, when one of its tokens is encountered.
1449
     *
1450
     * @since 5.5
1451
     *
1452
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
1453
     * @param int                         $stackPtr  The position of the current token in
1454
     *                                               the stack passed in $tokens.
1455
     *
1456
     * @return void
1457
     */
1458
    public function process(File $phpcsFile, $stackPtr)
136✔
1459
    {
1460
        $fileName = $phpcsFile->getFilename();
136✔
1461
        if ($this->currentFile !== $fileName) {
136✔
1462
            // Reset the properties for each new file.
1463
            $this->currentFile     = $fileName;
16✔
1464
            $this->importedClasses = [];
16✔
1465
        }
4✔
1466

1467
        $tokens = $phpcsFile->getTokens();
136✔
1468

1469
        switch ($tokens[$stackPtr]['code']) {
136✔
1470
            case \T_USE:
34✔
1471
                $this->processUseToken($phpcsFile, $stackPtr);
8✔
1472
                break;
8✔
1473

1474
            case \T_CONST:
34✔
1475
                $this->processConstantToken($phpcsFile, $stackPtr);
128✔
1476
                break;
128✔
1477

1478
            case \T_CATCH:
34✔
1479
                $this->processCatchToken($phpcsFile, $stackPtr);
128✔
1480
                break;
128✔
1481

1482
            case \T_NEW:
34✔
1483
            case \T_CLASS:
34✔
1484
            case \T_ANON_CLASS:
34✔
1485
            case \T_DOUBLE_COLON:
136✔
1486
                $this->processSingularToken($phpcsFile, $stackPtr);
136✔
1487
                break;
136✔
1488
        }
34✔
1489

1490
        if (isset(Collections::ooPropertyScopes()[$tokens[$stackPtr]['code']]) === true) {
136✔
1491
            $this->processOOProperties($phpcsFile, $stackPtr);
136✔
1492
        }
34✔
1493

1494
        if (isset(Collections::functionDeclarationTokens()[$tokens[$stackPtr]['code']]) === true) {
136✔
1495
            $this->processFunctionToken($phpcsFile, $stackPtr);
136✔
1496
        }
34✔
1497
    }
68✔
1498

1499

1500
    /**
1501
     * Processes this test for when a token resulting in a singular class name is encountered.
1502
     *
1503
     * @since 7.1.4
1504
     *
1505
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
1506
     * @param int                         $stackPtr  The position of the current token in
1507
     *                                               the stack passed in $tokens.
1508
     *
1509
     * @return void
1510
     */
1511
    private function processSingularToken(File $phpcsFile, $stackPtr)
136✔
1512
    {
1513
        $tokens      = $phpcsFile->getTokens();
136✔
1514
        $FQClassName = '';
136✔
1515

1516
        if ($tokens[$stackPtr]['code'] === \T_NEW) {
136✔
1517
            $FQClassName = ResolveHelper::getFQClassNameFromNewToken($phpcsFile, $stackPtr);
136✔
1518

1519
        } elseif ($tokens[$stackPtr]['code'] === \T_CLASS || $tokens[$stackPtr]['code'] === \T_ANON_CLASS) {
136✔
1520
            $FQClassName = ResolveHelper::getFQExtendedClassName($phpcsFile, $stackPtr);
136✔
1521

1522
        } elseif ($tokens[$stackPtr]['code'] === \T_DOUBLE_COLON) {
136✔
1523
            $FQClassName = ResolveHelper::getFQClassNameFromDoubleColonToken($phpcsFile, $stackPtr);
136✔
1524
        }
34✔
1525

1526
        if ($FQClassName === '') {
136✔
1527
            return;
136✔
1528
        }
1529

1530
        $className   = \substr($FQClassName, 1); // Remove global namespace indicator.
136✔
1531
        $classNameLc = \strtolower($className);
136✔
1532

1533
        if (isset($this->newClasses[$classNameLc]) === false) {
136✔
1534
            return;
136✔
1535
        }
1536

1537
        $itemInfo = [
68✔
1538
            'name'   => $className,
136✔
1539
            'nameLc' => $classNameLc,
136✔
1540
        ];
102✔
1541
        $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
136✔
1542
    }
68✔
1543

1544

1545
    /**
1546
     * Processes this test for when a function token is encountered.
1547
     *
1548
     * - Detect new classes when used as a parameter type declaration.
1549
     * - Detect new classes when used as a return type declaration.
1550
     *
1551
     * @since 7.1.4
1552
     *
1553
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
1554
     * @param int                         $stackPtr  The position of the current token in
1555
     *                                               the stack passed in $tokens.
1556
     *
1557
     * @return void
1558
     */
1559
    private function processFunctionToken(File $phpcsFile, $stackPtr)
136✔
1560
    {
1561
        /*
1562
         * Check parameter type declarations.
1563
         */
1564
        $parameters = FunctionDeclarations::getParameters($phpcsFile, $stackPtr);
136✔
1565
        if (empty($parameters) === false && \is_array($parameters) === true) {
136✔
1566
            foreach ($parameters as $param) {
136✔
1567
                if ($param['type_hint'] === '') {
136✔
1568
                    continue;
128✔
1569
                }
1570

1571
                $this->checkTypeDeclaration($phpcsFile, $param['type_hint_token'], $param['type_hint']);
136✔
1572
            }
34✔
1573
        }
34✔
1574

1575
        /*
1576
         * Check return type declarations.
1577
         */
1578
        $properties = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
136✔
1579
        if ($properties['return_type'] === '') {
136✔
1580
            return;
128✔
1581
        }
1582

1583
        $this->checkTypeDeclaration($phpcsFile, $properties['return_type_token'], $properties['return_type']);
136✔
1584
    }
68✔
1585

1586

1587
    /**
1588
     * Processes this test for when a constant token is encountered.
1589
     *
1590
     * - Detect new classes when used as a class constant type declaration.
1591
     *
1592
     * @since 10.0.0
1593
     *
1594
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
1595
     * @param int                         $stackPtr  The position of the current token in
1596
     *                                               the stack passed in $tokens.
1597
     *
1598
     * @return void
1599
     */
1600
    private function processConstantToken(File $phpcsFile, $stackPtr)
128✔
1601
    {
1602
        try {
1603
            $properties = Constants::getProperties($phpcsFile, $stackPtr);
128✔
1604
        } catch (ValueError $e) {
128✔
1605
            // Not an OO constant or parse error.
1606
            return;
128✔
1607
        }
1608

1609
        if ($properties['type'] === '') {
128✔
1610
            return;
128✔
1611
        }
1612

1613
        $this->checkTypeDeclaration($phpcsFile, $properties['type_token'], $properties['type']);
128✔
1614
    }
64✔
1615

1616

1617
    /**
1618
     * Processes an OO token for properties declared in the OO scope.
1619
     *
1620
     * - Detect new classes when used as a property type declaration.
1621
     *
1622
     * @since 10.0.0
1623
     *
1624
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
1625
     * @param int                         $stackPtr  The position of the current token in
1626
     *                                               the stack passed in $tokens.
1627
     *
1628
     * @return void
1629
     */
1630
    private function processOOProperties(File $phpcsFile, $stackPtr)
136✔
1631
    {
1632
        $ooProperties = ObjectDeclarations::getDeclaredProperties($phpcsFile, $stackPtr);
136✔
1633
        if (empty($ooProperties)) {
136✔
1634
            return;
136✔
1635
        }
1636

1637
        $tokens         = $phpcsFile->getTokens();
128✔
1638
        $endOfStatement = false;
128✔
1639
        foreach ($ooProperties as $variableToken) {
128✔
1640
            if ($endOfStatement !== false && $variableToken < $endOfStatement) {
128✔
1641
                // Don't throw the same error multiple times for multi-property declarations.
1642
                // Also skip over any other constructor promoted properties.
1643
                continue;
128✔
1644
            }
1645

1646
            try {
1647
                $properties = Variables::getMemberProperties($phpcsFile, $variableToken);
128✔
1648
            } catch (ValueError $e) {
128✔
1649
                /*
1650
                 * This must be constructor property promotion.
1651
                 * Ignore for now and skip over any other promoted properties, these will be handled
1652
                 * via the function token for the constructor.
1653
                 */
1654
                $deepestOpen = Parentheses::getLastOpener($phpcsFile, $variableToken);
128✔
1655
                if ($deepestOpen !== false
96✔
1656
                    && $stackPtr < $deepestOpen
128✔
1657
                    && Parentheses::isOwnerIn($phpcsFile, $deepestOpen, \T_FUNCTION)
128✔
1658
                    && isset($tokens[$deepestOpen]['parenthesis_closer'])
128✔
1659
                ) {
32✔
1660
                    $endOfStatement = $tokens[$deepestOpen]['parenthesis_closer'];
128✔
1661
                }
32✔
1662

1663
                continue;
128✔
1664
            }
1665

1666
            if ($properties['type'] === '') {
128✔
1667
                continue;
128✔
1668
            }
1669

1670
            $this->checkTypeDeclaration($phpcsFile, $properties['type_token'], $properties['type']);
128✔
1671

1672
            $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($variableToken + 1));
128✔
1673
        }
32✔
1674
    }
64✔
1675

1676

1677
    /**
1678
     * Processes a type declaration.
1679
     *
1680
     * @since 10.0.0
1681
     *
1682
     * @param \PHP_CodeSniffer\Files\File $phpcsFile  The file being scanned.
1683
     * @param int                         $stackPtr   The position of the current token in
1684
     *                                                the stack passed in $tokens.
1685
     * @param string                      $typeString The type declaration.
1686
     *
1687
     * @return void
1688
     */
1689
    private function checkTypeDeclaration($phpcsFile, $stackPtr, $typeString)
136✔
1690
    {
1691
        $types = TypeString::filterOOTypes(TypeString::toArray($typeString));
136✔
1692

1693
        if (empty($types) === true) {
136✔
1694
            return;
128✔
1695
        }
1696

1697
        foreach ($types as $type) {
136✔
1698
            // Strip off potential (global) namespace indication.
1699
            $type = \ltrim($type, '\\');
136✔
1700

1701
            if ($type === '') {
136✔
1702
                continue;
×
1703
            }
1704

1705
            $typeLc = \strtolower($type);
136✔
1706
            if (isset($this->newClasses[$typeLc]) === false) {
136✔
1707
                continue;
128✔
1708
            }
1709

1710
            $itemInfo = [
68✔
1711
                'name'   => $type,
136✔
1712
                'nameLc' => $typeLc,
136✔
1713
            ];
102✔
1714
            $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
136✔
1715
        }
34✔
1716
    }
68✔
1717

1718

1719
    /**
1720
     * Processes this test for when a catch token is encountered.
1721
     *
1722
     * - Detect exceptions when used in a catch statement.
1723
     *
1724
     * @since 7.1.4
1725
     *
1726
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
1727
     * @param int                         $stackPtr  The position of the current token in
1728
     *                                               the stack passed in $tokens.
1729
     *
1730
     * @return void
1731
     */
1732
    private function processCatchToken(File $phpcsFile, $stackPtr)
128✔
1733
    {
1734
        $exceptions = ControlStructures::getCaughtExceptions($phpcsFile, $stackPtr);
128✔
1735
        if (empty($exceptions) === true) {
128✔
1736
            return;
×
1737
        }
1738

1739
        foreach ($exceptions as $exception) {
128✔
1740
            // Strip off potential (global) namespace indication.
1741
            $name   = \ltrim($exception['type'], '\\');
128✔
1742
            $nameLC = \strtolower($name);
128✔
1743

1744
            if (isset($this->newExceptions[$nameLC]) === true) {
128✔
1745
                $itemInfo = [
64✔
1746
                    'name'   => $name,
128✔
1747
                    'nameLc' => $nameLC,
128✔
1748
                ];
96✔
1749
                $this->handleFeature($phpcsFile, $exception['type_token'], $itemInfo);
128✔
1750
            }
32✔
1751
        }
32✔
1752
    }
64✔
1753

1754
    /**
1755
     * Processes this test for when a use token is encountered.
1756
     *
1757
     * - Save imported classes for later use.
1758
     *
1759
     * @since 10.0.0
1760
     *
1761
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
1762
     * @param int                         $stackPtr  The position of the current token in
1763
     *                                               the stack passed in $tokens.
1764
     *
1765
     * @return void
1766
     */
1767
    private function processUseToken(File $phpcsFile, $stackPtr)
8✔
1768
    {
1769
        if (!UseStatements::isImportUse($phpcsFile, $stackPtr)) {
8✔
1770
            return;
×
1771
        }
1772

1773
        $splitUseStatement = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr);
8✔
1774

1775
        foreach ($splitUseStatement['name'] as $name => $fullyQualifiedName) {
8✔
1776
            $lowerFullyQualifiedName = \strtolower($fullyQualifiedName);
8✔
1777

1778
            if (isset($this->newClasses[$lowerFullyQualifiedName])) {
8✔
1779
                continue;
×
1780
            }
1781

1782
            $this->importedClasses[\strtolower($name)] = true;
8✔
1783
        }
2✔
1784
    }
4✔
1785

1786

1787
    /**
1788
     * Handle the retrieval of relevant information and - if necessary - throwing of an
1789
     * error for a matched item.
1790
     *
1791
     * @since 10.0.0
1792
     *
1793
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
1794
     * @param int                         $stackPtr  The position of the relevant token in
1795
     *                                               the stack.
1796
     * @param array                       $itemInfo  Base information about the item.
1797
     *
1798
     * @return void
1799
     */
1800
    protected function handleFeature(File $phpcsFile, $stackPtr, array $itemInfo)
136✔
1801
    {
1802
        if (isset($this->importedClasses[$itemInfo['nameLc']])) {
136✔
1803
            return;
8✔
1804
        }
1805

1806
        $itemArray   = $this->newClasses[$itemInfo['nameLc']];
136✔
1807
        $versionInfo = $this->getVersionInfo($itemArray);
136✔
1808

1809
        if (empty($versionInfo['not_in_version'])
136✔
1810
            || ScannedCode::shouldRunOnOrBelow($versionInfo['not_in_version']) === false
136✔
1811
        ) {
34✔
1812
            return;
128✔
1813
        }
1814

1815
        $this->addError($phpcsFile, $stackPtr, $itemInfo, $versionInfo);
128✔
1816
    }
64✔
1817

1818

1819
    /**
1820
     * Generates the error for this item.
1821
     *
1822
     * @since 10.0.0
1823
     *
1824
     * @param \PHP_CodeSniffer\Files\File $phpcsFile   The file being scanned.
1825
     * @param int                         $stackPtr    The position of the relevant token in
1826
     *                                                 the stack.
1827
     * @param array                       $itemInfo    Base information about the item.
1828
     * @param string[]                    $versionInfo Array with detail (version) information
1829
     *                                                 relevant to the item.
1830
     *
1831
     * @return void
1832
     */
1833
    protected function addError(File $phpcsFile, $stackPtr, array $itemInfo, array $versionInfo)
128✔
1834
    {
1835
        // Overrule the default message template.
1836
        $this->msgTemplate = 'The built-in class %s is not present in PHP version %s or earlier';
128✔
1837

1838
        $msgInfo = $this->getMessageInfo($itemInfo['name'], $itemInfo['name'], $versionInfo);
128✔
1839

1840
        $phpcsFile->addError($msgInfo['message'], $stackPtr, $msgInfo['errorcode'], $msgInfo['data']);
128✔
1841
    }
64✔
1842
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc