Completed
Push — master ( d54a81...13fd85 )
by Wim
03:05 queued 01:22
created

PHPCSHelper::findEndOfStatement()   C

Complexity

Conditions 24
Paths 25

Size

Total Lines 75
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 75
rs 5.2319
c 0
b 0
f 0
cc 24
eloc 46
nc 25
nop 3

How to fix   Long Method    Complexity   

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
 * PHPCS cross-version compatibility helper class.
4
 *
5
 * @category PHP
6
 * @package  PHPCompatibility
7
 * @author   Juliette Reinders Folmer <[email protected]>
8
 */
9
10
namespace PHPCompatibility;
11
12
/**
13
 * \PHPCompatibility\PHPCSHelper
14
 *
15
 * PHPCS cross-version compatibility helper class.
16
 *
17
 * A number of PHPCS classes were split up into several classes in PHPCS 3.x
18
 * Those classes cannot be aliased as they don't represent the same object.
19
 * This class provides helper methods for functions which were contained in
20
 * one of these classes and which are used within the PHPCompatibility library.
21
 *
22
 * @category PHP
23
 * @package  PHPCompatibility
24
 * @author   Juliette Reinders Folmer <[email protected]>
25
 */
26
class PHPCSHelper
27
{
28
29
    /**
30
     * Get the PHPCS version number.
31
     *
32
     * @return string
33
     */
34
    public static function getVersion()
35
    {
36
        if (defined('\PHP_CodeSniffer\Config::VERSION')) {
37
            // PHPCS 3.x.
38
            return \PHP_CodeSniffer\Config::VERSION;
39
        } else {
40
            // PHPCS 1.x & 2.x.
41
            return \PHP_CodeSniffer::VERSION;
42
        }
43
    }
44
45
46
    /**
47
     * Pass config data to PHPCS.
48
     *
49
     * PHPCS cross-version compatibility helper.
50
     *
51
     * @param string      $key   The name of the config value.
52
     * @param string|null $value The value to set. If null, the config entry
53
     *                           is deleted, reverting it to the default value.
54
     * @param boolean     $temp  Set this config data temporarily for this script run.
55
     *                           This will not write the config data to the config file.
56
     *
57
     * @return void
58
     */
59
    public static function setConfigData($key, $value, $temp = false)
60
    {
61
        if (method_exists('\PHP_CodeSniffer\Config', 'setConfigData')) {
62
            // PHPCS 3.x.
63
            \PHP_CodeSniffer\Config::setConfigData($key, $value, $temp);
64
        } else {
65
            // PHPCS 1.x & 2.x.
66
            \PHP_CodeSniffer::setConfigData($key, $value, $temp);
67
        }
68
    }
69
70
71
    /**
72
     * Get the value of a single PHPCS config key.
73
     *
74
     * @param string $key The name of the config value.
75
     *
76
     * @return string|null
77
     */
78
    public static function getConfigData($key)
79
    {
80
        if (method_exists('\PHP_CodeSniffer\Config', 'getConfigData')) {
81
            // PHPCS 3.x.
82
            return \PHP_CodeSniffer\Config::getConfigData($key);
83
        } else {
84
            // PHPCS 1.x & 2.x.
85
            return \PHP_CodeSniffer::getConfigData($key);
86
        }
87
    }
88
89
90
    /**
91
     * Returns the position of the last non-whitespace token in a statement.
92
     *
93
     * {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File`
94
     * class and introduced in PHPCS 2.1.0.
95
     *
96
     * Once the minimum supported PHPCS version for this standard goes beyond
97
     * that, this method can be removed and calls to it replaced with
98
     * `$phpcsFile->findEndOfStatement($start, $ignore)` calls.
99
     *
100
     * Last synced with PHPCS version: PHPCS 3.3.0-alpha at commit f5d899dcb5c534a1c3cca34668624517856ba823}}
101
     *
102
     * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
103
     * @param int                   $start     The position to start searching from in the token stack.
104
     * @param int|array             $ignore    Token types that should not be considered stop points.
105
     *
106
     * @return int
107
     */
108
    public static function findEndOfStatement(\PHP_CodeSniffer_File $phpcsFile, $start, $ignore = null)
109
    {
110
        if (version_compare(self::getVersion(), '3.3.0', '>=') === true) {
111
            return $phpcsFile->findEndOfStatement($start, $ignore);
112
        }
113
114
        $tokens    = $phpcsFile->getTokens();
115
        $endTokens = array(
116
            T_COLON                => true,
117
            T_COMMA                => true,
118
            T_DOUBLE_ARROW         => true,
119
            T_SEMICOLON            => true,
120
            T_CLOSE_PARENTHESIS    => true,
121
            T_CLOSE_SQUARE_BRACKET => true,
122
            T_CLOSE_CURLY_BRACKET  => true,
123
            T_CLOSE_SHORT_ARRAY    => true,
124
            T_OPEN_TAG             => true,
125
            T_CLOSE_TAG            => true,
126
        );
127
128
        if ($ignore !== null) {
129
            $ignore = (array) $ignore;
130
            foreach ($ignore as $code) {
131
                if (isset($endTokens[$code]) === true) {
132
                    unset($endTokens[$code]);
133
                }
134
            }
135
        }
136
137
        $lastNotEmpty = $start;
138
139
        for ($i = $start; $i < $phpcsFile->numTokens; $i++) {
140
            if ($i !== $start && isset($endTokens[$tokens[$i]['code']]) === true) {
141
                // Found the end of the statement.
142
                if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS
143
                    || $tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET
144
                    || $tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
145
                    || $tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY
146
                    || $tokens[$i]['code'] === T_OPEN_TAG
147
                    || $tokens[$i]['code'] === T_CLOSE_TAG
148
                ) {
149
                    return $lastNotEmpty;
150
                }
151
152
                return $i;
153
            }
154
155
            // Skip nested statements.
156
            if (isset($tokens[$i]['scope_closer']) === true
157
                && ($i === $tokens[$i]['scope_opener']
158
                || $i === $tokens[$i]['scope_condition'])
159
            ) {
160
                if ($i === $start && isset(Util\Tokens::$scopeOpeners[$this->tokens[$i]['code']]) === true) {
161
                    return $this->tokens[$i]['scope_closer'];
0 ignored issues
show
Bug introduced by
The property tokens does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
162
                }
163
164
                $i = $tokens[$i]['scope_closer'];
165
            } elseif (isset($tokens[$i]['bracket_closer']) === true
166
                && $i === $tokens[$i]['bracket_opener']
167
            ) {
168
                $i = $tokens[$i]['bracket_closer'];
169
            } elseif (isset($tokens[$i]['parenthesis_closer']) === true
170
                && $i === $tokens[$i]['parenthesis_opener']
171
            ) {
172
                $i = $tokens[$i]['parenthesis_closer'];
173
            }
174
175
            if (isset(\PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$i]['code']]) === false) {
176
                $lastNotEmpty = $i;
177
            }
178
        }//end for
179
180
        return ($phpcsFile->numTokens - 1);
181
182
    }//end findEndOfStatement()
183
184
185
    /**
186
     * Returns the name of the class that the specified class extends
187
     * (works for classes, anonymous classes and interfaces).
188
     *
189
     * Returns FALSE on error or if there is no extended class name.
190
     *
191
     * {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File`
192
     * class, but with some improvements which have been introduced in
193
     * PHPCS 2.8.0.
194
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/commit/0011d448119d4c568e3ac1f825ae78815bf2cc34}.
195
     *
196
     * Once the minimum supported PHPCS version for this standard goes beyond
197
     * that, this method can be removed and calls to it replaced with
198
     * `$phpcsFile->findExtendedClassName($stackPtr)` calls.
199
     *
200
     * Last synced with PHPCS version: PHPCS 3.1.0-alpha at commit a9efcc9b0703f3f9f4a900623d4e97128a6aafc6}}
201
     *
202
     * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
203
     * @param int                   $stackPtr  The position of the class token in the stack.
204
     *
205
     * @return string|false
206
     */
207
    public static function findExtendedClassName(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
208
    {
209
        if (version_compare(self::getVersion(), '3.1.0', '>=') === true) {
210
            return $phpcsFile->findExtendedClassName($stackPtr);
211
        }
212
213
        $tokens = $phpcsFile->getTokens();
214
215
        // Check for the existence of the token.
216
        if (isset($tokens[$stackPtr]) === false) {
217
            return false;
218
        }
219
220 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_CLASS
221
            && $tokens[$stackPtr]['type'] !== 'T_ANON_CLASS'
222
            && $tokens[$stackPtr]['type'] !== 'T_INTERFACE'
223
        ) {
224
            return false;
225
        }
226
227
        if (isset($tokens[$stackPtr]['scope_closer']) === false) {
228
            return false;
229
        }
230
231
        $classCloserIndex = $tokens[$stackPtr]['scope_closer'];
232
        $extendsIndex     = $phpcsFile->findNext(T_EXTENDS, $stackPtr, $classCloserIndex);
233
        if (false === $extendsIndex) {
234
            return false;
235
        }
236
237
        $find = array(
238
            T_NS_SEPARATOR,
239
            T_STRING,
240
            T_WHITESPACE,
241
        );
242
243
        $end  = $phpcsFile->findNext($find, ($extendsIndex + 1), $classCloserIndex, true);
244
        $name = $phpcsFile->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
245
        $name = trim($name);
246
247
        if ($name === '') {
248
            return false;
249
        }
250
251
        return $name;
252
253
    }//end findExtendedClassName()
