• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCSStandards / PHP_CodeSniffer / 15253296250

26 May 2025 11:55AM UTC coverage: 78.632% (+0.3%) from 78.375%
15253296250

Pull #1105

github

web-flow
Merge d9441d98f into caf806050
Pull Request #1105: Skip tests when 'git' command is not available

19665 of 25009 relevant lines covered (78.63%)

88.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.01
/src/Standards/Squiz/Sniffs/Functions/MultiLineFunctionDeclarationSniff.php
1
<?php
2
/**
3
 * Ensure single and multi-line function declarations are defined correctly.
4
 *
5
 * @author    Greg Sherwood <gsherwood@squiz.net>
6
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8
 */
9

10
namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\Functions;
11

12
use PHP_CodeSniffer\Standards\PEAR\Sniffs\Functions\FunctionDeclarationSniff as PEARFunctionDeclarationSniff;
13
use PHP_CodeSniffer\Util\Tokens;
14

15
class MultiLineFunctionDeclarationSniff extends PEARFunctionDeclarationSniff
16
{
17

18

19
    /**
20
     * Determine if this is a multi-line function declaration.
21
     *
22
     * @param \PHP_CodeSniffer\Files\File $phpcsFile   The file being scanned.
23
     * @param int                         $stackPtr    The position of the current token
24
     *                                                 in the stack passed in $tokens.
25
     * @param int                         $openBracket The position of the opening bracket
26
     *                                                 in the stack passed in $tokens.
27
     * @param array                       $tokens      The stack of tokens that make up
28
     *                                                 the file.
29
     *
30
     * @return bool
31
     */
32
    public function isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens)
3✔
33
    {
34
        $bracketsToCheck = [$stackPtr => $openBracket];
3✔
35

36
        // Closures may use the USE keyword and so be multi-line in this way.
37
        if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
3✔
38
            $use = $phpcsFile->findNext(T_USE, ($tokens[$openBracket]['parenthesis_closer'] + 1), $tokens[$stackPtr]['scope_opener']);
3✔
39
            if ($use !== false && isset($tokens[$use]['parenthesis_opener']) === true) {
3✔
40
                $bracketsToCheck[$use] = $tokens[$use]['parenthesis_opener'];
3✔
41
            }
42
        }
43

44
        foreach ($bracketsToCheck as $stackPtr => $openBracket) {
3✔
45
            // If the first argument is on a new line, this is a multi-line
46
            // function declaration, even if there is only one argument.
47
            $next = $phpcsFile->findNext(Tokens::EMPTY_TOKENS, ($openBracket + 1), null, true);
3✔
48
            if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
3✔
49
                return true;
3✔
50
            }
51

52
            $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
3✔
53

54
            $end = $phpcsFile->findEndOfStatement($openBracket + 1);
3✔
55
            while ($tokens[$end]['code'] === T_COMMA) {
3✔
56
                // If the next bit of code is not on the same line, this is a
57
                // multi-line function declaration.
58
                $next = $phpcsFile->findNext(Tokens::EMPTY_TOKENS, ($end + 1), $closeBracket, true);
3✔
59
                if ($next === false) {
3✔
60
                    continue(2);
×
61
                }
62

63
                if ($tokens[$next]['line'] !== $tokens[$end]['line']) {
3✔
64
                    return true;
3✔
65
                }
66

67
                $end = $phpcsFile->findEndOfStatement($next);
3✔
68
            }
69

70
            // We've reached the last argument, so see if the next content
71
            // (should be the close bracket) is also on the same line.
72
            $next = $phpcsFile->findNext(Tokens::EMPTY_TOKENS, ($end + 1), $closeBracket, true);
3✔
73
            if ($next !== false && $tokens[$next]['line'] !== $tokens[$end]['line']) {
3✔
74
                return true;
×
75
            }
76
        }//end foreach
77

78
        return false;
3✔
79

80
    }//end isMultiLineDeclaration()
81

82

83
    /**
84
     * Processes single-line declarations.
85
     *
86
     * Just uses the Generic BSD-Allman brace sniff.
87
     *
88
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
89
     * @param int                         $stackPtr  The position of the current token
90
     *                                               in the stack passed in $tokens.
91
     * @param array                       $tokens    The stack of tokens that make up
92
     *                                               the file.
93
     *
94
     * @return void
95
     */
96
    public function processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens)
3✔
97
    {
98
        // We do everything the parent sniff does, and a bit more because we
99
        // define multi-line declarations a bit differently.
100
        parent::processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens);
3✔
101

102
        $openingBracket = $tokens[$stackPtr]['parenthesis_opener'];
3✔
103
        $closingBracket = $tokens[$stackPtr]['parenthesis_closer'];
3✔
104

105
        $prevNonWhiteSpace = $phpcsFile->findPrevious(T_WHITESPACE, ($closingBracket - 1), $openingBracket, true);
3✔
106
        if ($tokens[$prevNonWhiteSpace]['line'] !== $tokens[$closingBracket]['line']) {
3✔
107
            $error = 'There must not be a newline before the closing parenthesis of a single-line function declaration';
3✔
108

109
            if (isset(Tokens::EMPTY_TOKENS[$tokens[$prevNonWhiteSpace]['code']]) === true) {
3✔
110
                $phpcsFile->addError($error, $closingBracket, 'CloseBracketNewLine');
3✔
111
            } else {
112
                $fix = $phpcsFile->addFixableError($error, $closingBracket, 'CloseBracketNewLine');
3✔
113
                if ($fix === true) {
3✔
114
                    $phpcsFile->fixer->beginChangeset();
3✔
115
                    for ($i = ($closingBracket - 1); $i > $openingBracket; $i--) {
3✔
116
                        if ($tokens[$i]['code'] !== T_WHITESPACE) {
3✔
117
                            break;
3✔
118
                        }
119

120
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
121
                    }
122

123
                    $phpcsFile->fixer->endChangeset();
3✔
124
                }
125
            }
126
        }//end if
