Completed
Pull Request — master (#222)
by Juliette
02:53
created

PHPCompatibility_Sniff::isNamespaced()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
/**
3
 * PHPCompatibility_Sniff.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @category  PHP
8
 * @package   PHPCompatibility
9
 * @author    Wim Godden <[email protected]>
10
 * @copyright 2014 Cu.be Solutions bvba
11
 */
12
13
/**
14
 * PHPCompatibility_Sniff.
15
 *
16
 * @category  PHP
17
 * @package   PHPCompatibility
18
 * @author    Wim Godden <[email protected]>
19
 * @version   1.1.0
20
 * @copyright 2014 Cu.be Solutions bvba
21
 */
22
abstract class PHPCompatibility_Sniff implements PHP_CodeSniffer_Sniff
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
/* The testVersion configuration variable may be in any of the following formats:
26
 * 1) Omitted/empty, in which case no version is specified.  This effectively
27
 *    disables all the checks provided by this standard.
28
 * 2) A single PHP version number, e.g. "5.4" in which case the standard checks that
29
 *    the code will run on that version of PHP (no deprecated features or newer
30
 *    features being used).
31
 * 3) A range, e.g. "5.0-5.5", in which case the standard checks the code will run
32
 *    on all PHP versions in that range, and that it doesn't use any features that
33
 *    were deprecated by the final version in the list, or which were not available
34
 *    for the first version in the list.
35
 * PHP version numbers should always be in Major.Minor format.  Both "5", "5.3.2"
36
 * would be treated as invalid, and ignored.
37
 * This standard doesn't support checking against PHP4, so the minimum version that
38
 * is recognised is "5.0".
39
 */
40
41
    private function getTestVersion()
42
    {
43
        /**
44
         * var $arrTestVersions will hold an array containing min/max version of PHP
45
         *   that we are checking against (see above).  If only a single version
46
         *   number is specified, then this is used as both the min and max.
47
         */
48
        static $arrTestVersions = array();
49
50
        $testVersion = trim(PHP_CodeSniffer::getConfigData('testVersion'));
51
52
        if (!isset($arrTestVersions[$testVersion]) && !empty($testVersion)) {
53
54
            $arrTestVersions[$testVersion] = array(null, null);
55
            if (preg_match('/^\d+\.\d+$/', $testVersion)) {
56
                $arrTestVersions[$testVersion] = array($testVersion, $testVersion);
57
            }
58
            elseif (preg_match('/^(\d+\.\d+)\s*-\s*(\d+\.\d+)$/', $testVersion,
59
                               $matches))
60
            {
61
                if (version_compare($matches[1], $matches[2], '>')) {
62
                    trigger_error("Invalid range in testVersion setting: '"
63
                                  . $testVersion . "'", E_USER_WARNING);
64
                }
65
                else {
66
                    $arrTestVersions[$testVersion] = array($matches[1], $matches[2]);
67
                }
68
            }
69
            elseif (!$testVersion == '') {
70
                trigger_error("Invalid testVersion setting: '" . $testVersion
71
                              . "'", E_USER_WARNING);
72
            }
73
        }
74
75
        if (isset($arrTestVersions[$testVersion])) {
76
            return $arrTestVersions[$testVersion];
77
        }
78
        else {
79
			return array(null, null);
80
        }
81
    }
82
83 View Code Duplication
    public function supportsAbove($phpVersion)
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...
84
    {
85
        $testVersion = $this->getTestVersion();
86
        $testVersion = $testVersion[1];
87
88
        if (is_null($testVersion)
89
            || version_compare($testVersion, $phpVersion) >= 0
90
        ) {
91
            return true;
92
        } else {
93
            return false;
94
        }
95
    }//end supportsAbove()
96
97 View Code Duplication
    public function supportsBelow($phpVersion)
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...
98
    {
99
        $testVersion = $this->getTestVersion();
100
        $testVersion = $testVersion[0];
101
102
        if (!is_null($testVersion)
103
            && version_compare($testVersion, $phpVersion) <= 0
104
        ) {
105
            return true;
106
        } else {
107
            return false;
108
        }
109
    }//end supportsBelow()
110
111
    /**
112
     * Returns the name(s) of the interface(s) that the specified class implements.
113
     *
114
     * Returns FALSE on error or if there are no implemented interface names.
115
     *
116
     * {@internal Duplicate of same method as introduced in PHPCS 2.7.
117
     * Once the minimum supported PHPCS version for this sniff library goes beyond
118
     * that, this method can be removed and call to it replaced with
119
     * `$phpcsFile->findImplementedInterfaceNames($stackPtr)` calls.}}
120
     *
121
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
122
     * @param int                  $stackPtr  The position of the class token.
123
     *
124
     * @return array|false
125
     */
126
    public function findImplementedInterfaceNames(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
127
    {
128
        if (method_exists($phpcsFile, 'findImplementedInterfaceNames')) {
129
            return $phpcsFile->findImplementedInterfaceNames($stackPtr);
130
        }
131
132
        $tokens = $phpcsFile->getTokens();
133
134
        // Check for the existence of the token.
135
        if (isset($tokens[$stackPtr]) === false) {
136
            return false;
137
        }
138
139
        if ($tokens[$stackPtr]['code'] !== T_CLASS) {
140
            return false;
141
        }
142
143
        if (isset($tokens[$stackPtr]['scope_closer']) === false) {
144
            return false;
145
        }
146
147
        $classOpenerIndex = $tokens[$stackPtr]['scope_opener'];
148
        $implementsIndex  = $phpcsFile->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
149
        if ($implementsIndex === false) {
150
            return false;
151
        }
152
153
        $find = array(
154
                 T_NS_SEPARATOR,
155
                 T_STRING,
156
                 T_WHITESPACE,
157
                 T_COMMA,
158
                );
159
160
        $end  = $phpcsFile->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
161
        $name = $phpcsFile->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
162
        $name = trim($name);
163
164
        if ($name === '') {
165
            return false;
166
        } else {
167
            $names = explode(',', $name);
168
            $names = array_map('trim', $names);
169
            return $names;
170
        }
171
172
    }//end findImplementedInterfaceNames()
173
174
175
    /**
176
     * Checks if a function call has parameters.
177
     *
178
     * Expects to be passed the T_STRING stack pointer for the function call.
179
     * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
180
     *
181
     * @link https://github.com/wimg/PHPCompatibility/issues/120
182
     * @link https://github.com/wimg/PHPCompatibility/issues/152
183
     *
184
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
185
     * @param int                  $stackPtr  The position of the function call token.
186
     *
187
     * @return bool
188
     */
189
    public function doesFunctionCallHaveParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
190
    {
191
        $tokens = $phpcsFile->getTokens();
192
193
        // Check for the existence of the token.
194
        if (isset($tokens[$stackPtr]) === false) {
195
            return false;
196
        }
197
198
        if ($tokens[$stackPtr]['code'] !== T_STRING) {
199
            return false;
200
        }
201
202
        // Next non-empty token should be the open parenthesis.
203
        $openParenthesis = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
204
        if ($openParenthesis === false || $tokens[$openParenthesis]['code'] !== T_OPEN_PARENTHESIS) {
205
            return false;
206
        }
207
208
        if (isset($tokens[$openParenthesis]['parenthesis_closer']) === false) {
209
            return false;
210
        }
211
212
        $closeParenthesis = $tokens[$openParenthesis]['parenthesis_closer'];
213
        $nextNonEmpty     = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $openParenthesis + 1, $closeParenthesis + 1, true);
214
215
        if ($nextNonEmpty === $closeParenthesis) {
216
            // No parameters.
217
            return false;
218
        }
219
220
        return true;
221
    }
222
223
224
    /**
225
     * Count the number of parameters a function call has been passed.
226
     *
227
     * Expects to be passed the T_STRING stack pointer for the function call.
228
     * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
229
     *
230
     * @link https://github.com/wimg/PHPCompatibility/issues/111
231
     * @link https://github.com/wimg/PHPCompatibility/issues/114
232
     * @link https://github.com/wimg/PHPCompatibility/issues/151
233
     *
234
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
235
     * @param int                  $stackPtr  The position of the function call token.
236
     *
237
     * @return int
238
     */
239
    public function getFunctionCallParameterCount(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
240
    {
241
        if ($this->doesFunctionCallHaveParameters($phpcsFile, $stackPtr) === false) {
242
            return 0;
243
        }
244
245
        return count($this->getFunctionCallParameters($phpcsFile, $stackPtr));
246
    }
247
248
249
    /**
250
     * Get information on all parameters passed to a function call.
251
     *
252
     * Expects to be passed the T_STRING stack pointer for the function call.
253
     * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
254
     *
255
     * Will return an multi-dimentional array with the start token pointer, end token
256
     * pointer and raw parameter value for all parameters. Index will be 1-based.
257
     * If no parameters are found, will return an empty array.
258
     *
259
     * @param PHP_CodeSniffer_File $phpcsFile     The file being scanned.
260
     * @param int                  $stackPtr      The position of the function call token.
261
     *
262
     * @return array
263
     */
264
    public function getFunctionCallParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
265
    {
266
        if ($this->doesFunctionCallHaveParameters($phpcsFile, $stackPtr) === false) {
267
            return array();
268
        }
269
270
        // Ok, we know we have a T_STRING with parameters and valid open & close parenthesis.
271
        $tokens = $phpcsFile->getTokens();
272
273
        $openParenthesis  = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
274
        $closeParenthesis = $tokens[$openParenthesis]['parenthesis_closer'];
275
276
        // Which nesting level is the one we are interested in ?
277
        $nestedParenthesisCount = 1;
278 View Code Duplication
        if (isset($tokens[$openParenthesis]['nested_parenthesis'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
279
            $nestedParenthesisCount = count($tokens[$openParenthesis]['nested_parenthesis']) + 1;
280
        }
281
282
        $parameters = array();
283
        $nextComma  = $openParenthesis;
284
        $paramStart = $openParenthesis + 1;
285
        $cnt        = 1;
286
        while ($nextComma = $phpcsFile->findNext(array(T_COMMA, T_CLOSE_PARENTHESIS, T_OPEN_SHORT_ARRAY), $nextComma + 1, $closeParenthesis + 1)) {
287
            // Ignore anything within short array definition brackets.
288
            if (
289
                $tokens[$nextComma]['type'] === 'T_OPEN_SHORT_ARRAY'
290
                &&
291
                ( isset($tokens[$nextComma]['bracket_opener']) && $tokens[$nextComma]['bracket_opener'] === $nextComma )
292
                &&
293
                isset($tokens[$nextComma]['bracket_closer'])
294
            ) {
295
                // Skip forward to the end of the short array definition.
296
                $nextComma = $tokens[$nextComma]['bracket_closer'];
297
                continue;
298
            }
299
300
            // Ignore comma's at a lower nesting level.
301
            if (
302
                $tokens[$nextComma]['type'] === 'T_COMMA'
303
                &&
304
                isset($tokens[$nextComma]['nested_parenthesis'])
305
                &&
306
                count($tokens[$nextComma]['nested_parenthesis']) !== $nestedParenthesisCount
307
            ) {
308
                continue;
309
            }
310
311
            // Ignore closing parenthesis if not 'ours'.
312
            if ($tokens[$nextComma]['type'] === 'T_CLOSE_PARENTHESIS' && $nextComma !== $closeParenthesis) {
313
                continue;
314
            }
315
316
            // Ok, we've reached the end of the parameter.
317
            $parameters[$cnt]['start'] = $paramStart;
318
            $parameters[$cnt]['end']   = $nextComma - 1;
319
            $parameters[$cnt]['raw']   = trim($phpcsFile->getTokensAsString($paramStart, ($nextComma - $paramStart)));
320
321
            // Check if there are more tokens before the closing parenthesis.
322
            // Prevents code like the following from setting a third parameter:
323
            // functionCall( $param1, $param2, );
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
324
            $hasNextParam = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $nextComma + 1, $closeParenthesis, true, null, true);
325
            if ($hasNextParam === false) {
326
                break;
327
            }
328
329
            // Prepare for the next parameter.
330
            $paramStart = $nextComma + 1;
331
            $cnt++;
332
        }
333
334
        return $parameters;
335
    }
336
337
338
    /**
339
     * Get information on a specific parameter passed to a function call.
340
     *
341
     * Expects to be passed the T_STRING stack pointer for the function call.
342
     * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
343
     *
344
     * Will return a array with the start token pointer, end token pointer and the raw value
345
     * of the parameter at a specific offset.
346
     * If the specified parameter is not found, will return false.
347
     *
348
     * @param PHP_CodeSniffer_File $phpcsFile   The file being scanned.
349
     * @param int                  $stackPtr    The position of the function call token.
350
     * @param int                  $paramOffset The 1-based index position of the parameter to retrieve.
351
     *
352
     * @return array|false
353
     */
354
    public function getFunctionCallParameter(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $paramOffset)
355
    {
356
        $parameters = $this->getFunctionCallParameters($phpcsFile, $stackPtr);
357
358
        if (isset($parameters[$paramOffset]) === false) {
359
            return false;
360
        }
361
        else {
362
            return $parameters[$paramOffset];
363
        }
364
    }
365
366
367
    /**
368
     * Verify whether a token is within a scoped condition.
369
     *
370
     * If the optional $validScopes parameter has been passed, the function
371
     * will check that the token has at least one condition which is of a
372
     * type defined in $validScopes.
373
     *
374
     * @param PHP_CodeSniffer_File $phpcsFile   The file being scanned.
375
     * @param int                  $stackPtr    The position of the token.
376
     * @param array|int            $validScopes Optional. Array of valid scopes
377
     *                                          or int value of a valid scope.
378
     *
379
     * @return bool Without the optional $scopeTypes: True if within a scope, false otherwise.
380
     *              If the $scopeTypes are set: True if *one* of the conditions is a
381
     *              valid scope, false otherwise.
382
     */
383
    public function tokenHasScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $validScopes = null)
384
    {
385
        $tokens = $phpcsFile->getTokens();
386
387
        // Check for the existence of the token.
388
        if (isset($tokens[$stackPtr]) === false) {
389
            return false;
390
        }
391
392
        // No conditions = no scope.
393
        if (empty($tokens[$stackPtr]['conditions'])) {
394
            return false;
395
        }
396
397
        // Ok, there are conditions, do we have to check for specific ones ?
398
        if (isset($validScopes) === false) {
399
            return true;
400
        }
401
402
        if (is_int($validScopes)) {
403
            // Received an integer, so cast to array.
404
            $validScopes = (array) $validScopes;
405
        }
406
407
        if (empty($validScopes) || is_array($validScopes) === false) {
408
            // No valid scope types received, so will not comply.
409
            return false;
410
        }
411
412
        // Check for required scope types.
413
        foreach ($tokens[$stackPtr]['conditions'] as $pointer => $type) {
414
            if (in_array($type, $validScopes, true)) {
415
                return true;
416
            }
417
        }
418
419
        return false;
420
    }
421
422
423
    /**
424
     * Verify whether a token is within a class scope.
425
     *
426
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
427
     * @param int                  $stackPtr  The position of the token.
428
     * @param bool                 $strict    Whether to strictly check for the T_CLASS
429
     *                                        scope or also accept interfaces and traits
430
     *                                        as scope.
431
     *
432
     * @return bool True if within class scope, false otherwise.
433
     */
434
    public function inClassScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $strict = true)
435
    {
436
        $validScopes = array(T_CLASS);
437
        if ($strict === false) {
438
            $validScopes[] = T_INTERFACE;
439
            $validScopes[] = T_TRAIT;
440
        }
441
442
        return $this->tokenHasScope($phpcsFile, $stackPtr, $validScopes);
443
    }
444
445
446
    /**
447
     * Returns the fully qualified class name for a new class instantiation.
448
     *
449
     * Returns an empty string if the class name could not be reliably inferred.
450
     *
451
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
452
     * @param int                  $stackPtr  The position of a T_NEW token.
453
     *
454
     * @return string
455
     */
456
    public function getFQClassNameFromNewToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
457
    {
458
        $tokens = $phpcsFile->getTokens();
459
460
        // Check for the existence of the token.
461
        if (isset($tokens[$stackPtr]) === false) {
462
            return '';
463
        }
464
465
        if ($tokens[$stackPtr]['code'] !== T_NEW) {
466
            return '';
467
        }
468
469
        $find = array(
470
                 T_NS_SEPARATOR,
471
                 T_STRING,
472
                 T_NAMESPACE,
473
                 T_WHITESPACE,
474
                );
475
476
        $start = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
477
        // Bow out if the next token is a variable as we don't know where it was defined.
478
        if ($tokens[$start]['code'] === T_VARIABLE) {
479
            return '';
480
        }
481
482
        $end       = $phpcsFile->findNext($find, ($start + 1), null, true, null, true);
483
        $className = $phpcsFile->getTokensAsString($start, ($end - $start));
484
        $className = trim($className);
485
486
        return $this->getFQName($phpcsFile, $stackPtr, $className);
487
    }
488
489
490
    /**
491
     * Returns the fully qualified name of the class that the specified class extends.
492
     *
493
     * Returns an empty string if the class does not extend another class or if
494
     * the class name could not be reliably inferred.
495
     *
496
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
497
     * @param int                  $stackPtr  The position of a T_CLASS token.
498
     *
499
     * @return string
500
     */
501
    public function getFQExtendedClassName(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
502
    {
503
        $tokens = $phpcsFile->getTokens();
504
505
        // Check for the existence of the token.
506
        if (isset($tokens[$stackPtr]) === false) {
507
            return '';
508
        }
509
510
        if ($tokens[$stackPtr]['code'] !== T_CLASS) {
511
            return '';
512
        }
513
514
        $extends = $phpcsFile->findExtendedClassName($stackPtr);
515
        if (empty($extends) || is_string($extends) === false) {
516
            return '';
517
        }
518
519
        return $this->getFQName($phpcsFile, $stackPtr, $extends);
520
    }
521
522
523
    /**
524
     * Returns the class name for the static usage of a class.
525
     * This can be a call to a method, the use of a property or constant.
526
     *
527
     * Returns an empty string if the class name could not be reliably inferred.
528
     *
529
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
530
     * @param int                  $stackPtr  The position of a T_NEW token.
531
     *
532
     * @return string
533
     */
534
    public function getFQClassNameFromDoubleColonToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
535
    {
536
        $tokens = $phpcsFile->getTokens();
537
538
        // Check for the existence of the token.
539
        if (isset($tokens[$stackPtr]) === false) {
540
            return '';
541
        }
542
543
        if ($tokens[$stackPtr]['code'] !== T_DOUBLE_COLON) {
544
            return '';
545
        }
546
547
        // Nothing to do if previous token is a variable as we don't know where it was defined.
548
        if ($tokens[$stackPtr - 1]['code'] === T_VARIABLE) {
549
            return '';
550
        }
551
552
        // Nothing to do if 'parent' or 'static' as we don't know how far the class tree extends.
553
        if (in_array($tokens[$stackPtr - 1]['code'], array(T_PARENT, T_STATIC), true)) {
554
            return '';
555
        }
556
557
        // Get the classname from the class declaration if self is used.
558
        if ($tokens[$stackPtr - 1]['code'] === T_SELF) {
559
            $classDeclarationPtr = $phpcsFile->findPrevious(T_CLASS, $stackPtr - 1);
560
            if ($classDeclarationPtr === false) {
561
				return '';
562
			}
563
            $className = $phpcsFile->getDeclarationName($classDeclarationPtr);
564
            return $this->getFQName($phpcsFile, $classDeclarationPtr, $className);
565
        }
566
567
        $find = array(
568
                 T_NS_SEPARATOR,
569
                 T_STRING,
570
                 T_NAMESPACE,
571
                 T_WHITESPACE,
572
                );
573
574
        $start     = ($phpcsFile->findPrevious($find, $stackPtr - 1, null, true, null, true) + 1);
575
        $className = $phpcsFile->getTokensAsString($start, ($stackPtr - $start));
576
        $className = trim($className);
577
578
        return $this->getFQName($phpcsFile, $stackPtr, $className);
579
    }
580
581
582
    /**
583
     * Get the Fully Qualified name for a class/function/constant etc.
584
     *
585
     * Checks if a class/function/constant name is already fully qualified and
586
     * if not, enrich it with the relevant namespace information.
587
     *
588
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
589
     * @param int                  $stackPtr  The position of the token.
590
     * @param string               $name      The class / function / constant name.
591
     *
592
     * @return string
593
     */
594
    public function getFQName(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $name)
595
    {
596
        if (strpos($name, '\\' ) === 0) {
597
            // Already fully qualified.
598
            return $name;
599
        }
600
601
        // Remove the namespace keyword if used.
602
        if (strpos($name, 'namespace\\') === 0) {
603
            $name = substr($name, 10);
604
        }
605
606
        $namespace = $this->determineNamespace($phpcsFile, $stackPtr);
607
608
        if ($namespace === '') {
609
            return '\\' . $name;
610
        }
611
        else {
612
            return '\\' . $namespace . '\\' . $name;
613
        }
614
    }
615
616
617
    /**
618
     * Is the class/function/constant name namespaced or global ?
619
     *
620
     * @param string $FQName Fully Qualified name of a class, function etc.
621
     *                       I.e. should always start with a `\` !
622
     *
623
     * @return bool True if namespaced, false if global.
624
     */
625
    public function isNamespaced($FQName) {
626
        if (strpos($FQName, '\\') !== 0) {
627
            throw new PHP_CodeSniffer_Exception('$FQName must be a fully qualified name');
628
        }
629
630
        return (strpos(substr($FQName, 1), '\\') !== false);
631
    }
632
633
634
    /**
635
     * Determine the namespace name an arbitrary token lives in.
636
     *
637
     * @param PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
638
     * @param int                  $stackPtr  The token position for which to determine the namespace.
639
     *
640
     * @return string Namespace name or empty string if it couldn't be determined or no namespace applies.
641
     */
642
    public function determineNamespace(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
643
    {
644
        $tokens = $phpcsFile->getTokens();
645
646
        // Check for the existence of the token.
647
        if (isset($tokens[$stackPtr]) === false) {
648
            return '';
649
        }
650
651
        // Check for scoped namespace {}.
652
        if (empty($tokens[$stackPtr]['conditions']) === false) {
653
            foreach ($tokens[$stackPtr]['conditions'] as $pointer => $type) {
654
                if ($type === T_NAMESPACE) {
655
                    $namespace = $this->getDeclaredNamespaceName($phpcsFile, $pointer);
656
                    if ($namespace !== false) {
657
                        return $namespace;
658
                    }
659
                }
660
                break; // We only need to check the highest level condition.
661
            }
662
        }
663
664
        /*
665
         * Not in a scoped namespace, so let's see if we can find a non-scoped namespace instead.
666
         * Keeping in mind that:
667
         * - there can be multiple non-scoped namespaces in a file (bad practice, but it happens).
668
         * - the namespace keyword can also be used as part of a function/method call and such.
669
         * - that a non-named namespace resolves to the global namespace.
670
         */
671
        $previousNSToken = $stackPtr;
672
        $namespace       = false;
673
        do {
674
            $previousNSToken = $phpcsFile->findPrevious(T_NAMESPACE, $previousNSToken -1);
675
676
            // Stop if we encounter a scoped namespace declaration as we already know we're not in one.
677
            if (empty($tokens[$previousNSToken]['scope_condition']) === false && $tokens[$previousNSToken]['scope_condition'] = $previousNSToken) {
678
                break;
679
            }
680
            $namespace = $this->getDeclaredNamespaceName($phpcsFile, $previousNSToken);
681
682
        } while ($namespace === false && $previousNSToken !== false);
683
684
        // If we still haven't got a namespace, return an empty string.
685
        if ($namespace === false) {
686
            return '';
687
        }
688
        else {
689
            return $namespace;
690
        }
691
    }
692
693
    /**
694
     * Get the complete namespace name for a namespace declaration.
695
     *
696
     * For hierarchical namespaces, the name will be composed of several tokens,
697
     * i.e. MyProject\Sub\Level which will be returned together as one string.
698
     *
699
     * @param PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
700
     * @param int|bool             $stackPtr  The position of a T_NAMESPACE token.
701
     *
702
     * @return string|false Namespace name or false if not a namespace declaration.
703
     *                      Namespace name can be an empty string for global namespace declaration.
704
     */
705
    public function getDeclaredNamespaceName(PHP_CodeSniffer_File $phpcsFile, $stackPtr )
706
    {
707
        $tokens = $phpcsFile->getTokens();
708
709
        // Check for the existence of the token.
710
        if ($stackPtr === false || isset($tokens[$stackPtr]) === false) {
711
            return false;
712
        }
713
714
        if ($tokens[$stackPtr]['code'] !== T_NAMESPACE) {
715
            return false;
716
        }
717
718
        if ($tokens[$stackPtr + 1]['code'] === T_NS_SEPARATOR) {
719
            // Not a namespace declaration, but use of, i.e. namespace\someFunction();
720
            return false;
721
        }
722
723
        $nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
724
        if ($tokens[$nextToken]['code'] === T_OPEN_CURLY_BRACKET) {
725
            // Declaration for global namespace when using multiple namespaces in a file.
726
            // I.e.: namespace {}
727
            return '';
728
        }
729
730
        // Ok, this should be a namespace declaration, so get all the parts together.
731
        $validTokens = array(
732
                        T_STRING,
733
                        T_NS_SEPARATOR,
734
                        T_WHITESPACE,
735
                       );
736
737
        $namespaceName = '';
738
        while(in_array($tokens[$nextToken]['code'], $validTokens, true) === true) {
739
            $namespaceName .= trim($tokens[$nextToken]['content']);
740
            $nextToken++;
741
        }
742
743
        return $namespaceName;
744
    }
745
746
747
    /**
748
     * Returns the method parameters for the specified T_FUNCTION token.
749
     *
750
     * Each parameter is in the following format:
751
     *
752
     * <code>
753
     *   0 => array(
754
     *         'name'              => '$var',  // The variable name.
755
     *         'pass_by_reference' => false,   // Passed by reference.
756
     *         'type_hint'         => string,  // Type hint for array or custom type
757
     *        )
758
     * </code>
759
     *
760
     * Parameters with default values have an additional array index of
761
     * 'default' with the value of the default as a string.
762
     *
763
     * {@internal Duplicate of same method as contained in the `PHP_CodeSniffer_File`
764
     * class, but with some improvements which were only introduced in PHPCS 2.7.
765
     * Once the minimum supported PHPCS version for this sniff library goes beyond
766
     * that, this method can be removed and calls to it replaced with
767
     * `$phpcsFile->getMethodParameters($stackPtr)` calls.}}
768
     *
769
     * @param PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
770
     * @param int $stackPtr The position in the stack of the T_FUNCTION token
771
     *                      to acquire the parameters for.
772
     *
773
     * @return array
774
     * @throws PHP_CodeSniffer_Exception If the specified $stackPtr is not of
775
     *                                   type T_FUNCTION.
776
     */
777
    public function getMethodParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
778
    {
779
        $tokens = $phpcsFile->getTokens();
780
781
        // Check for the existence of the token.
782
        if (isset($tokens[$stackPtr]) === false) {
783
            return false;
784
        }
785
786
        if ($tokens[$stackPtr]['code'] !== T_FUNCTION) {
787
            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION');
788
        }
789
790
        $opener = $tokens[$stackPtr]['parenthesis_opener'];
791
        $closer = $tokens[$stackPtr]['parenthesis_closer'];
792
793
        $vars            = array();
794
        $currVar         = null;
795
        $paramStart      = ($opener + 1);
796
        $defaultStart    = null;
797
        $paramCount      = 0;
798
        $passByReference = false;
799
        $variableLength  = false;
800
        $typeHint        = '';
801
802
        for ($i = $paramStart; $i <= $closer; $i++) {
803
            // Check to see if this token has a parenthesis or bracket opener. If it does
804
            // it's likely to be an array which might have arguments in it. This
805
            // could cause problems in our parsing below, so lets just skip to the
806
            // end of it.
807 View Code Duplication
            if (isset($tokens[$i]['parenthesis_opener']) === true) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
808
                // Don't do this if it's the close parenthesis for the method.
809
                if ($i !== $tokens[$i]['parenthesis_closer']) {
810
                    $i = ($tokens[$i]['parenthesis_closer'] + 1);
811
                }
812
            }
813
814 View Code Duplication
            if (isset($tokens[$i]['bracket_opener']) === true) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
815
                // Don't do this if it's the close parenthesis for the method.
816
                if ($i !== $tokens[$i]['bracket_closer']) {
817
                    $i = ($tokens[$i]['bracket_closer'] + 1);
818
                }
819
            }
820
821
            switch ($tokens[$i]['code']) {
822
            case T_BITWISE_AND:
823
                $passByReference = true;
824
                break;
825
            case T_VARIABLE:
826
                $currVar = $i;
827
                break;
828
            case T_ELLIPSIS:
829
                $variableLength = true;
830
                break;
831
            case T_ARRAY_HINT:
832
            case T_CALLABLE:
833
                $typeHint = $tokens[$i]['content'];
834
                break;
835
            case T_SELF:
836
            case T_PARENT:
837
            case T_STATIC:
838
                // Self is valid, the others invalid, but were probably intended as type hints.
839
                if (isset($defaultStart) === false) {
840
                    $typeHint = $tokens[$i]['content'];
841
                }
842
                break;
843
            case T_STRING:
844
                // This is a string, so it may be a type hint, but it could
845
                // also be a constant used as a default value.
846
                $prevComma = false;
847 View Code Duplication
                for ($t = $i; $t >= $opener; $t--) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
848
                    if ($tokens[$t]['code'] === T_COMMA) {
849
                        $prevComma = $t;
850
                        break;
851
                    }
852
                }
853
854
                if ($prevComma !== false) {
855
                    $nextEquals = false;
856 View Code Duplication
                    for ($t = $prevComma; $t < $i; $t++) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
857
                        if ($tokens[$t]['code'] === T_EQUAL) {
858
                            $nextEquals = $t;
859
                            break;
860
                        }
861
                    }
862
863
                    if ($nextEquals !== false) {
864
                        break;
865
                    }
866
                }
867
868
                if ($defaultStart === null) {
869
                    $typeHint .= $tokens[$i]['content'];
870
                }
871
                break;
872
            case T_NS_SEPARATOR:
873
                // Part of a type hint or default value.
874
                if ($defaultStart === null) {
875
                    $typeHint .= $tokens[$i]['content'];
876
                }
877
                break;
878
            case T_CLOSE_PARENTHESIS:
879
            case T_COMMA:
880
                // If it's null, then there must be no parameters for this
881
                // method.
882
                if ($currVar === null) {
883
                    continue;
884
                }
885
886
                $vars[$paramCount]         = array();
887
                $vars[$paramCount]['name'] = $tokens[$currVar]['content'];
888
889
                if ($defaultStart !== null) {
890
                    $vars[$paramCount]['default']
891
                        = $phpcsFile->getTokensAsString(
892
                            $defaultStart,
893
                            ($i - $defaultStart)
894
                        );
895
                }
896
897
                $rawContent = trim($phpcsFile->getTokensAsString($paramStart, ($i - $paramStart)));
898
899
                $vars[$paramCount]['pass_by_reference'] = $passByReference;
900
                $vars[$paramCount]['variable_length']   = $variableLength;
901
                $vars[$paramCount]['type_hint']         = $typeHint;
902
                $vars[$paramCount]['raw'] = $rawContent;
903
904
                // Reset the vars, as we are about to process the next parameter.
905
                $defaultStart    = null;
906
                $paramStart      = ($i + 1);
907
                $passByReference = false;
908
                $variableLength  = false;
909
                $typeHint        = '';
910
911
                $paramCount++;
912
                break;
913
            case T_EQUAL:
914
                $defaultStart = ($i + 1);
915
                break;
916
            }//end switch
917
        }//end for
918
919
        return $vars;
920
921
    }//end getMethodParameters()
922
923
}//end class
924