• 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

95.89
/src/Standards/Squiz/Sniffs/Commenting/LongConditionClosingCommentSniff.php
1
<?php
2
/**
3
 * Ensures long conditions have a comment at the end.
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\Commenting;
11

12
use PHP_CodeSniffer\Files\File;
13
use PHP_CodeSniffer\Sniffs\Sniff;
14

15
class LongConditionClosingCommentSniff implements Sniff
16
{
17

18
    /**
19
     * The openers that we are interested in.
20
     *
21
     * @var array<int|string>
22
     */
23
    private static $openers = [
24
        T_SWITCH,
25
        T_IF,
26
        T_FOR,
27
        T_FOREACH,
28
        T_WHILE,
29
        T_TRY,
30
        T_CASE,
31
        T_MATCH,
32
    ];
33

34
    /**
35
     * The length that a code block must be before
36
     * requiring a closing comment.
37
     *
38
     * @var integer
39
     */
40
    public $lineLimit = 20;
41

42
    /**
43
     * The format the end comment should be in.
44
     *
45
     * The placeholder %s will be replaced with the type of condition opener.
46
     *
47
     * @var string
48
     */
49
    public $commentFormat = '//end %s';
50

51

52
    /**
53
     * Returns an array of tokens this test wants to listen for.
54
     *
55
     * @return array<int|string>
56
     */
57
    public function register()
3✔
58
    {
59
        return [T_CLOSE_CURLY_BRACKET];
3✔
60

61
    }//end register()
62

63

64
    /**
65
     * Processes this test, when one of its tokens is encountered.
66
     *
67
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
68
     * @param int                         $stackPtr  The position of the current token in the
69
     *                                               stack passed in $tokens.
70
     *
71
     * @return void
72
     */
73
    public function process(File $phpcsFile, $stackPtr)
3✔
74
    {
75
        $tokens = $phpcsFile->getTokens();
3✔
76

77
        if (isset($tokens[$stackPtr]['scope_condition']) === false) {
3✔
78
            // No scope condition. It is a function closer.
79
            return;
×
80
        }
81

82
        $startCondition = $tokens[$tokens[$stackPtr]['scope_condition']];
3✔
83
        $startBrace     = $tokens[$tokens[$stackPtr]['scope_opener']];
3✔
84
        $endBrace       = $tokens[$stackPtr];
3✔
85

86
        // We are only interested in some code blocks.
87
        if (in_array($startCondition['code'], self::$openers, true) === false) {
3✔
88
            return;
3✔
89
        }
90

91
        if ($startCondition['code'] === T_IF) {
3✔
92
            // If this is actually an ELSE IF, skip it as the brace
93
            // will be checked by the original IF.
94
            $else = $phpcsFile->findPrevious(T_WHITESPACE, ($tokens[$stackPtr]['scope_condition'] - 1), null, true);
3✔
95
            if ($tokens[$else]['code'] === T_ELSE) {
3✔
96
                return;
3✔
97
            }
98

99
            // IF statements that have an ELSE block need to use
100
            // "end if" rather than "end else" or "end elseif".
101
            do {
102
                $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
3✔
103
                if ($tokens[$nextToken]['code'] === T_ELSE || $tokens[$nextToken]['code'] === T_ELSEIF) {
3✔
104
                    // Check for ELSE IF (2 tokens) as opposed to ELSEIF (1 token).
105
                    if ($tokens[$nextToken]['code'] === T_ELSE
3✔
106
                        && isset($tokens[$nextToken]['scope_closer']) === false
3✔
107
                    ) {
108
                        $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true);
3✔
109
                        if ($tokens[$nextToken]['code'] !== T_IF
3✔
110
                            || isset($tokens[$nextToken]['scope_closer']) === false
3✔
111
                        ) {
112
                            // Not an ELSE IF or is an inline ELSE IF.
113
                            break;
×
114
                        }
115
                    }
116

117
                    if (isset($tokens[$nextToken]['scope_closer']) === false) {
3✔
118
                        // There isn't going to be anywhere to print the "end if" comment
119
                        // because there is no closer.
120
                        return;
3✔
121
                    }
122

123
                    // The end brace becomes the ELSE's end brace.
124
                    $stackPtr = $tokens[$nextToken]['scope_closer'];
3✔
125
                    $endBrace = $tokens[$stackPtr];
3✔
126
                } else {
127
                    break;
3✔
128
                }//end if
129
            } while (isset($tokens[$nextToken]['scope_closer']) === true);
3✔
130
        }//end if
