processParameters()   F
last analyzed

Complexity

Conditions 46
Paths 1698

Size

Total Lines 168

Duplication

Lines 20
Ratio 11.9 %

Importance

Changes 0
Metric Value
dl 20
loc 168
rs 0
c 0
b 0
f 0
cc 46
nc 1698
nop 4

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\ParameterValues;
12
13
use PHPCompatibility\AbstractFunctionCallParameterSniff;
14
use PHP_CodeSniffer_File as File;
15
use PHP_CodeSniffer_Tokens as Tokens;
16
17
/**
18
 * Passing the `$glue` and `$pieces` parameters to `implode()` in reverse order has
19
 * been deprecated in PHP 7.4.
20
 *
21
 * PHP version 7.4
22
 *
23
 * @link https://www.php.net/manual/en/migration74.deprecated.php#migration74.deprecated.core.implode-reverse-parameters
24
 * @link https://wiki.php.net/rfc/deprecations_php_7_4#implode_parameter_order_mix
25
 * @link https://php.net/manual/en/function.implode.php
26
 *
27
 * @since 9.3.0
28
 */
29
class RemovedImplodeFlexibleParamOrderSniff extends AbstractFunctionCallParameterSniff
30
{
31
32
    /**
33
     * Functions to check for.
34
     *
35
     * @since 9.3.0
36
     *
37
     * @var array
38
     */
39
    protected $targetFunctions = array(
40
        'implode' => true,
41
        'join'    => true,
42
    );
43
44
    /**
45
     * List of PHP native constants which should be recognized as text strings.
46
     *
47
     * @since 9.3.0
48
     *
49
     * @var array
50
     */
51
    private $constantStrings = array(
52
        'DIRECTORY_SEPARATOR' => true,
53
        'PHP_EOL'             => true,
54
    );
55
56
    /**
57
     * List of PHP native functions which should be recognized as returning an array.
58
     *
59
     * Note: The array_*() functions will always be taken into account.
60
     *
61
     * @since 9.3.0
62
     *
63
     * @var array
64
     */
65
    private $arrayFunctions = array(
66
        'compact' => true,
67
        'explode' => true,
68
        'range'   => true,
69
    );
70
71
    /**
72
     * List of PHP native array functions which should *not* be recognized as returning an array.
73
     *
74
     * @since 9.3.0
75
     *
76
     * @var array
77
     */
78
    private $arrayFunctionExceptions = array(
79
        'array_key_exists'     => true,
80
        'array_key_first'      => true,
81
        'array_key_last'       => true,
82
        'array_multisort'      => true,
83
        'array_pop'            => true,
84
        'array_product'        => true,
85
        'array_push'           => true,
86
        'array_search'         => true,
87
        'array_shift'          => true,
88
        'array_sum'            => true,
89
        'array_unshift'        => true,
90
        'array_walk_recursive' => true,
91
        'array_walk'           => true,
92
    );
93
94
95
    /**
96
     * Do a version check to determine if this sniff needs to run at all.
97
     *
98
     * @since 9.3.0
99
     *
100
     * @return bool
101
     */
102
    protected function bowOutEarly()
103
    {
104
        return ($this->supportsAbove('7.4') === false);
105
    }
106
107
108
    /**
109
     * Process the parameters of a matched function.
110
     *
111
     * @since 9.3.0
112
     *
113
     * @param \PHP_CodeSniffer_File $phpcsFile    The file being scanned.
114
     * @param int                   $stackPtr     The position of the current token in the stack.
115
     * @param string                $functionName The token content (function name) which was matched.
116
     * @param array                 $parameters   Array with information about the parameters.
117
     *
118
     * @return int|void Integer stack pointer to skip forward or void to continue
119
     *                  normal file processing.
120
     */
121
    public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
122
    {
123
        if (isset($parameters[2]) === false) {
124
            // Only one parameter, this must be $pieces. Bow out.
125
            return;
126
        }
127
128
        $tokens = $phpcsFile->getTokens();
129
130
        /*
131
         * Examine the first parameter.
132
         * If there is any indication that this is an array declaration, we have an error.
133
         */
134
135
        $targetParam = $parameters[1];
136
        $start       = $targetParam['start'];
137
        $end         = ($targetParam['end'] + 1);
138
        $isOnlyText  = true;
139
140
        $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true);
141
        if ($firstNonEmpty === false) {
142
            // Parse error. Shouldn't be possible.
143
            return;
144
        }
145
146 View Code Duplication
        if ($tokens[$firstNonEmpty]['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...
147
            $start = ($firstNonEmpty + 1);
148
            $end   = $tokens[$firstNonEmpty]['parenthesis_closer'];
149
        }
150
151
        $hasTernary = $phpcsFile->findNext(\T_INLINE_THEN, $start, $end);
152 View Code Duplication
        if ($hasTernary !== false
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...
153
            && isset($tokens[$start]['nested_parenthesis'], $tokens[$hasTernary]['nested_parenthesis'])
154
            && count($tokens[$start]['nested_parenthesis']) === count($tokens[$hasTernary]['nested_parenthesis'])
155
        ) {
156
            $start = ($hasTernary + 1);
157
        }
158
159
        for ($i = $start; $i < $end; $i++) {
160
            $tokenCode = $tokens[$i]['code'];
161
162
            if (isset(Tokens::$emptyTokens[$tokenCode])) {
163
                continue;
164
            }
165
166
            if ($tokenCode === \T_STRING && isset($this->constantStrings[$tokens[$i]['content']])) {
167
                continue;
168
            }
169
170
            if ($hasTernary !== false && $tokenCode === \T_INLINE_ELSE) {
171
                continue;
172
            }
173
174
            if (isset(Tokens::$stringTokens[$tokenCode]) === false) {
175
                $isOnlyText = false;
176
            }
177
178
            if ($tokenCode === \T_ARRAY || $tokenCode === \T_OPEN_SHORT_ARRAY || $tokenCode === \T_ARRAY_CAST) {
179
                $this->throwNotice($phpcsFile, $stackPtr, $functionName);
180
                return;
181
            }
182
183
            if ($tokenCode === \T_STRING) {
184
                /*
185
                 * Check for specific functions which return an array (i.e. $pieces).
186
                 */
187
                $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), $end, true);
188
                if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
189
                    continue;
190
                }
191
192
                $nameLc = strtolower($tokens[$i]['content']);
193
                if (isset($this->arrayFunctions[$nameLc]) === false
194
                    && (strpos($nameLc, 'array_') !== 0
195
                    || isset($this->arrayFunctionExceptions[$nameLc]) === true)
196
                ) {
197
                    continue;
198
                }
199
200
                // Now make sure it's the PHP native function being called.
201
                $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($i - 1), $start, true);
