Failed Conditions
Push — test-scrutinizer-coverage ( 1fb662...215aeb )
by Juliette
03:50
created

register()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 15
cts 15
cp 1
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 14
nc 2
nop 0
crap 2
1
<?php
2
/**
3
 * PHPCompatibility_Sniffs_PHP_NewClassesSniff.
4
 *
5
 * @category  PHP
6
 * @package   PHPCompatibility
7
 * @author    Wim Godden <[email protected]>
8
 * @copyright 2013 Cu.be Solutions bvba
9
 */
10
11
/**
12
 * PHPCompatibility_Sniffs_PHP_NewClassesSniff.
13
 *
14
 * @category  PHP
15
 * @package   PHPCompatibility
16
 * @author    Wim Godden <[email protected]>
17
 * @copyright 2013 Cu.be Solutions bvba
18
 */
19
class PHPCompatibility_Sniffs_PHP_NewClassesSniff extends PHPCompatibility_AbstractNewFeatureSniff
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
20
{
21
22
    /**
23
     * A list of new classes, not present in older versions.
24
     *
25
     * The array lists : version number with false (not present) or true (present).
26
     * If's sufficient to list the first version where the class appears.
27
     *
28
     * @var array(string => array(string => bool))
29
     */
30
    protected $newClasses = array(
31
        'libXMLError' => array(
32
            '5.0' => false,
33
            '5.1' => true,
34
        ),
35
36
        'DateTime' => array(
37
            '5.1' => false,
38
            '5.2' => true,
39
        ),
40
        'DateTimeZone' => array(
41
            '5.1' => false,
42
            '5.2' => true,
43
        ),
44
        'RegexIterator' => array(
45
            '5.1' => false,
46
            '5.2' => true,
47
        ),
48
        'RecursiveRegexIterator' => array(
49
            '5.1' => false,
50
            '5.2' => true,
51
        ),
52
53
        'DateInterval' => array(
54
            '5.2' => false,
55
            '5.3' => true,
56
        ),
57
        'DatePeriod' => array(
58
            '5.2' => false,
59
            '5.3' => true,
60
        ),
61
        'Phar' => array(
62
            '5.2' => false,
63
            '5.3' => true,
64
        ),
65
        'PharData' => array(
66
            '5.2' => false,
67
            '5.3' => true,
68
        ),
69
        'PharFileInfo' => array(
70
            '5.2' => false,
71
            '5.3' => true,
72
        ),
73
        'FilesystemIterator' => array(
74
            '5.2' => false,
75
            '5.3' => true,
76
        ),
77
        'GlobIterator' => array(
78
            '5.2' => false,
79
            '5.3' => true,
80
        ),
81
        'MultipleIterator' => array(
82
            '5.2' => false,
83
            '5.3' => true,
84
        ),
85
        'RecursiveTreeIterator' => array(
86
            '5.2' => false,
87
            '5.3' => true,
88
        ),
89
        'SplDoublyLinkedList' => array(
90
            '5.2' => false,
91
            '5.3' => true,
92
        ),
93
        'SplFixedArray' => array(
94
            '5.2' => false,
95
            '5.3' => true,
96
        ),
97
        'SplHeap' => array(
98
            '5.2' => false,
99
            '5.3' => true,
100
        ),
101
        'SplMaxHeap' => array(
102
            '5.2' => false,
103
            '5.3' => true,
104
        ),
105
        'SplMinHeap' => array(
106
            '5.2' => false,
107
            '5.3' => true,
108
        ),
109
        'SplPriorityQueue' => array(
110
            '5.2' => false,
111
            '5.3' => true,
112
        ),
113
        'SplQueue' => array(
114
            '5.2' => false,
115
            '5.3' => true,
116
        ),
117
        'SplStack' => array(
118
            '5.2' => false,
119
            '5.3' => true,
120
        ),
121
122
        'CallbackFilterIterator' => array(
123
            '5.3' => false,
124
            '5.4' => true,
125
        ),
126
        'RecursiveCallbackFilterIterator' => array(
127
            '5.3' => false,
128
            '5.4' => true,
129
        ),
130
        'ReflectionZendExtension' => array(
131
            '5.3' => false,
132
            '5.4' => true,
133
        ),
134
        'SessionHandler' => array(
135
            '5.3' => false,
136
            '5.4' => true,
137
        ),
138
        'SNMP' => array(
139
            '5.3' => false,
140
            '5.4' => true,
141
        ),
142
        'Transliterator' => array(
143
            '5.3' => false,
144
            '5.4' => true,
145
        ),
146
        'Spoofchecker' => array(
147
            '5.3' => false,
148
            '5.4' => true,
149
        ),
150
151
        'CURLFile' => array(
152
            '5.4' => false,
153
            '5.5' => true,
154
        ),
155
        'DateTimeImmutable' => array(
156
            '5.4' => false,
157
            '5.5' => true,
158
        ),
159
        'IntlCalendar' => array(
160
            '5.4' => false,
161
            '5.5' => true,
162
        ),
163
        'IntlGregorianCalendar' => array(
164
            '5.4' => false,
165
            '5.5' => true,
166
        ),
167
        'IntlTimeZone' => array(
168
            '5.4' => false,
169
            '5.5' => true,
170
        ),
171
        'IntlBreakIterator' => array(
172
            '5.4' => false,
173
            '5.5' => true,
174
        ),
175
        'IntlRuleBasedBreakIterator' => array(
176
            '5.4' => false,
177
            '5.5' => true,
178
        ),
179
        'IntlCodePointBreakIterator' => array(
180
            '5.4' => false,
181
            '5.5' => true,
182
        ),
183
184
    );
185
186
    /**
187
     * A list of new Exception classes, not present in older versions.
188
     *
189
     * The array lists : version number with false (not present) or true (present).
190
     * If's sufficient to list the first version where the class appears.
191
     *
192
     * {@internal Classes listed here do not need to be added to the $newClasses
193
     *            property as well.
194
     *            This list is automatically added to the $newClasses property
195
     *            in the `register()` method.}}
196
     *
197
     * @var array(string => array(string => bool))
198
     */
199
    protected $newExceptions = array(
200
        'Exception' => array(
201
            // According to the docs introduced in PHP 5.1, but this appears to be.
202
            // an error.  Class was introduced with try/catch keywords in PHP 5.0.
203
            '4.4' => false,
204
            '5.0' => true,
205
        ),
206
        'ErrorException' => array(
207
            '5.0' => false,
208
            '5.1' => true,
209
        ),
210
        'BadFunctionCallException' => array(
211
            '5.0' => false,
212
            '5.1' => true,
213
        ),
214
        'BadMethodCallException' => array(
215
            '5.0' => false,
216
            '5.1' => true,
217
        ),
218
        'DomainException' => array(
219
            '5.0' => false,
220
            '5.1' => true,
221
        ),
222
        'InvalidArgumentException' => array(
223
            '5.0' => false,
224
            '5.1' => true,
225
        ),
226
        'LengthException' => array(
227
            '5.0' => false,
228
            '5.1' => true,
229
        ),
230
        'LogicException' => array(
231
            '5.0' => false,
232
            '5.1' => true,
233
        ),
234
        'OutOfBoundsException' => array(
235
            '5.0' => false,
236
            '5.1' => true,
237
        ),
238
        'OutOfRangeException' => array(
239
            '5.0' => false,
240
            '5.1' => true,
241
        ),
242
        'OverflowException' => array(
243
            '5.0' => false,
244
            '5.1' => true,
245
        ),
246
        'RangeException' => array(
247
            '5.0' => false,
248
            '5.1' => true,
249
        ),
250
        'RuntimeException' => array(
251
            '5.0' => false,
252
            '5.1' => true,
253
        ),
254
        'UnderflowException' => array(
255
            '5.0' => false,
256
            '5.1' => true,
257
        ),
258
        'UnexpectedValueException' => array(
259
            '5.0' => false,
260
            '5.1' => true,
261
        ),
262
        'DOMException' => array(
263
            '4.4' => false,
264
            '5.0' => true,
265
        ),
266
        'mysqli_sql_exception' => array(
267
            '4.4' => false,
268
            '5.0' => true,
269
        ),
270
        'PDOException' => array(
271
            '5.0' => false,
272
            '5.1' => true,
273
        ),
274
        'ReflectionException' => array(
275
            '4.4' => false,
276
            '5.0' => true,
277
        ),
278
        'SoapFault' => array(
279
            '4.4' => false,
280
            '5.0' => true,
281
        ),
282
283
        'PharException' => array(
284
            '5.2' => false,
285
            '5.3' => true,
286
        ),
287
288
        'SNMPException' => array(
289
            '5.3' => false,
290
            '5.4' => true,
291
        ),
292
293
        'IntlException' => array(
294
            '5.5.0' => false,
295
            '5.5.1' => true,
296
        ),
297
298
        'Error' => array(
299
            '5.6' => false,
300
            '7.0' => true,
301
        ),
302
        'ArithmeticError' => array(
303
            '5.6' => false,
304
            '7.0' => true,
305
        ),
306
        'AssertionError' => array(
307
            '5.6' => false,
308
            '7.0' => true,
309
        ),
310
        'DivisionByZeroError' => array(
311
            '5.6' => false,
312
            '7.0' => true,
313
        ),
314
        'ParseError' => array(
315
            '5.6' => false,
316
            '7.0' => true,
317
        ),
318
        'TypeError' => array(
319
            '5.6' => false,
320
            '7.0' => true,
321
        ),
322
        'UI\Exception\InvalidArgumentException' => array(
323
            '5.6' => false,
324
            '7.0' => true,
325
        ),
326
        'UI\Exception\RuntimeException' => array(
327
            '5.6' => false,
328
            '7.0' => true,
329
        ),
330
331
    );
332
333
334
    /**
335
     * Returns an array of tokens this test wants to listen for.
336
     *
337
     * @return array
338
     */
339 82
    public function register()
340
    {
341
        // Handle case-insensitivity of class names.
342 82
        $this->newClasses = $this->arrayKeysToLowercase($this->newClasses);
343 82
        $this->newExceptions = $this->arrayKeysToLowercase($this->newExceptions);
344
345
        // Add the Exception classes to the Classes list.
346 82
        $this->newClasses = array_merge($this->newClasses, $this->newExceptions);
347
348
        $targets = array(
349 82
            T_NEW,
350 82
            T_CLASS,
351 82
            T_DOUBLE_COLON,
352 82
            T_FUNCTION,
353 82
            T_CLOSURE,
354 82
            T_CATCH,
355 82
        );
356
357 82
        if (defined('T_ANON_CLASS')) {
358 82
            $targets[] = constant('T_ANON_CLASS');
359 82
        }
360
361 82
        return $targets;
362
363
    }//end register()
364
365
366
    /**
367
     * Processes this test, when one of its tokens is encountered.
368
     *
369
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
370
     * @param int                  $stackPtr  The position of the current token in
371
     *                                        the stack passed in $tokens.
372
     *
373
     * @return void
374
     */
375 9 View Code Duplication
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
376
    {
377 9
        $tokens = $phpcsFile->getTokens();
378
379 9
        switch ($tokens[$stackPtr]['type']) {
380 9
            case 'T_FUNCTION':
381 9
            case 'T_CLOSURE':
382 9
                $this->processFunctionToken($phpcsFile, $stackPtr);
383 9
                break;
384
385 9
            case 'T_CATCH':
386 9
                $this->processCatchToken($phpcsFile, $stackPtr);
387 9
                break;
388
389 9
            default:
390 9
                $this->processSingularToken($phpcsFile, $stackPtr);
391 9
                break;
392 9
        }
393
394 9
    }//end process()
395
396
397
    /**
398
     * Processes this test for when a token resulting in a singular class name is encountered.
399
     *
400
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
401
     * @param int                  $stackPtr  The position of the current token in
402
     *                                        the stack passed in $tokens.
403
     *
404
     * @return void
405
     */
406 9
    private function processSingularToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
407
    {
408 9
        $tokens      = $phpcsFile->getTokens();
409 9
        $FQClassName = '';
410
411 9
        if ($tokens[$stackPtr]['type'] === 'T_NEW') {
412 9
            $FQClassName = $this->getFQClassNameFromNewToken($phpcsFile, $stackPtr);
413
414 9
        } elseif ($tokens[$stackPtr]['type'] === 'T_CLASS' || $tokens[$stackPtr]['type'] === 'T_ANON_CLASS') {
415 9
            $FQClassName = $this->getFQExtendedClassName($phpcsFile, $stackPtr);
416
417 9
        } elseif ($tokens[$stackPtr]['type'] === 'T_DOUBLE_COLON') {
418 9
            $FQClassName = $this->getFQClassNameFromDoubleColonToken($phpcsFile, $stackPtr);
419 9
        }
420
421 9
        if ($FQClassName === '') {
422 9
            return;
423
        }
424
425 9
        $className   = substr($FQClassName, 1); // Remove global namespace indicator.
426 9
        $classNameLc = strtolower($className);
427
428 9
        if (isset($this->newClasses[$classNameLc]) === false) {
429 9
            return;
430
        }
431
432
        $itemInfo = array(
433 9
            'name'   => $className,
434 9
            'nameLc' => $classNameLc,
435 9
        );
436 9
        $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
437
438 9
    }//end processSingularToken()
439
440
441
    /**
442
     * Processes this test for when a function token is encountered.
443
     *
444
     * - Detect new classes when used as a type hint.
445
     *
446
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
447
     * @param int                  $stackPtr  The position of the current token in
448
     *                                        the stack passed in $tokens.
449
     *
450
     * @return void
451
     */
452 9 View Code Duplication
    private function processFunctionToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
453
    {
454
        // Retrieve typehints stripped of global NS indicator and/or nullable indicator.
455 9
        $typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr);
456 9
        if (empty($typeHints) || is_array($typeHints) === false) {
457
            return;
458
        }
459
460 9
        foreach ($typeHints as $hint) {
461
462 9
            $typeHintLc = strtolower($hint);
463
464 9
            if (isset($this->newClasses[$typeHintLc]) === true) {
465
                $itemInfo = array(
466 9
                    'name'   => $hint,
467 9
                    'nameLc' => $typeHintLc,
468 9
                );
469 9
                $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
470 9
            }
471 9
        }
472 9
    }
