ChangedConcatOperatorPrecedenceSniff::process()   F
last analyzed

Complexity

Conditions 36
Paths 13

Size

Total Lines 116

Duplication

Lines 33
Ratio 28.45 %

Importance

Changes 0
Metric Value
dl 33
loc 116
rs 3.3333
c 0
b 0
f 0
cc 36
nc 13
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
 * PHPCompatibility, an external standard for PHP_CodeSniffer.
4
 *
5
 * @package   PHPCompatibility
6
 * @copyright 2012-2019 PHPCompatibility Contributors
7
 * @license   https://opensource.org/licenses/LGPL-3.0 LGPL3
8
 * @link      https://github.com/PHPCompatibility/PHPCompatibility
9
 */
10
11
namespace PHPCompatibility\Sniffs\Operators;
12
13
use PHPCompatibility\Sniff;
14
use PHP_CodeSniffer_File as File;
15
use PHP_CodeSniffer_Tokens as Tokens;
16
17
/**
18
 * Detect code affected by the change in operator precedence of concatenation in PHP 8.0.
19
 *
20
 * In PHP < 8.0 the operator precedence of `.`, `+` and `-` are the same.
21
 * As of PHP 8.0, the operator precedence of the concatenation operator will be
22
 * lowered to be right below the `<<` and `>>` operators.
23
 *
24
 * As of PHP 7.4, a deprecation warning will be thrown upon encountering an
25
 * unparenthesized expression containing an `.` before a `+` or `-`.
26
 *
27
 * PHP version 7.4
28
 * PHP version 8.0
29
 *
30
 * @link https://wiki.php.net/rfc/concatenation_precedence
31
 * @link https://www.php.net/manual/en/language.operators.precedence.php
32
 *
33
 * @since 9.2.0
34
 */
