PHPCSHelper::getMethodParameters()   F
last analyzed

Complexity

Conditions 42
Paths 490

Size

Total Lines 182

Duplication

Lines 49
Ratio 26.92 %

Importance

Changes 0
Metric Value
dl 49
loc 182
rs 0.5666
c 0
b 0
f 0
cc 42
nc 490
nop 2

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
     *         'name'              => '$var',  // The variable name.
332
     *         'token'             => integer, // The stack pointer to 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
     *         'type_hint_token'   => integer, // The stack pointer to the type hint
338
     *                                         // or false if there is no type hint.
339
     *         'nullable_type'     => boolean, // Is the variable using a nullable type?
340
     *        )
341
     * </code>
342
     *
343
     * Parameters with default values have an additional array index of
344
     * 'default' with the value of the default as a string.
345
     *
346
     * {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File`
347
     * class.
348
     *
349
     * Last synced with PHPCS version: PHPCS 3.3.0-alpha at commit 53a28408d345044c0360c2c1b4a2aaebf4a3b8c9}}
350
     *
351
     * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
352
     * @param int                   $stackPtr  The position in the stack of the
353
     *                                         function token to acquire the
354
     *                                         parameters for.
355
     *
356
     * @return array|false
357
     * @throws \PHP_CodeSniffer_Exception If the specified $stackPtr is not of
358
     *                                    type T_FUNCTION or T_CLOSURE.
359
     */
360
    public static function getMethodParameters(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
361
    {
362
        if (version_compare(self::getVersion(), '3.3.0', '>=') === true) {
363
            return $phpcsFile->getMethodParameters($stackPtr);
364
        }
365
366
        $tokens = $phpcsFile->getTokens();
367
368
        // Check for the existence of the token.
369
        if (isset($tokens[$stackPtr]) === false) {
370
            return false;
371
        }
372
373 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_FUNCTION
374
            && $tokens[$stackPtr]['code'] !== T_CLOSURE
375
        ) {
376
            throw new \PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
377
        }
378
379
        $opener = $tokens[$stackPtr]['parenthesis_opener'];
380
        $closer = $tokens[$stackPtr]['parenthesis_closer'];
381
382
        $vars            = array();
383
        $currVar         = null;
384
        $paramStart      = ($opener + 1);
385
        $defaultStart    = null;
386
        $paramCount      = 0;
387
        $passByReference = false;
388
        $variableLength  = false;
389
        $typeHint        = '';
390
        $typeHintToken   = false;
391
        $nullableType    = false;
392
393
        for ($i = $paramStart; $i <= $closer; $i++) {
394
            // Check to see if this token has a parenthesis or bracket opener. If it does
395
            // it's likely to be an array which might have arguments in it. This
396
            // could cause problems in our parsing below, so lets just skip to the
397
            // end of it.
398 View Code Duplication
            if (isset($tokens[$i]['parenthesis_opener']) === true) {
399
                // Don't do this if it's the close parenthesis for the method.
400
                if ($i !== $tokens[$i]['parenthesis_closer']) {
401
                    $i = ($tokens[$i]['parenthesis_closer'] + 1);
402
                }
403
            }
404
405 View Code Duplication
            if (isset($tokens[$i]['bracket_opener']) === true) {
406
                // Don't do this if it's the close parenthesis for the method.
407
                if ($i !== $tokens[$i]['bracket_closer']) {
408
                    $i = ($tokens[$i]['bracket_closer'] + 1);
409
                }
410
            }
411
412
            switch ($tokens[$i]['type']) {
413
                case 'T_BITWISE_AND':
414
                    if ($defaultStart === null) {
415
                        $passByReference = true;
416
                    }
417
                    break;
418
                case 'T_VARIABLE':
419
                    $currVar = $i;
420
                    break;
421
                case 'T_ELLIPSIS':
422
                    $variableLength = true;
423
                    break;
424
                case 'T_ARRAY_HINT': // Pre-PHPCS 3.3.0.
425
                case 'T_CALLABLE':
426
                    if ($typeHintToken === false) {
427
                        $typeHintToken = $i;
428
                    }
429
430
                    $typeHint .= $tokens[$i]['content'];
431
                    break;
432
                case 'T_SELF':
433
                case 'T_PARENT':
434 View Code Duplication
                case 'T_STATIC':
435
                    // Self and parent are valid, static invalid, but was probably intended as type hint.
436
                    if (isset($defaultStart) === false) {
437
                        if ($typeHintToken === false) {
438
                            $typeHintToken = $i;
439
                        }
440
441
                        $typeHint .= $tokens[$i]['content'];
442
                    }
443
                    break;
444
                case 'T_STRING':
445
                    // This is a string, so it may be a type hint, but it could
446
                    // also be a constant used as a default value.
447
                    $prevComma = false;
448 View Code Duplication
                    for ($t = $i; $t >= $opener; $t--) {
449
                        if ($tokens[$t]['code'] === T_COMMA) {
450
                            $prevComma = $t;
451
                            break;
452
                        }
453
                    }
454
455
                    if ($prevComma !== false) {
456
                        $nextEquals = false;
457 View Code Duplication
                        for ($t = $prevComma; $t < $i; $t++) {
458
                            if ($tokens[$t]['code'] === T_EQUAL) {
459
                                $nextEquals = $t;
460
                                break;
461
                            }
462
                        }
463
464
                        if ($nextEquals !== false) {
465
                            break;
466
                        }
467
                    }
468
469
                    if ($defaultStart === null) {
470
                        if ($typeHintToken === false) {
471
                            $typeHintToken = $i;
472
                        }
473
474
                        $typeHint .= $tokens[$i]['content'];
475
                    }
476
                    break;
477 View Code Duplication
                case 'T_NS_SEPARATOR':
478
                    // Part of a type hint or default value.
479
                    if ($defaultStart === null) {
480
                        if ($typeHintToken === false) {
481
                            $typeHintToken = $i;
482
                        }
483
484
                        $typeHint .= $tokens[$i]['content'];
485
                    }
486
                    break;
487
                case 'T_NULLABLE':
488
                case 'T_INLINE_THEN': // Pre-PHPCS 2.8.0.
489
                    if ($defaultStart === null) {
490
                        $nullableType = true;
491
                        $typeHint    .= $tokens[$i]['content'];
492
                    }
493
                    break;
494
                case 'T_CLOSE_PARENTHESIS':
495
                case 'T_COMMA':
496
                    // If it's null, then there must be no parameters for this
497
                    // method.
498
                    if ($currVar === null) {
499
                        continue;
500
                    }
501
502
                    $vars[$paramCount]            = array();
503
                    $vars[$paramCount]['token']   = $currVar;
504
                    $vars[$paramCount]['name']    = $tokens[$currVar]['content'];
505
                    $vars[$paramCount]['content'] = trim($phpcsFile->getTokensAsString($paramStart, ($i - $paramStart)));
506
507
                    if ($defaultStart !== null) {
508
                        $vars[$paramCount]['default'] = trim(
509
                            $phpcsFile->getTokensAsString(
510
                                $defaultStart,
511
                                ($i - $defaultStart)
512
                            )
513
                        );
514
                    }
515
516
                    $vars[$paramCount]['pass_by_reference'] = $passByReference;
517
                    $vars[$paramCount]['variable_length']   = $variableLength;
518
                    $vars[$paramCount]['type_hint']         = $typeHint;
519
                    $vars[$paramCount]['type_hint_token']   = $typeHintToken;
520
                    $vars[$paramCount]['nullable_type']     = $nullableType;
521
522
                    // Reset the vars, as we are about to process the next parameter.
523
                    $defaultStart    = null;
524
                    $paramStart      = ($i + 1);
525
                    $passByReference = false;
526
                    $variableLength  = false;
527
                    $typeHint        = '';
528
                    $typeHintToken   = false;
529
                    $nullableType    = false;
530
531
                    $paramCount++;
532
                    break;
533
                case 'T_EQUAL':
534
                    $defaultStart = ($i + 1);
535
                    break;
536
            }//end switch
537
        }//end for
538
539
        return $vars;
540
541
    }//end getMethodParameters()
542
}
543