131

132
        if ($startCondition['code'] === T_TRY) {
3✔
133
            // TRY statements need to check until the end of all CATCH statements.
134
            do {
135
                $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
3✔
136
                if ($tokens[$nextToken]['code'] === T_CATCH
3✔
137
                    || $tokens[$nextToken]['code'] === T_FINALLY
3✔
138
                ) {
139
                    // The end brace becomes the CATCH end brace.
140
                    $stackPtr = $tokens[$nextToken]['scope_closer'];
3✔
141
                    $endBrace = $tokens[$stackPtr];
3✔
142
                } else {
143
                    break;
3✔
144
                }
145
            } while (isset($tokens[$nextToken]['scope_closer']) === true);
3✔
146
        }
147

148
        if ($startCondition['code'] === T_MATCH) {
3✔
149
            // Move the stackPtr to after the semicolon/comma if there is one.
150
            $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
3✔
151
            if ($nextToken !== false
3✔
152
                && ($tokens[$nextToken]['code'] === T_SEMICOLON
3✔
153
                || $tokens[$nextToken]['code'] === T_COMMA)
3✔
154
            ) {
155
                $stackPtr = $nextToken;
3✔
156
            }
157
        }
158

159
        $lineDifference = ($endBrace['line'] - $startBrace['line']);
3✔
160

161
        $expected = sprintf($this->commentFormat, $startCondition['content']);
3✔
162
        $comment  = $phpcsFile->findNext([T_COMMENT], $stackPtr, null, false);
3✔
163

164
        if (($comment === false) || ($tokens[$comment]['line'] !== $endBrace['line'])) {
3✔
165
            if ($lineDifference >= $this->lineLimit) {
3✔
166
                $error = 'End comment for long condition not found; expected "%s"';
3✔
167
                $data  = [$expected];
3✔
168
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'Missing', $data);
3✔
169

170
                if ($fix === true) {
3✔
171
                    $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
3✔
172
                    if ($next !== false && $tokens[$next]['line'] === $tokens[$stackPtr]['line']) {
3✔
173
                        $expected .= $phpcsFile->eolChar;
×
174
                    }
175

176
                    $phpcsFile->fixer->addContent($stackPtr, $expected);
3✔
177
                }
178
            }
179

180
            return;
3✔
181
        }
182

183
        if (($comment - $stackPtr) !== 1) {
3✔
184
            $error = 'Space found before closing comment; expected "%s"';
3✔
185
            $data  = [$expected];
3✔
186
            $phpcsFile->addError($error, $stackPtr, 'SpacingBefore', $data);
3✔
187
        }
188

189
        if (trim($tokens[$comment]['content']) !== $expected) {
3✔
190
            $found = trim($tokens[$comment]['content']);
3✔
191
            $error = 'Incorrect closing comment; expected "%s" but found "%s"';
3✔
192
            $data  = [
2✔
193
                $expected,
3✔
194
                $found,
3✔
195
            ];
2✔
196

197
            $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Invalid', $data);
3✔
198
            if ($fix === true) {
3✔
199
                $phpcsFile->fixer->replaceToken($comment, $expected.$phpcsFile->eolChar);
3✔
200
            }
201

202
            return;
3✔
203
        }
204

205
    }//end process()
1✔
206

207

208
}//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