35
class ChangedConcatOperatorPrecedenceSniff extends Sniff
36
{
37
38
    /**
39
     * List of tokens with a lower operator precedence than concatenation in PHP >= 8.0.
40
     *
41
     * @since 9.2.0
42
     *
43
     * @var array
44
     */
45
    private $tokensWithLowerPrecedence = array(
46
        'T_BITWISE_AND' => true,
47
        'T_BITWISE_XOR' => true,
48
        'T_BITWISE_OR'  => true,
49
        'T_COALESCE'    => true,
50
        'T_INLINE_THEN' => true,
51
        'T_INLINE_ELSE' => true,
52
        'T_YIELD_FROM'  => true,
53
        'T_YIELD'       => true,
54
    );
55
56
57
    /**
58
     * Returns an array of tokens this test wants to listen for.
59
     *
60
     * @since 9.2.0
61
     *
62
     * @return array
63
     */
64
    public function register()
65
    {
66
        return array(
67
            \T_PLUS,
68
            \T_MINUS,
69
        );
70
    }
71
72
    /**
73
     * Processes this test, when one of its tokens is encountered.
74
     *
75
     * @since 9.2.0
76
     *
77
     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
78
     * @param int                   $stackPtr  The position of the current token in the
79
     *                                         stack passed in $tokens.
80
     *
81
     * @return void
82
     */
83
    public function process(File $phpcsFile, $stackPtr)
84
    {
85
        if ($this->supportsAbove('7.4') === false) {
86
            return;
87
        }
88
89
        if ($this->isUnaryPlusMinus($phpcsFile, $stackPtr) === true) {
90
            return;
91
        }
92
93
        $tokens = $phpcsFile->getTokens();
94
95
        for ($i = ($stackPtr - 1); $stackPtr >= 0; $i--) {
96
            if ($tokens[$i]['code'] === \T_STRING_CONCAT) {
97
                // Found one.
98
                break;
99
            }
100
101
            if ($tokens[$i]['code'] === \T_SEMICOLON
102
                || $tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET
103
                || $tokens[$i]['code'] === \T_OPEN_TAG
104
                || $tokens[$i]['code'] === \T_OPEN_TAG_WITH_ECHO
105
                || $tokens[$i]['code'] === \T_COMMA
106
                || $tokens[$i]['code'] === \T_COLON
107
                || $tokens[$i]['code'] === \T_CASE
108
            ) {
109
                // If we reached any of the above tokens, we've reached the end of
110
                // the statement without encountering a concatenation operator.
111
                return;
112
            }
113
114 View Code Duplication
            if ($tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET
0 ignored issues
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...
115
                && isset($tokens[$i]['bracket_closer'])
116
                && $tokens[$i]['bracket_closer'] > $stackPtr
117
            ) {
118
                // No need to look any further, this is plus/minus within curly braces
119
                // and we've reached the open curly.
120
                return;
121
            }
122
123 View Code Duplication
            if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS
0 ignored issues
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...
124
                && isset($tokens[$i]['parenthesis_closer'])
125
                && $tokens[$i]['parenthesis_closer'] > $stackPtr
126
            ) {
127
                // No need to look any further, this is plus/minus within parenthesis
128
                // and we've reached the open parenthesis.
129
                return;
130
            }
131
132 View Code Duplication
            if (($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY
0 ignored issues
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...
133
                || $tokens[$i]['code'] === \T_OPEN_SQUARE_BRACKET)
134
                && isset($tokens[$i]['bracket_closer'])
135
                && $tokens[$i]['bracket_closer'] > $stackPtr
136
            ) {
137
                // No need to look any further, this is plus/minus within a short array
138
                // or array key square brackets and we've reached the opener.
139
                return;
140
            }
141
142
            if ($tokens[$i]['code'] === \T_CLOSE_CURLY_BRACKET) {
143
                if (isset($tokens[$i]['scope_owner'])) {
144
                    // Different scope, we've passed the start of the statement.
145
                    return;
146
                }
147
148
                if (isset($tokens[$i]['bracket_opener'])) {
149
                    $i = $tokens[$i]['bracket_opener'];
150
                }
151
152
                continue;
153
            }
154
155
            if ($tokens[$i]['code'] === \T_CLOSE_PARENTHESIS
156
                && isset($tokens[$i]['parenthesis_opener'])
157
            ) {
158
                // Skip over statements in parenthesis, including long arrays.
159
                $i = $tokens[$i]['parenthesis_opener'];
160
                continue;
161
            }
162
163 View Code Duplication
            if (($tokens[$i]['code'] === \T_CLOSE_SQUARE_BRACKET
0 ignored issues
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...
164
                || $tokens[$i]['code'] === \T_CLOSE_SHORT_ARRAY)
165
                && isset($tokens[$i]['bracket_opener'])
166
            ) {
167
                // Skip over array keys and short arrays.
168
                $i = $tokens[$i]['bracket_opener'];
169
                continue;
170
            }
171
172
            // Check for chain being broken by a token with a lower precedence.
173
            if (isset(Tokens::$booleanOperators[$tokens[$i]['code']]) === true
174
                || isset(Tokens::$assignmentTokens[$tokens[$i]['code']]) === true
175
            ) {
176
                return;
177
            }
178
179
            if (isset($this->tokensWithLowerPrecedence[$tokens[$i]['type']]) === true) {
180
                if ($tokens[$i]['code'] === \T_BITWISE_AND
181
                    && $phpcsFile->isReference($i) === true
182
                ) {
183
                    continue;
184
                }
185
186
                return;
187
            }
188
        }
189
190
        $message = 'Using an unparenthesized expression containing a "." before a "+" or "-" has been deprecated in PHP 7.4';
191
        $isError = false;
192
        if ($this->supportsAbove('8.0') === true) {
193
            $message .= ' and removed in PHP 8.0';
194
            $isError  = true;
195
        }
196
197
        $this->addMessage($phpcsFile, $message, $i, $isError);
198
    }
199
}
200