Completed
Push — feature/issue-373-exceptions-i... ( aac8c5 )
by Juliette
02:02
created

processCatchToken()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 53
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 53
rs 7.5251
c 0
b 0
f 0
cc 7
eloc 30
nc 7
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * PHPCompatibility_Sniffs_PHP_NewClassesSniff.
4
 *
5
 * PHP version 5.5
6
 *
7
 * @category  PHP
8
 * @package   PHPCompatibility
9
 * @author    Wim Godden <[email protected]>
10
 * @copyright 2013 Cu.be Solutions bvba
11
 */
12
13
/**
14
 * PHPCompatibility_Sniffs_PHP_NewClassesSniff.
15
 *
16
 * @category  PHP
17
 * @package   PHPCompatibility
18
 * @author    Wim Godden <[email protected]>
19
 * @version   1.0.0
20
 * @copyright 2013 Cu.be Solutions bvba
21
 */
22
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...
23
{
24
25
    /**
26
     * A list of new classes, not present in older versions.
27
     *
28
     * The array lists : version number with false (not present) or true (present).
29
     * If's sufficient to list the first version where the class appears.
30
     *
31
     * @var array(string => array(string => bool))
32
     */
33
    protected $newClasses = array(
34
                                        'libXMLError' => array(
35
                                            '5.0' => false,
36
                                            '5.1' => true
37
                                        ),
38
39
                                        'DateTime' => array(
40
                                            '5.1' => false,
41
                                            '5.2' => true
42
                                        ),
43
                                        'DateTimeZone' => array(
44
                                            '5.1' => false,
45
                                            '5.2' => true
46
                                        ),
47
                                        'RegexIterator' => array(
48
                                            '5.1' => false,
49
                                            '5.2' => true
50
                                        ),
51
                                        'RecursiveRegexIterator' => array(
52
                                            '5.1' => false,
53
                                            '5.2' => true
54
                                        ),
55
56
                                        'DateInterval' => array(
57
                                            '5.2' => false,
58
                                            '5.3' => true
59
                                        ),
60
                                        'DatePeriod' => array(
61
                                            '5.2' => false,
62
                                            '5.3' => true
63
                                        ),
64
                                        'Phar' => array(
65
                                            '5.2' => false,
66
                                            '5.3' => true
67
                                        ),
68
                                        'PharData' => array(
69
                                            '5.2' => false,
70
                                            '5.3' => true
71
                                        ),
72
                                        'PharFileInfo' => array(
73
                                            '5.2' => false,
74
                                            '5.3' => true
75
                                        ),
76
                                        'FilesystemIterator' => array(
77
                                            '5.2' => false,
78
                                            '5.3' => true
79
                                        ),
80
                                        'GlobIterator' => array(
81
                                            '5.2' => false,
82
                                            '5.3' => true
83
                                        ),
84
                                        'MultipleIterator' => array(
85
                                            '5.2' => false,
86
                                            '5.3' => true
87
                                        ),
88
                                        'RecursiveTreeIterator' => array(
89
                                            '5.2' => false,
90
                                            '5.3' => true
91
                                        ),
92
                                        'SplDoublyLinkedList' => array(
93
                                            '5.2' => false,
94
                                            '5.3' => true
95
                                        ),
96
                                        'SplFixedArray' => array(
97
                                            '5.2' => false,
98
                                            '5.3' => true
99
                                        ),
100
                                        'SplHeap' => array(
101
                                            '5.2' => false,
102
                                            '5.3' => true
103
                                        ),
104
                                        'SplMaxHeap' => array(
105
                                            '5.2' => false,
106
                                            '5.3' => true
107
                                        ),
108
                                        'SplMinHeap' => array(
109
                                            '5.2' => false,
110
                                            '5.3' => true
111
                                        ),
112
                                        'SplPriorityQueue' => array(
113
                                            '5.2' => false,
114
                                            '5.3' => true
115
                                        ),
116
                                        'SplQueue' => array(
117
                                            '5.2' => false,
118
                                            '5.3' => true
119
                                        ),
120
                                        'SplStack' => array(
121
                                            '5.2' => false,
122
                                            '5.3' => true
123
                                        ),
124
125
                                        'CallbackFilterIterator' => array(
126
                                            '5.3' => false,
127
                                            '5.4' => true
128
                                        ),
129
                                        'RecursiveCallbackFilterIterator' => array(
130
                                            '5.3' => false,
131
                                            '5.4' => true
132
                                        ),
133
                                        'ReflectionZendExtension' => array(
134
                                            '5.3' => false,
135
                                            '5.4' => true
136
                                        ),
137
                                        'SessionHandler' => array(
138
                                            '5.3' => false,
139
                                            '5.4' => true
140
                                        ),
141
                                        'SNMP' => array(
142
                                            '5.3' => false,
143
                                            '5.4' => true
144
                                        ),
145
                                        'Transliterator' => array(
146
                                            '5.3' => false,
147
                                            '5.4' => true
148
                                        ),
149
                                        'Spoofchecker' => array(
150
                                            '5.3' => false,
151
                                            '5.4' => true
152
                                        ),
153
154
                                        'CURLFile' => array(
155
                                            '5.4' => false,
156
                                            '5.5' => true
157
                                        ),
158
                                        'DateTimeImmutable' => array(
159
                                            '5.4' => false,
160
                                            '5.5' => true
161
                                        ),
162
                                        'IntlCalendar' => array(
163
                                            '5.4' => false,
164
                                            '5.5' => true
165
                                        ),
166
                                        'IntlGregorianCalendar' => array(
167
                                            '5.4' => false,
168
                                            '5.5' => true
169
                                        ),
170
                                        'IntlTimeZone' => array(
171
                                            '5.4' => false,
172
                                            '5.5' => true
173
                                        ),
174
                                        'IntlBreakIterator' => array(
175
                                            '5.4' => false,
176
                                            '5.5' => true
177
                                        ),
178
                                        'IntlRuleBasedBreakIterator' => array(
179
                                            '5.4' => false,
180
                                            '5.5' => true
181
                                        ),
182
                                        'IntlCodePointBreakIterator' => array(
183
                                            '5.4' => false,
184
                                            '5.5' => true
185
                                        ),
186
187
                                    );
188
189
    /**
190
     * A list of new Exception classes, not present in older versions.
191
     *
192
     * The array lists : version number with false (not present) or true (present).
193
     * If's sufficient to list the first version where the class appears.
194
     *
195
     * {@internal Classes listed here do not need to be added to the $newClasses
196
     *            property as well.
197
     *            This list is automatically added to the $newClasses property
198
     *            in the `register()` method.}}
199
     *
200
     * @var array(string => array(string => bool))
201
     */
202
    protected $newExceptions = array(
203
        'Exception' => array(
204
            '5.0' => false,
205
            '5.1' => true
206
        ),
207
        'ErrorException' => array(
208
            '5.0' => false,
209
            '5.1' => true
210
        ),
211
        'BadFunctionCallException' => array(
212
            '5.0' => false,
213
            '5.1' => true
214
        ),
215
        'BadMethodCallException' => array(
216
            '5.0' => false,
217
            '5.1' => true
218
        ),
219
        'DomainException' => array(
220
            '5.0' => false,
221
            '5.1' => true
222
        ),
223
        'InvalidArgumentException' => array(
224
            '5.0' => false,
225
            '5.1' => true
226
        ),
227
        'LengthException' => array(
228
            '5.0' => false,
229
            '5.1' => true
230
        ),
231
        'LogicException' => array(
232
            '5.0' => false,
233
            '5.1' => true
234
        ),
235
        'OutOfBoundsException' => array(
236
            '5.0' => false,
237
            '5.1' => true
238
        ),
239
        'OutOfRangeException' => array(
240
            '5.0' => false,
241
            '5.1' => true
242
        ),
243
        'OverflowException' => array(
244
            '5.0' => false,
245
            '5.1' => true
246
        ),
247
        'RangeException' => array(
248
            '5.0' => false,
249
            '5.1' => true
250
        ),
251
        'RuntimeException' => array(
252
            '5.0' => false,
253
            '5.1' => true
254
        ),
255
        'UnderflowException' => array(
256
            '5.0' => false,
257
            '5.1' => true
258
        ),
259
        'UnexpectedValueException' => array(
260
            '5.0' => false,
261
            '5.1' => true
262
        ),
263
        'DOMException' => array(
264
            // According to the docs introduced in PHP 5.0, but Exception was only introduced in 5.1.
265
            '5.0' => false,
266
            '5.1' => true
267
        ),
268
        'mysqli_sql_exception' => array(
269
            // According to the docs introduced in PHP 5.0, but Exception was only introduced in 5.1.
270
            '5.0' => false,
271
            '5.1' => true
272
        ),
273
        'PDOException' => array(
274
            '5.0' => false,
275
            '5.1' => true
276
        ),
277
        'ReflectionException' => array(
278
            // According to the docs introduced in PHP 5.0, but Exception was only introduced in 5.1.
279
            '5.0' => false,
280
            '5.1' => true
281
        ),
282
        'SoapFault' => array(
283
            // According to the docs introduced in PHP 5.0.1, but Exception was only introduced in 5.1.
284
            '5.0' => false,
285
            '5.1' => true
286
        ),
287
288
        'PharException' => array(
289
            '5.2' => false,
290
            '5.3' => true
291
        ),
292
293
        'SNMPException' => array(
294
            '5.3' => false,
295
            '5.4' => true
296
        ),
297
298
        'IntlException' => array(
299
            '5.5.0' => false,
300
            '5.5.1' => true
301
        ),
302
303
        'Error' => array(
304
            '5.6' => false,
305
            '7.0' => true
306
        ),
307
        'ArithmeticError' => array(
308
            '5.6' => false,
309
            '7.0' => true
310
        ),
311
        'AssertionError' => array(
312
            '5.6' => false,
313
            '7.0' => true
314
        ),
315
        'DivisionByZeroError' => array(
316
            '5.6' => false,
317
            '7.0' => true
318
        ),
319
        'ParseError' => array(
320
            '5.6' => false,
321
            '7.0' => true
322
        ),
323
        'TypeError' => array(
324
            '5.6' => false,
325
            '7.0' => true
326
        ),
327
        'UI\Exception\InvalidArgumentException' => array(
328
            '5.6' => false,
329
            '7.0' => true
330
        ),
331
        'UI\Exception\RuntimeException' => array(
332
            '5.6' => false,
333
            '7.0' => true
334
        ),
335
336
    );
337
338
339
    /**
340
     * Returns an array of tokens this test wants to listen for.
341
     *
342
     * @return array
343
     */
344
    public function register()
345
    {
346
        // Handle case-insensitivity of class names.
347
        $this->newClasses = $this->arrayKeysToLowercase($this->newClasses);
348
        $this->newExceptions = $this->arrayKeysToLowercase($this->newExceptions);
349
350
        // Add the Exception classes to the Classes list.
351
        $this->newClasses = array_merge($this->newClasses, $this->newExceptions);
352
353
        $targets = array(
354
            T_NEW,
355
            T_CLASS,
356
            T_DOUBLE_COLON,
357
            T_FUNCTION,
358
            T_CLOSURE,
359
            T_CATCH,
360
        );
361
362
        if (defined('T_ANON_CLASS')) {
363
            $targets[] = constant('T_ANON_CLASS');
364
        }
365
366
        return $targets;
367
368
    }//end register()
369
370
371
    /**
372
     * Processes this test, when one of its tokens is encountered.
373
     *
374
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
375
     * @param int                  $stackPtr  The position of the current token in
376
     *                                        the stack passed in $tokens.
377
     *
378
     * @return void
379
     */
380 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...
381
    {
382
        $tokens = $phpcsFile->getTokens();
383
384
        switch($tokens[$stackPtr]['type']) {
385
            case 'T_FUNCTION':
386
            case 'T_CLOSURE':
387
                $this->processFunctionToken($phpcsFile, $stackPtr);
388
                break;
389
390
            case 'T_CATCH':
391
                $this->processCatchToken($phpcsFile, $stackPtr);
392
                break;
393
394
            default:
395
                $this->processSingularToken($phpcsFile, $stackPtr);
396
                break;
397
        }
398
399
    }//end process()
400
401
402
    /**
403
     * Processes this test for when a token resulting in a singular class name is encountered.
404
     *
405
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
406
     * @param int                  $stackPtr  The position of the current token in
407
     *                                        the stack passed in $tokens.
408
     *
409
     * @return void
410
     */
411
    private function processSingularToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
412
    {
413
        $tokens      = $phpcsFile->getTokens();
414
        $FQClassName = '';
415
416
        if ($tokens[$stackPtr]['type'] === 'T_NEW') {
417
            $FQClassName = $this->getFQClassNameFromNewToken($phpcsFile, $stackPtr);
418
        }
419
        else if ($tokens[$stackPtr]['type'] === 'T_CLASS' || $tokens[$stackPtr]['type'] === 'T_ANON_CLASS') {
420
            $FQClassName = $this->getFQExtendedClassName($phpcsFile, $stackPtr);
421
        }
422
        else if ($tokens[$stackPtr]['type'] === 'T_DOUBLE_COLON') {
423
            $FQClassName = $this->getFQClassNameFromDoubleColonToken($phpcsFile, $stackPtr);
424
        }
425
426
        if ($FQClassName === '') {
427
            return;
428
        }
429
430
        $className   = substr($FQClassName, 1); // Remove global namespace indicator.
431
        $classNameLc = strtolower($className);
432
433
        if (isset($this->newClasses[$classNameLc]) === false) {
434
            return;
435
        }
436
437
        $itemInfo = array(
438
            'name'   => $className,
439
            'nameLc' => $classNameLc,
440
        );
441
        $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
442
443
    }//end processSingularToken()
444
445
446
    /**
447
     * Processes this test for when a function token is encountered.
448
     *
449
     * - Detect new classes when used as a type hint.
450
     *
451
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
452
     * @param int                  $stackPtr  The position of the current token in
453
     *                                        the stack passed in $tokens.
454
     *
455
     * @return void
456
     */
457 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...
458
    {
459
        // Retrieve typehints stripped of global NS indicator and/or nullable indicator.
460
        $typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr);
461
        if (empty($typeHints) || is_array($typeHints) === false) {
462
            return;
463
        }
464
465
        foreach ($typeHints as $hint) {
466
467
            $typeHintLc = strtolower($hint);
468
469
            if (isset($this->newClasses[$typeHintLc]) === true) {
470
                $itemInfo = array(
471
                    'name'   => $hint,
472
                    'nameLc' => $typeHintLc,
473
                );
474
                $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
475
            }
476
        }
477
    }
