Completed
Push — master ( 53fb91...4f1650 )
by Bill
8s
created

IndentationLevelSniff::removeTokenScopes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
1
<?php
2
3
namespace Codor\Sniffs\Files;
4
5
use PHP_CodeSniffer_Sniff;
6
use PHP_CodeSniffer_File;
7
8
class IndentationLevelSniff implements PHP_CodeSniffer_Sniff
9
{
10
    /**
11
     * Indentations cannot be >= to this number.
12
     * @var integer
13
     */
14
    public $indentationLimit = 2;
15
16
    /**
17
     * The highest indentation level found.
18
     * @var integer
19
     */
20
    protected $maxIndentationFound;
21
22
    /**
23
     * Returns the token types that this sniff is interested in.
24
     * @return array
25
     */
26
    public function register()
27
    {
28
        return [T_FUNCTION, T_CLOSURE, T_SWITCH];
29
    }
30
31
    /**
32
     * Processes the tokens that this sniff is interested in.
33
     *
34
     * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found.
35
     * @param integer              $stackPtr  The position in the stack where
36
     *                                    the token was found.
37
     * @return void
38
     */
39
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
40
    {
41
        $tokens = $phpcsFile->getTokens();
42
        $token = $tokens[$stackPtr];
43
        $this->maxIndentationFound = 0;
44
45
        // Ignore functions with no body
46
        if (isset($token['scope_opener']) === false) {
47
            return;
48
        }
49
50
        $this->inspectScope($token, $tokens);
51
52
        if ($this->maxIndentationFound <= $this->indentationLimit) {
53
            return;
54
        }
55
56
        $phpcsFile->addError($this->getErrorMessage(), $stackPtr);
57
    }
58
59
    /**
60
     * Inspect the tokens in the scope of the provided $token.
61
     * @param  array $token  Token Data.
62
     * @param  array $tokens Tokens.
63
     * @return void
64
     */
65
    protected function inspectScope(array $token, array $tokens)
66
    {
67
        $start = $token['scope_opener'];
68
        $length = $token['scope_closer'] - $start + 1;
69
        $relativeScopeLevel = $tokens[$start]['level'];
70
71
        $scope = array_slice($tokens, $start, $length, true);
72
        $scope = $this->removeTokenScopes($scope, 'T_SWITCH');
73
74
        $this->maxIndentationFound = array_reduce($scope, function ($max, $nestedToken) use ($relativeScopeLevel) {
75
            $max = max($max, $nestedToken['level'] - $relativeScopeLevel);
76
            return $max;
77
        });
78
    }
79
80
    /**
81
     * Remove the bodies of given token type from the scope.
82
     * @param  array  $scope The tokens in a scope.
83
     * @param  string $type  The type of token to remove from the scope.
84
     * @return array  $scope The tokens scope without the removed tokens.
85
     */
86
    protected function removeTokenScopes(array $scope, $type)
87
    {
88
        $typeTokens = array_filter($scope, function ($token) use ($type) {
89
            return $token['type'] == $type;
90
        });
91
92
        foreach ($typeTokens as $token) {
93
            $range = array_flip(range($token['scope_opener'], $token['scope_closer']));
94
            $scope = array_diff_key($scope, $range);
95
        }
96
97
        return $scope;
98
    }
99
100
    /**
101
     * Produce the error message.
102
     * @return string
103
     */
104
    protected function getErrorMessage()
105
    {
106
        // Hack to fix the output numbers for the
107
        // indentation levels found and the
108
        // indentation limit.
109
        $indentationFound = $this->maxIndentationFound - 1;
110
        $indentationLimit = $this->indentationLimit - 1;
111
        return "{$indentationFound} indentation levels found. " .
112
        "Maximum of {$indentationLimit} indentation levels allowed.";
113
    }
114
}
115