Completed
Push — feature/travis-add-interim-php... ( d1bcd9...c11f41 )
by Juliette
02:17
created

processSingularToken()   C

Complexity

Conditions 7
Paths 12

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 19
nc 12
nop 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
    public function register()
340
    {
341
        // Handle case-insensitivity of class names.
342
        $this->newClasses = $this->arrayKeysToLowercase($this->newClasses);
343
        $this->newExceptions = $this->arrayKeysToLowercase($this->newExceptions);
344
345
        // Add the Exception classes to the Classes list.
346
        $this->newClasses = array_merge($this->newClasses, $this->newExceptions);
347
348
        $targets = array(
349
            T_NEW,
350
            T_CLASS,
351
            T_DOUBLE_COLON,
352
            T_FUNCTION,
353
            T_CLOSURE,
354
            T_CATCH,
355
        );
356
357
        if (defined('T_ANON_CLASS')) {
358
            $targets[] = constant('T_ANON_CLASS');
359
        }
360
361
        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 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
        $tokens = $phpcsFile->getTokens();
378
379
        switch ($tokens[$stackPtr]['type']) {
380
            case 'T_FUNCTION':
381
            case 'T_CLOSURE':
382
                $this->processFunctionToken($phpcsFile, $stackPtr);
383
                break;
384
385
            case 'T_CATCH':
386
                $this->processCatchToken($phpcsFile, $stackPtr);
387
                break;
388
389
            default:
390
                $this->processSingularToken($phpcsFile, $stackPtr);
391
                break;
392
        }
393
394
    }//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
    private function processSingularToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
407
    {
408
        $tokens      = $phpcsFile->getTokens();
409
        $FQClassName = '';
410
411
        if ($tokens[$stackPtr]['type'] === 'T_NEW') {
412
            $FQClassName = $this->getFQClassNameFromNewToken($phpcsFile, $stackPtr);
413
414
        } elseif ($tokens[$stackPtr]['type'] === 'T_CLASS' || $tokens[$stackPtr]['type'] === 'T_ANON_CLASS') {
415
            $FQClassName = $this->getFQExtendedClassName($phpcsFile, $stackPtr);
416
417
        } elseif ($tokens[$stackPtr]['type'] === 'T_DOUBLE_COLON') {
418
            $FQClassName = $this->getFQClassNameFromDoubleColonToken($phpcsFile, $stackPtr);
419
        }
420
421
        if ($FQClassName === '') {
422
            return;
423
        }
424
425
        $className   = substr($FQClassName, 1); // Remove global namespace indicator.
426
        $classNameLc = strtolower($className);
427
428
        if (isset($this->newClasses[$classNameLc]) === false) {
429
            return;
430
        }
431
432
        $itemInfo = array(
433
            'name'   => $className,
434
            'nameLc' => $classNameLc,
435
        );
436
        $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
437
438
    }//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 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
        $typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr);
456
        if (empty($typeHints) || is_array($typeHints) === false) {
457
            return;
458
        }
459
460
        foreach ($typeHints as $hint) {
461
462
            $typeHintLc = strtolower($hint);
463
464
            if (isset($this->newClasses[$typeHintLc]) === true) {
465
                $itemInfo = array(
466
                    'name'   => $hint,
467
                    'nameLc' => $typeHintLc,
468
                );
469
                $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
470
            }
471
        }
472
    }
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
    private function processCatchToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
487
    {
488
        $tokens = $phpcsFile->getTokens();
489
490
        // Bow out during live coding.
491
        if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
492
            return;
493
        }
494
495
        $opener = $tokens[$stackPtr]['parenthesis_opener'];
496
        $closer = ($tokens[$stackPtr]['parenthesis_closer'] + 1);
497
        $name   = '';
498
        $listen = array(
499
            // Parts of a (namespaced) class name.
500
            T_STRING              => true,
501
            T_NS_SEPARATOR        => true,
502
            // End/split tokens.
503
            T_VARIABLE            => false,
504
            T_BITWISE_OR          => false,
505
            T_CLOSE_CURLY_BRACKET => false, // Shouldn't be needed as we expect a var before this.
506
        );
507
508
        for ($i = ($opener + 1); $i < $closer; $i++) {
509
            if (isset($listen[$tokens[$i]['code']]) === false) {
510
                continue;
511
            }
512
513
            if ($listen[$tokens[$i]['code']] === true) {
514
                $name .= $tokens[$i]['content'];
515
                continue;
516
            } else {
517
                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
                $name   = ltrim($name, '\\');
524
                $nameLC = strtolower($name);
525
526
                if (isset($this->newExceptions[$nameLC]) === true) {
527
                    $itemInfo = array(
528
                        'name'   => $name,
529
                        'nameLc' => $nameLC,
530
                    );
531
                    $this->handleFeature($phpcsFile, $i, $itemInfo);
532
                }
533
534
                // Reset for a potential multi-catch.
535
                $name = '';
536
            }
537
        }
538
    }
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
    public function getItemArray(array $itemInfo)
549
    {
550
        return $this->newClasses[$itemInfo['nameLc']];
551
    }
552
553
554
    /**
555
     * Get the error message template for this sniff.
556
     *
557
     * @return string
558
     */
559
    protected function getErrorMsgTemplate()
560
    {
561
        return 'The built-in class '.parent::getErrorMsgTemplate();
562
    }
563
564
565
}//end class
566