127

128
    }//end processSingleLineDeclaration()
1✔
129

130

131
    /**
132
     * Processes multi-line declarations.
133
     *
134
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
135
     * @param int                         $stackPtr  The position of the current token
136
     *                                               in the stack passed in $tokens.
137
     * @param array                       $tokens    The stack of tokens that make up
138
     *                                               the file.
139
     *
140
     * @return void
141
     */
142
    public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens)
3✔
143
    {
144
        // We do everything the parent sniff does, and a bit more.
145
        parent::processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens);
3✔
146

147
        $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
3✔
148
        $this->processBracket($phpcsFile, $openBracket, $tokens, 'function');
3✔
149

150
        if ($tokens[$stackPtr]['code'] !== T_CLOSURE) {
3✔
151
            return;
3✔
152
        }
153

154
        $use = $phpcsFile->findNext(T_USE, ($tokens[$stackPtr]['parenthesis_closer'] + 1), $tokens[$stackPtr]['scope_opener']);
3✔
155
        if ($use === false || isset($tokens[$use]['parenthesis_opener']) === false) {
3✔
156
            return;
×
157
        }
158

159
        $this->processBracket($phpcsFile, $tokens[$use]['parenthesis_opener'], $tokens, 'use');
3✔
160

161
    }//end processMultiLineDeclaration()
1✔
162

163

164
    /**
165
     * Processes the contents of a single set of brackets.
166
     *
167
     * @param \PHP_CodeSniffer\Files\File $phpcsFile   The file being scanned.
168
     * @param int                         $openBracket The position of the open bracket
169
     *                                                 in the stack passed in $tokens.
170
     * @param array                       $tokens      The stack of tokens that make up
171
     *                                                 the file.
172
     * @param string                      $type        The type of the token the brackets
173
     *                                                 belong to (function or use).
174
     *
175
     * @return void
176
     */
177
    public function processBracket($phpcsFile, $openBracket, $tokens, $type='function')
3✔
178
    {
179
        $errorPrefix = '';
3✔
180
        if ($type === 'use') {
3✔
181
            $errorPrefix = 'Use';
3✔
182
        }
183

184
        $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
3✔
185

186
        // The open bracket should be the last thing on the line.
187
        if ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']) {
3✔
188
            $next = $phpcsFile->findNext(Tokens::EMPTY_TOKENS, ($openBracket + 1), null, true);
3✔
189
            if ($tokens[$next]['line'] === $tokens[$openBracket]['line']) {
3✔
190
                $error = 'The first parameter of a multi-line '.$type.' declaration must be on the line after the opening bracket';
3✔
191
                $fix   = $phpcsFile->addFixableError($error, $next, $errorPrefix.'FirstParamSpacing');
3✔
192
                if ($fix === true) {
3✔
193
                    if ($tokens[$next]['line'] === $tokens[$openBracket]['line']) {
3✔
194
                        $phpcsFile->fixer->addNewline($openBracket);
3✔
195
                    } else {
196
                        $phpcsFile->fixer->beginChangeset();
×
197
                        for ($x = $openBracket; $x < $next; $x++) {
×
198
                            if ($tokens[$x]['line'] === $tokens[$openBracket]['line']) {
×
199
                                continue;
×
200
                            }
201

202
                            if ($tokens[$x]['line'] === $tokens[$next]['line']) {
×
203
                                break;
×
204
                            }
205
                        }
206

207
                        $phpcsFile->fixer->endChangeset();
×
208
                    }
209
                }
210
            }//end if
211
        }//end if
212

213
        // Each line between the brackets should contain a single parameter.
214
        for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
3✔
215
            // Skip brackets, like arrays, as they can contain commas.
216
            if (isset($tokens[$i]['bracket_closer']) === true) {
3✔
217
                $i = $tokens[$i]['bracket_closer'];
3✔
218
                continue;
3✔
219
            }
220

221
            if (isset($tokens[$i]['parenthesis_closer']) === true) {
3✔
222
                $i = $tokens[$i]['parenthesis_closer'];
3✔
223
                continue;
3✔
224
            }
225

226
            if (isset($tokens[$i]['attribute_closer']) === true) {
3✔
227
                $i = $tokens[$i]['attribute_closer'];
3✔
228
                continue;
3✔
229
            }
230

231
            if ($tokens[$i]['code'] !== T_COMMA) {
3✔
232
                continue;
3✔
233
            }
234

235
            $next = $phpcsFile->findNext(Tokens::EMPTY_TOKENS, ($i + 1), null, true);
3✔
236
            if ($tokens[$next]['line'] === $tokens[$i]['line']) {
3✔
237
                $error = 'Multi-line '.$type.' declarations must define one parameter per line';
3✔
238
                $fix   = $phpcsFile->addFixableError($error, $next, $errorPrefix.'OneParamPerLine');
3✔
239
                if ($fix === true) {
3✔
240
                    $phpcsFile->fixer->addNewline($i);
3✔
241
                }
242
            }
243
        }//end for
244

245
    }//end processBracket()
1✔
246

247

248
}//end class
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc