Completed
Push — feature/isClassProperty-additi... ( 286703...879b22 )
by Juliette
01:45
created

PHPCSHelper   C

Complexity

Total Complexity 59

Size/Duplication

Total Lines 408
Duplicated Lines 8.09 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 59
lcom 1
cbo 1
dl 33
loc 408
rs 6.1904
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getVersion() 0 10 2
A setConfigData() 0 10 2
A getConfigData() 0 10 2
C findExtendedClassName() 6 47 9
C findImplementedInterfaceNames() 0 49 8
F getMethodParameters() 27 158 36

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PHPCSHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PHPCSHelper, and based on these observations, apply Extract Interface, too.

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 name of the class that the specified class extends
92
     * (works for classes, anonymous classes and interfaces).
93
     *
94
     * Returns FALSE on error or if there is no extended class name.
95
     *
96
     * {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File`
97
     * class, but with some improvements which have been introduced in
98
     * PHPCS 2.8.0.
99
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/commit/0011d448119d4c568e3ac1f825ae78815bf2cc34}.
100
     *
101
     * Once the minimum supported PHPCS version for this standard goes beyond
102
     * that, this method can be removed and calls to it replaced with
103
     * `$phpcsFile->findExtendedClassName($stackPtr)` calls.
104
     *
105
     * Last synced with PHPCS version: PHPCS 3.1.0-alpha at commit a9efcc9b0703f3f9f4a900623d4e97128a6aafc6}}
106
     *
107
     * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
108
     * @param int                   $stackPtr  The position of the class token in the stack.
109
     *
110
     * @return string|false
111
     */
112
    public static function findExtendedClassName(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
113
    {
114
        if (version_compare(self::getVersion(), '3.1.0', '>=') === true) {
115
            return $phpcsFile->findExtendedClassName($stackPtr);
116
        }
117
118
        $tokens = $phpcsFile->getTokens();
119
120
        // Check for the existence of the token.
121
        if (isset($tokens[$stackPtr]) === false) {
122
            return false;
123
        }
124
125 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_CLASS
126
            && $tokens[$stackPtr]['type'] !== 'T_ANON_CLASS'
127
            && $tokens[$stackPtr]['type'] !== 'T_INTERFACE'
128
        ) {
129
            return false;
130
        }
131
132
        if (isset($tokens[$stackPtr]['scope_closer']) === false) {
133
            return false;
134
        }
135
136
        $classCloserIndex = $tokens[$stackPtr]['scope_closer'];
137
        $extendsIndex     = $phpcsFile->findNext(T_EXTENDS, $stackPtr, $classCloserIndex);
138
        if (false === $extendsIndex) {
139
            return false;
140
        }
141
142
        $find = array(
143
            T_NS_SEPARATOR,
144
            T_STRING,
145
            T_WHITESPACE,
146
        );
147
148
        $end  = $phpcsFile->findNext($find, ($extendsIndex + 1), $classCloserIndex, true);
149
        $name = $phpcsFile->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
150
        $name = trim($name);
151
152
        if ($name === '') {
153
            return false;
154
        }
155
156
        return $name;
157
158
    }//end findExtendedClassName()
159
160
161
    /**
162
     * Returns the name(s) of the interface(s) that the specified class implements.
163
     *
164
     * Returns FALSE on error or if there are no implemented interface names.
165
     *
166
     * {@internal Duplicate of same method as introduced in PHPCS 2.7.
167
     * This method also includes an improvement we use which was only introduced
168
     * in PHPCS 2.8.0, so only defer to upstream for higher versions.
169
     * Once the minimum supported PHPCS version for this sniff library goes beyond
170
     * that, this method can be removed and calls to it replaced with
171
     * `$phpcsFile->findImplementedInterfaceNames($stackPtr)` calls.}}
172
     *
173
     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
174
     * @param int                   $stackPtr  The position of the class token.
175
     *
176
     * @return array|false
177
     */
178
    public static function findImplementedInterfaceNames(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
179
    {
180
        if (version_compare(self::getVersion(), '2.7.1', '>') === true) {
181
            return $phpcsFile->findImplementedInterfaceNames($stackPtr);
182
        }
183
184
        $tokens = $phpcsFile->getTokens();
185
186
        // Check for the existence of the token.
187
        if (isset($tokens[$stackPtr]) === false) {
188
            return false;
189
        }
190
191
        if ($tokens[$stackPtr]['code'] !== T_CLASS
192
            && $tokens[$stackPtr]['type'] !== 'T_ANON_CLASS'
193
        ) {
194
            return false;
195
        }
196
197
        if (isset($tokens[$stackPtr]['scope_closer']) === false) {
198
            return false;
199
        }
200
201
        $classOpenerIndex = $tokens[$stackPtr]['scope_opener'];
202
        $implementsIndex  = $phpcsFile->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
203
        if ($implementsIndex === false) {
204
            return false;
205
        }
206
207
        $find = array(
208
            T_NS_SEPARATOR,
209
            T_STRING,
210
            T_WHITESPACE,
211
            T_COMMA,
212
        );
213
214
        $end  = $phpcsFile->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
215
        $name = $phpcsFile->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
216
        $name = trim($name);
217
218
        if ($name === '') {
219
            return false;
220
        } else {
221
            $names = explode(',', $name);
222
            $names = array_map('trim', $names);
223
            return $names;
224
        }
225
226
    }//end findImplementedInterfaceNames()
227
228
229
    /**
230
     * Returns the method parameters for the specified function token.
231
     *
232
     * Each parameter is in the following format:
233
     *
234
     * <code>
235
     *   0 => array(
236
     *         'token'             => int,     // The position of the var in the token stack.
237
     *         'name'              => '$var',  // The variable name.
238
     *         'content'           => string,  // The full content of the variable definition.
239
     *         'pass_by_reference' => boolean, // Is the variable passed by reference?
240
     *         'variable_length'   => boolean, // Is the param of variable length through use of `...` ?
241
     *         'type_hint'         => string,  // The type hint for the variable.
242
     *         'nullable_type'     => boolean, // Is the variable using a nullable type?
243
     *        )
244
     * </code>
245
     *
246
     * Parameters with default values have an additional array index of
247
     * 'default' with the value of the default as a string.
248
     *
249
     * {@internal Duplicate of same method as contained in the `\PHP_CodeSniffer_File`
250
     * class, but with some improvements which have been introduced in
251
     * PHPCS 2.8.0.
252
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1117},
253
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1193} and
254
     * {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/1293}.
255
     *
256
     * Once the minimum supported PHPCS version for this standard goes beyond
257
     * that, this method can be removed and calls to it replaced with
258
     * `$phpcsFile->getMethodParameters($stackPtr)` calls.
259
     *
260
     * NOTE: This version does not deal with the new T_NULLABLE token type.
261
     * This token is included upstream only in 2.8.0+ and as we defer to upstream
262
     * in that case, no need to deal with it here.
263
     *
264
     * Last synced with PHPCS version: PHPCS 2.9.0-alpha at commit f1511adad043edfd6d2e595e77385c32577eb2bc}}
265
     *
266
     * @param \PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
267
     * @param int                   $stackPtr  The position in the stack of the
268
     *                                         function token to acquire the
269
     *                                         parameters for.
270
     *
271
     * @return array|false
272
     * @throws \PHP_CodeSniffer_Exception If the specified $stackPtr is not of
273
     *                                    type T_FUNCTION or T_CLOSURE.
274
     */
275
    public static function getMethodParameters(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
276
    {
277
        if (version_compare(self::getVersion(), '2.7.1', '>') === true) {
278
            return $phpcsFile->getMethodParameters($stackPtr);
279
        }
280
281
        $tokens = $phpcsFile->getTokens();
282
283
        // Check for the existence of the token.
284
        if (isset($tokens[$stackPtr]) === false) {
285
            return false;
286
        }
287
288 View Code Duplication
        if ($tokens[$stackPtr]['code'] !== T_FUNCTION && $tokens[$stackPtr]['code'] !== T_CLOSURE) {
289
            throw new \PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
290
        }
291
292
        $opener = $tokens[$stackPtr]['parenthesis_opener'];
293
        $closer = $tokens[$stackPtr]['parenthesis_closer'];
294
295
        $vars            = array();
296
        $currVar         = null;
297
        $paramStart      = ($opener + 1);
298
        $defaultStart    = null;
299
        $paramCount      = 0;
300
        $passByReference = false;
301
        $variableLength  = false;
302
        $typeHint        = '';
303
        $nullableType    = false;
304
305
        for ($i = $paramStart; $i <= $closer; $i++) {
306
            // Check to see if this token has a parenthesis or bracket opener. If it does
307
            // it's likely to be an array which might have arguments in it. This
308
            // could cause problems in our parsing below, so lets just skip to the
309
            // end of it.
310 View Code Duplication
            if (isset($tokens[$i]['parenthesis_opener']) === true) {
311
                // Don't do this if it's the close parenthesis for the method.
312
                if ($i !== $tokens[$i]['parenthesis_closer']) {
313
                    $i = ($tokens[$i]['parenthesis_closer'] + 1);
314
                }
315
            }
316
317 View Code Duplication
            if (isset($tokens[$i]['bracket_opener']) === true) {
318
                // Don't do this if it's the close parenthesis for the method.
319
                if ($i !== $tokens[$i]['bracket_closer']) {
320
                    $i = ($tokens[$i]['bracket_closer'] + 1);
321
                }
322
            }
323
324
            switch ($tokens[$i]['type']) {
325
                case 'T_BITWISE_AND':
326
                    $passByReference = true;
327
                    break;
328
                case 'T_VARIABLE':
329
                    $currVar = $i;
330
                    break;
331
                case 'T_ELLIPSIS':
332
                    $variableLength = true;
333
                    break;
334
                case 'T_ARRAY_HINT':
335
                case 'T_CALLABLE':
336
                    $typeHint .= $tokens[$i]['content'];
337
                    break;
338
                case 'T_SELF':
339
                case 'T_PARENT':
340
                case 'T_STATIC':
341
                    // Self and parent are valid, static invalid, but was probably intended as type hint.
342
                    if (isset($defaultStart) === false) {
343
                        $typeHint .= $tokens[$i]['content'];
344
                    }
345
                    break;
346
                case 'T_STRING':
347
                    // This is a string, so it may be a type hint, but it could
348
                    // also be a constant used as a default value.
349
                    $prevComma = false;
350 View Code Duplication
                    for ($t = $i; $t >= $opener; $t--) {
351
                        if ($tokens[$t]['code'] === T_COMMA) {
352
                            $prevComma = $t;
353
                            break;
354
                        }
355
                    }
356
357
                    if ($prevComma !== false) {
358
                        $nextEquals = false;
359 View Code Duplication
                        for ($t = $prevComma; $t < $i; $t++) {
360
                            if ($tokens[$t]['code'] === T_EQUAL) {
361
                                $nextEquals = $t;
362
                                break;
363
                            }
364
                        }
365
366
                        if ($nextEquals !== false) {
367
                            break;
368
                        }
369
                    }
370
371
                    if ($defaultStart === null) {
372
                        $typeHint .= $tokens[$i]['content'];
373
                    }
374
                    break;
375
                case 'T_NS_SEPARATOR':
376
                    // Part of a type hint or default value.
377
                    if ($defaultStart === null) {
378
                        $typeHint .= $tokens[$i]['content'];
379
                    }
380
                    break;
381
                case 'T_INLINE_THEN':
382
                    if ($defaultStart === null) {
383
                        $nullableType = true;
384
                        $typeHint    .= $tokens[$i]['content'];
385
                    }
386
                    break;
387
                case 'T_CLOSE_PARENTHESIS':
388
                case 'T_COMMA':
389
                    // If it's null, then there must be no parameters for this
390
                    // method.
391
                    if ($currVar === null) {
392
                        continue;
393
                    }
394
395
                    $vars[$paramCount]            = array();
396
                    $vars[$paramCount]['token']   = $currVar;
397
                    $vars[$paramCount]['name']    = $tokens[$currVar]['content'];
398
                    $vars[$paramCount]['content'] = trim($phpcsFile->getTokensAsString($paramStart, ($i - $paramStart)));
399
400
                    if ($defaultStart !== null) {
401
                        $vars[$paramCount]['default'] = trim(
402
                            $phpcsFile->getTokensAsString(
403
                                $defaultStart,
404
                                ($i - $defaultStart)
405
                            )
406
                        );
407
                    }
408
409
                    $vars[$paramCount]['pass_by_reference'] = $passByReference;
410
                    $vars[$paramCount]['variable_length']   = $variableLength;
411
                    $vars[$paramCount]['type_hint']         = $typeHint;
412
                    $vars[$paramCount]['nullable_type']     = $nullableType;
413
414
                    // Reset the vars, as we are about to process the next parameter.
415
                    $defaultStart    = null;
416
                    $paramStart      = ($i + 1);
417
                    $passByReference = false;
418
                    $variableLength  = false;
419
                    $typeHint        = '';
420
                    $nullableType    = false;
421
422
                    $paramCount++;
423
                    break;
424
                case 'T_EQUAL':
425
                    $defaultStart = ($i + 1);
426
                    break;
427
            }//end switch
428
        }//end for
429
430
        return $vars;
431
432
    }//end getMethodParameters()
433
}
434