202
                if ($tokens[$prevNonEmpty]['code'] === \T_DOUBLE_COLON
203
                    || $tokens[$prevNonEmpty]['code'] === \T_OBJECT_OPERATOR
204
                ) {
205
                    // Method call, not a call to the PHP native function.
206
                    continue;
207
                }
208
209
                if ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR
210
                    && $tokens[$prevNonEmpty - 1]['code'] === \T_STRING
211
                ) {
212
                    // Namespaced function.
213
                    continue;
214
                }
215
216
                // Ok, so we know that there is an array function in the first param.
217
                // 99.9% chance that this is $pieces, not $glue.
218
                $this->throwNotice($phpcsFile, $stackPtr, $functionName);
219
                return;
220
            }
221
        }
222
223
        if ($isOnlyText === true) {
224
            // First parameter only contained text string tokens, i.e. glue.
225
            return;
226
        }
227
228
        /*
229
         * Examine the second parameter.
230
         */
231
232
        $targetParam = $parameters[2];
233
        $start       = $targetParam['start'];
234
        $end         = ($targetParam['end'] + 1);
235
236
        $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true);
237
        if ($firstNonEmpty === false) {
238
            // Parse error. Shouldn't be possible.
239
            return;
240
        }
241
242 View Code Duplication
        if ($tokens[$firstNonEmpty]['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...
243
            $start = ($firstNonEmpty + 1);
244
            $end   = $tokens[$firstNonEmpty]['parenthesis_closer'];
245
        }
246
247
        $hasTernary = $phpcsFile->findNext(\T_INLINE_THEN, $start, $end);
248 View Code Duplication
        if ($hasTernary !== false
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...
249
            && isset($tokens[$start]['nested_parenthesis'], $tokens[$hasTernary]['nested_parenthesis'])
250
            && count($tokens[$start]['nested_parenthesis']) === count($tokens[$hasTernary]['nested_parenthesis'])
251
        ) {
252
            $start = ($hasTernary + 1);
253
        }
254
255
        for ($i = $start; $i < $end; $i++) {
256
            $tokenCode = $tokens[$i]['code'];
257
258
            if (isset(Tokens::$emptyTokens[$tokenCode])) {
259
                continue;
260
            }
261
262
            if ($tokenCode === \T_ARRAY || $tokenCode === \T_OPEN_SHORT_ARRAY || $tokenCode === \T_ARRAY_CAST) {
263
                // Found an array, $pieces is second.
264
                return;
265
            }
266
267
            if ($tokenCode === \T_STRING && isset($this->constantStrings[$tokens[$i]['content']])) {
268
                // One of the special cased, PHP native string constants found.
269
                $this->throwNotice($phpcsFile, $stackPtr, $functionName);
270
                return;
271
            }
272
273
            if ($tokenCode === \T_STRING || $tokenCode === \T_VARIABLE) {
274
                // Function call, constant or variable encountered.
275
                // No matter what this is combined with, we won't be able to reliably determine the value.
276
                return;
277
            }
278
279
            if ($tokenCode === \T_CONSTANT_ENCAPSED_STRING
280
                || $tokenCode === \T_DOUBLE_QUOTED_STRING
281
                || $tokenCode === \T_HEREDOC
282
                || $tokenCode === \T_NOWDOC
283
            ) {
284
                $this->throwNotice($phpcsFile, $stackPtr, $functionName);
285
                return;
286
            }
287
        }
288
    }
289
290
291
    /**
292
     * Throw the error/warning.
293
     *
294
     * @since 9.3.0
295
     *
296
     * @param \PHP_CodeSniffer_File $phpcsFile    The file being scanned.
297
     * @param int                   $stackPtr     The position of the current token in the stack.
298
     * @param string                $functionName The token content (function name) which was matched.
299
     *
300
     * @return void
301
     */
302
    protected function throwNotice(File $phpcsFile, $stackPtr, $functionName)
303
    {
304
        $message   = 'Passing the $glue and $pieces parameters in reverse order to %s has been deprecated since PHP 7.4';
305
        $isError   = false;
306
        $errorCode = 'Deprecated';
307
        $data      = array($functionName);
308
309
        /*
310
        Support for the deprecated behaviour is expected to be removed in PHP 8.0.
311
        Once this has been implemented, this section should be uncommented.
312
        if ($this->supportsAbove('8.0') === true) {
313
            $message  .= ' and is removed since PHP 8.0';
314
            $isError   = true;
315
            $errorCode = 'Removed';
316
        }
317
        */
318
319
        $message .= '; $glue should be the first parameter and $pieces the second';
320
321
        $this->addMessage($phpcsFile, $message, $stackPtr, $isError, $errorCode, $data);
322
    }
323
}
324