478
479
480
    /**
481
     * Processes this test for when a catch token is encountered.
482
     *
483
     * - Detect exceptions when used in a catch statement.
484
     *
485
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
486
     * @param int                  $stackPtr  The position of the current token in
487
     *                                        the stack passed in $tokens.
488
     *
489
     * @return void
490
     */
491
    private function processCatchToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
492
    {
493
        $tokens = $phpcsFile->getTokens();
494
495
        // Bow out during live coding.
496
        if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
497
            return;
498
        }
499
500
        $opener = $tokens[$stackPtr]['parenthesis_opener'];
501
        $closer = ($tokens[$stackPtr]['parenthesis_closer'] + 1);
502
        $name   = '';
503
        $listen = array(
504
            // Parts of a (namespaced) class name.
505
            T_STRING              => true,
506
            T_NS_SEPARATOR        => true,
507
            // End/split tokens.
508
            T_VARIABLE            => false,
509
            T_BITWISE_OR          => false,
510
            T_CLOSE_CURLY_BRACKET => false, // Shouldn't be needed as we expect a var before this.
511
        );
512
513
        for ($i = ($opener + 1); $i < $closer; $i++ ) {
514
            if (isset($listen[$tokens[$i]['code']]) === false) {
515
                continue;
516
            }
517
518
            if ($listen[$tokens[$i]['code']] === true) {
519
                $name .= $tokens[$i]['content'];
520
                continue;
521
            } else {
522
                if (empty($name) === true) {
523
                    // Weird, we should have a name by the time we encounter a variable or |.
524
                    // So this may be the closer.
525
                    continue;
526
                }
527
528
                $name   = ltrim($name, '\\');
529
                $nameLC = strtolower($name);
530
531
                if (isset($this->newExceptions[$nameLC]) === true) {
532
                    $itemInfo = array(
533
                        'name'   => $name,
534
                        'nameLc' => $nameLC,
535
                    );
536
                    $this->handleFeature($phpcsFile, $i, $itemInfo);
537
                }
538
539
                // Reset for a potential multi-catch.
540
                $name = '';
541
            }
542
        }
543
    }
544
545
546
    /**
547
     * Get the relevant sub-array for a specific item from a multi-dimensional array.
548
     *
549
     * @param array $itemInfo Base information about the item.
550
     *
551
     * @return array Version and other information about the item.
552
     */
553
    public function getItemArray(array $itemInfo)
554
    {
555
        return $this->newClasses[$itemInfo['nameLc']];
556
    }
557
558
559
    /**
560
     * Get the error message template for this sniff.
561
     *
562
     * @return string
563
     */
564
    protected function getErrorMsgTemplate()
565
    {
566
        return 'The built-in class '.parent::getErrorMsgTemplate();
567
    }
568
569
570
}//end class
571