473
474
475
    /**
476
     * Processes this test for when a catch token is encountered.
477
     *
478
     * - Detect exceptions when used in a catch statement.
479
     *
480
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
481
     * @param int                  $stackPtr  The position of the current token in
482
     *                                        the stack passed in $tokens.
483
     *
484
     * @return void
485
     */
486 9
    private function processCatchToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
487
    {
488 9
        $tokens = $phpcsFile->getTokens();
489
490
        // Bow out during live coding.
491 9
        if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
492
            return;
493
        }
494
495 9
        $opener = $tokens[$stackPtr]['parenthesis_opener'];
496 9
        $closer = ($tokens[$stackPtr]['parenthesis_closer'] + 1);
497 9
        $name   = '';
498
        $listen = array(
499
            // Parts of a (namespaced) class name.
500 9
            T_STRING              => true,
501 9
            T_NS_SEPARATOR        => true,
502
            // End/split tokens.
503 9
            T_VARIABLE            => false,
504 9
            T_BITWISE_OR          => false,
505 9
            T_CLOSE_CURLY_BRACKET => false, // Shouldn't be needed as we expect a var before this.
506 9
        );
507
508 9
        for ($i = ($opener + 1); $i < $closer; $i++) {
509 9
            if (isset($listen[$tokens[$i]['code']]) === false) {
510 9
                continue;
511
            }
512
513 9
            if ($listen[$tokens[$i]['code']] === true) {
514 9
                $name .= $tokens[$i]['content'];
515 9
                continue;
516
            } else {
517 9
                if (empty($name) === true) {
518
                    // Weird, we should have a name by the time we encounter a variable or |.
519
                    // So this may be the closer.
520
                    continue;
521
                }
522
523 9
                $name   = ltrim($name, '\\');
524 9
                $nameLC = strtolower($name);
525
526 9
                if (isset($this->newExceptions[$nameLC]) === true) {
527
                    $itemInfo = array(
528 9
                        'name'   => $name,
529 9
                        'nameLc' => $nameLC,
530 9
                    );
531 9
                    $this->handleFeature($phpcsFile, $i, $itemInfo);
532 9
                }
533
534
                // Reset for a potential multi-catch.
535 9
                $name = '';
536
            }
537 9
        }
538 9
    }
539
540
541
    /**
542
     * Get the relevant sub-array for a specific item from a multi-dimensional array.
543
     *
544
     * @param array $itemInfo Base information about the item.
545
     *
546
     * @return array Version and other information about the item.
547
     */
548 9
    public function getItemArray(array $itemInfo)
549
    {
550 9
        return $this->newClasses[$itemInfo['nameLc']];
551
    }
552
553
554
    /**
555
     * Get the error message template for this sniff.
556
     *
557
     * @return string
558
     */
559 7
    protected function getErrorMsgTemplate()
560
    {
561 7
        return 'The built-in class '.parent::getErrorMsgTemplate();
562
    }
563
564
565
}//end class
566