254
255
256
    /**
257
     * Returns the name(s) of the interface(s) that the specified class implements.
258
     *
259
     * Returns FALSE on error or if there are no implemented interface names.
260
     *
261
     * {@internal Duplicate of same method as introduced in PHPCS 2.7.
262
     * This method also includes an improvement we use which was only introduced
263
     * in PHPCS 2.8.0, so only defer to upstream for higher versions.
264
     * Once the minimum supported PHPCS version for this sniff library goes beyond
265
     * that, this method can be removed and calls to it replaced with
266
     * `$phpcsFile->findImplementedInterfaceNames($stackPtr)` calls.}}
267
     *
268
     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
269
     * @param int                   $stackPtr  The position of the class token.
270
     *
271
     * @return array|false
272
     */
273
    public static function findImplementedInterfaceNames(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
274
    {
275
        if (version_compare(self::getVersion(), '2.7.1', '>') === true) {
276
            return $phpcsFile->findImplementedInterfaceNames($stackPtr);
277
        }
278
279
        $tokens = $phpcsFile->getTokens();
280
281
        // Check for the existence of the token.
282
        if (isset($tokens[$stackPtr]) === false) {
283
            return false;
284
        }
285
286
        if ($tokens[$stackPtr]['code'] !== T_CLASS
287
            && $tokens[$stackPtr]['type'] !== 'T_ANON_CLASS'
288
        ) {
289
            return false;
290
        }
291
292
        if (isset($tokens[$stackPtr]['scope_closer']) === false) {
293
            return false;
294
        }
295
296
        $classOpenerIndex = $tokens[$stackPtr]['scope_opener'];
297
        $implementsIndex  = $phpcsFile->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
298
        if ($implementsIndex === false) {
299
            return false;
300
        }
301
302
        $find = array(
303
            T_NS_SEPARATOR,
304
            T_STRING,
305
            T_WHITESPACE,
306
            T_COMMA,
307
        );
308
309
        $end  = $phpcsFile->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
310
        $name = $phpcsFile->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
311
        $name = trim($name);
312
313
        if ($name === '') {
314
            return false;
315
        } else {
316
            $names = explode(',', $name);
317
            $names = array_map('trim', $names);
318
            return $names;
319
        }
320
321
    }//end findImplementedInterfaceNames()
322
323
324
    /**
325
     * Returns the method parameters for the specified function token.
326
     *
327
     * Each parameter is in the following format:
328
     *
329
     * <code>
330
     *   0 => array(
331
     *         'token'             => int,     // The position of the var in the token stack.
332
     *         'name'              => '$var',  // The variable name.
333
     *         'content'           => string,  // The full content of the variable definition.
334
     *         'pass_by_reference' => boolean, // Is the variable passed by reference?
335
     *         'variable_length'   => boolean, // Is the param of variable length through use of `...` ?
336
     *         'type_hint'         => string,  // The type hint for the variable.
337
     *         'nullable_type'     => boolean, // Is the variable using a nullable type?
338
     *        )
339
     * </code>
340
     *
341
     * Parameters with default values have an additional array index of
342
     * 'default' with the value of the default as a string.
343
     *
344
     * {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File`
345
     * class, but with some improvements which have been introduced in
346
     * PHPCS 2.8.0.
347
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1117},
348
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1193} and
349
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1293}.
350
     *
351
     * Once the minimum supported PHPCS version for this standard goes beyond
352
     * that, this method can be removed and calls to it replaced with
353
     * `$phpcsFile->getMethodParameters($stackPtr)` calls.
354
     *
355
     * NOTE: This version does not deal with the new T_NULLABLE token type.
356
     * This token is included upstream only in 2.8.0+ and as we defer to upstream
357
     * in that case, no need to deal with it here.
358
     *
359
     * Last synced with PHPCS version: PHPCS 2.9.0-alpha at commit f1511adad043edfd6d2e595e77385c32577eb2bc}}
360
     *
361
     * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
362
     * @param int                   $stackPtr  The position in the stack of the
363
     *                                         function token to acquire the
364
     *                                         parameters for.
365
     *
366
     * @return array|false
367
     * @throws \PHP_CodeSniffer_Exception If the specified $stackPtr is not of
368
     *                                    type T_FUNCTION or T_CLOSURE.
369
     */
370
    public static function getMethodParameters(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
371
    {
372
        if (version_compare(self::getVersion(), '2.7.1', '>') === true) {
373
            return $phpcsFile->getMethodParameters($stackPtr);
374
        }
375
376
        $tokens = $phpcsFile->getTokens();
377
378
        // Check for the existence of the token.
379
        if (isset($tokens[$stackPtr]) === false) {
380
            return false;
381
        }
382
383 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_FUNCTION && $tokens[$stackPtr]['code'] !== T_CLOSURE) {
384
            throw new \PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
385
        }
386
387
        $opener = $tokens[$stackPtr]['parenthesis_opener'];
388
        $closer = $tokens[$stackPtr]['parenthesis_closer'];
389
390
        $vars            = array();
391
        $currVar         = null;
392
        $paramStart      = ($opener + 1);
393
        $defaultStart    = null;
394
        $paramCount      = 0;
395
        $passByReference = false;
396
        $variableLength  = false;
397
        $typeHint        = '';
398
        $nullableType    = false;
399
400
        for ($i = $paramStart; $i <= $closer; $i++) {
401
            // Check to see if this token has a parenthesis or bracket opener. If it does
402
            // it's likely to be an array which might have arguments in it. This
403
            // could cause problems in our parsing below, so lets just skip to the
404
            // end of it.
405 View Code Duplication
            if (isset($tokens[$i]['parenthesis_opener']) === true) {
406
                // Don't do this if it's the close parenthesis for the method.
407
                if ($i !== $tokens[$i]['parenthesis_closer']) {
408
                    $i = ($tokens[$i]['parenthesis_closer'] + 1);
409
                }
410
            }
411
412 View Code Duplication
            if (isset($tokens[$i]['bracket_opener']) === true) {
413
                // Don't do this if it's the close parenthesis for the method.
414
                if ($i !== $tokens[$i]['bracket_closer']) {
415
                    $i = ($tokens[$i]['bracket_closer'] + 1);
416
                }
417
            }
418
419
            switch ($tokens[$i]['type']) {
420
                case 'T_BITWISE_AND':
421
                    $passByReference = true;
422
                    break;
423
                case 'T_VARIABLE':
424
                    $currVar = $i;
425
                    break;
426
                case 'T_ELLIPSIS':
427
                    $variableLength = true;
428
                    break;
429
                case 'T_ARRAY_HINT':
430
                case 'T_CALLABLE':
431
                    $typeHint .= $tokens[$i]['content'];
432
                    break;
433
                case 'T_SELF':
434
                case 'T_PARENT':
435
                case 'T_STATIC':
436
                    // Self and parent are valid, static invalid, but was probably intended as type hint.
437
                    if (isset($defaultStart) === false) {
438
                        $typeHint .= $tokens[$i]['content'];
439
                    }
440
                    break;
441
                case 'T_STRING':
442
                    // This is a string, so it may be a type hint, but it could
443
                    // also be a constant used as a default value.
444
                    $prevComma = false;
445 View Code Duplication
                    for ($t = $i; $t >= $opener; $t--) {
446
                        if ($tokens[$t]['code'] === T_COMMA) {
447
                            $prevComma = $t;
448
                            break;
449
                        }
450
                    }
451
452
                    if ($prevComma !== false) {
453
                        $nextEquals = false;
454 View Code Duplication
                        for ($t = $prevComma; $t < $i; $t++) {
455
                            if ($tokens[$t]['code'] === T_EQUAL) {
456
                                $nextEquals = $t;
457
                                break;
458
                            }
459
                        }
460
461
                        if ($nextEquals !== false) {
462
                            break;
463
                        }
464
                    }
465
466
                    if ($defaultStart === null) {
467
                        $typeHint .= $tokens[$i]['content'];
468
                    }
469
                    break;
470
                case 'T_NS_SEPARATOR':
471
                    // Part of a type hint or default value.
472
                    if ($defaultStart === null) {
473
                        $typeHint .= $tokens[$i]['content'];
474
                    }
475
                    break;
476
                case 'T_INLINE_THEN':
477
                    if ($defaultStart === null) {
478
                        $nullableType = true;
479
                        $typeHint    .= $tokens[$i]['content'];
480
                    }
481
                    break;
482
                case 'T_CLOSE_PARENTHESIS':
483
                case 'T_COMMA':
484
                    // If it's null, then there must be no parameters for this
485
                    // method.
486
                    if ($currVar === null) {
487
                        continue;
488
                    }
489
490
                    $vars[$paramCount]            = array();
491
                    $vars[$paramCount]['token']   = $currVar;
492
                    $vars[$paramCount]['name']    = $tokens[$currVar]['content'];
493
                    $vars[$paramCount]['content'] = trim($phpcsFile->getTokensAsString($paramStart, ($i - $paramStart)));
494
495
                    if ($defaultStart !== null) {
496
                        $vars[$paramCount]['default'] = trim(
497
                            $phpcsFile->getTokensAsString(
498
                                $defaultStart,
499
                                ($i - $defaultStart)
500
                            )
501
                        );
502
                    }
503
504
                    $vars[$paramCount]['pass_by_reference'] = $passByReference;
505
                    $vars[$paramCount]['variable_length']   = $variableLength;
506
                    $vars[$paramCount]['type_hint']         = $typeHint;
507
                    $vars[$paramCount]['nullable_type']     = $nullableType;
508
509
                    // Reset the vars, as we are about to process the next parameter.
510
                    $defaultStart    = null;
511
                    $paramStart      = ($i + 1);
512
                    $passByReference = false;
513
                    $variableLength  = false;
514
                    $typeHint        = '';
515
                    $nullableType    = false;
516
517
                    $paramCount++;
518
                    break;
519
                case 'T_EQUAL':
520
                    $defaultStart = ($i + 1);
521
                    break;
522
            }//end switch
523
        }//end for
524
525
        return $vars;
526
527
    }//end getMethodParameters()
528
}
529