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

PHPCSStandards / PHP_CodeSniffer / 14454424553

14 Apr 2025 07:49PM UTC coverage: 77.579% (+2.3%) from 75.291%
14454424553

push

github

web-flow
Merge pull request #983 from PHPCSStandards/phpcs-4.0/feature/sq-2448-remove-support-js-css

Remove CSS/JS support (HUGE PR, reviews welcome!)

119 of 126 new or added lines in 14 files covered. (94.44%)

26 existing lines in 10 files now uncovered.

19384 of 24986 relevant lines covered (77.58%)

78.47 hits per line

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

94.39
/src/Standards/Squiz/Sniffs/PHP/CommentedOutCodeSniff.php
1
<?php
2
/**
3
 * Warn about commented out code.
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\PHP;
11

12
use PHP_CodeSniffer\Exceptions\TokenizerException;
13
use PHP_CodeSniffer\Files\File;
14
use PHP_CodeSniffer\Sniffs\Sniff;
15
use PHP_CodeSniffer\Tokenizers\PHP;
16
use PHP_CodeSniffer\Util\Tokens;
17

18
class CommentedOutCodeSniff implements Sniff
19
{
20

21
    /**
22
     * If a comment is more than $maxPercentage% code, a warning will be shown.
23
     *
24
     * @var integer
25
     */
26
    public $maxPercentage = 35;
27

28

29
    /**
30
     * Returns an array of tokens this test wants to listen for.
31
     *
32
     * @return array<int|string>
33
     */
34
    public function register()
3✔
35
    {
36
        return [T_COMMENT];
3✔
37

38
    }//end register()
39

40

41
    /**
42
     * Processes this test, when one of its tokens is encountered.
43
     *
44
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
45
     * @param int                         $stackPtr  The position of the current token
46
     *                                               in the stack passed in $tokens.
47
     *
48
     * @return int|void Integer stack pointer to skip forward or void to continue
49
     *                  normal file processing.
50
     */
51
    public function process(File $phpcsFile, $stackPtr)
3✔
52
    {
53
        $tokens = $phpcsFile->getTokens();
3✔
54

55
        // Ignore comments at the end of code blocks.
56
        if (substr($tokens[$stackPtr]['content'], 0, 6) === '//end ') {
3✔
57
            return;
3✔
58
        }
59

60
        $content      = '';
3✔
61
        $lastLineSeen = $tokens[$stackPtr]['line'];
3✔
62
        $commentStyle = 'line';
3✔
63
        if (strpos($tokens[$stackPtr]['content'], '/*') === 0) {
3✔
64
            $commentStyle = 'block';
3✔
65
        }
66

67
        $lastCommentBlockToken = $stackPtr;
3✔
68
        for ($i = $stackPtr; $i < $phpcsFile->numTokens; $i++) {
3✔
69
            if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) {
3✔
70
                break;
3✔
71
            }
72

73
            if ($tokens[$i]['code'] === T_WHITESPACE) {
3✔
74
                continue;
3✔
75
            }
76

77
            if (isset(Tokens::$phpcsCommentTokens[$tokens[$i]['code']]) === true) {
3✔
78
                $lastLineSeen = $tokens[$i]['line'];
3✔
79
                continue;
3✔
80
            }
81

82
            if ($commentStyle === 'line'
3✔
83
                && ($lastLineSeen + 1) <= $tokens[$i]['line']
3✔
84
                && strpos($tokens[$i]['content'], '/*') === 0
3✔
85
            ) {
86
                // First non-whitespace token on a new line is start of a different style comment.
87
                break;
3✔
88
            }
89

90
            if ($commentStyle === 'line'
3✔
91
                && ($lastLineSeen + 1) < $tokens[$i]['line']
3✔
92
            ) {
93
                // Blank line breaks a '//' style comment block.
94
                break;
3✔
95
            }
96

97
            /*
98
                Trim as much off the comment as possible so we don't
99
                have additional whitespace tokens or comment tokens
100
            */
101

102
            $tokenContent = trim($tokens[$i]['content']);
3✔
103
            $break        = false;
3✔
104

105
            if ($commentStyle === 'line') {
3✔
106
                if (substr($tokenContent, 0, 2) === '//') {
3✔
107
                    $tokenContent = substr($tokenContent, 2);
3✔
108
                }
109

110
                if (substr($tokenContent, 0, 1) === '#') {
3✔
111
                    $tokenContent = substr($tokenContent, 1);
3✔
112
                }
113
            } else {
114
                if (substr($tokenContent, 0, 3) === '/**') {
3✔
115
                    $tokenContent = substr($tokenContent, 3);
×
116
                }
117

118
                if (substr($tokenContent, 0, 2) === '/*') {
3✔
119
                    $tokenContent = substr($tokenContent, 2);
3✔
120
                }
121

122
                if (substr($tokenContent, -2) === '*/') {
3✔
123
                    $tokenContent = substr($tokenContent, 0, -2);
3✔
124
                    $break        = true;
3✔
125
                }
126

127
                if (substr($tokenContent, 0, 1) === '*') {
3✔
128
                    $tokenContent = substr($tokenContent, 1);
3✔
129
                }
130
            }//end if
131

132
            $content     .= $tokenContent.$phpcsFile->eolChar;
3✔
133
            $lastLineSeen = $tokens[$i]['line'];
3✔
134

135
            $lastCommentBlockToken = $i;
3✔
136

137
            if ($break === true) {
3✔
138
                // Closer of a block comment found.
139
                break;
3✔
140
            }
141
        }//end for
142

143
        // Ignore typical warning suppression annotations from other tools.
144
        if (preg_match('`^\s*@[A-Za-z()\._-]+\s*$`', $content) === 1) {
3✔
145
            return ($lastCommentBlockToken + 1);
3✔
146
        }
147

148
        // Quite a few comments use multiple dashes, equals signs etc
149
        // to frame comments and licence headers.
150
        $content = preg_replace('/[-=#*]{2,}/', '-', $content);
3✔
151

152
        // Random numbers sitting inside the content can throw parse errors
153
        // for invalid literals in PHP7+, so strip those.
154
        $content = preg_replace('/\d+/', '', $content);
3✔
155

156
        $content = trim($content);
3✔
157

158
        if ($content === '') {
3✔
159
            return ($lastCommentBlockToken + 1);
3✔
160
        }
161

162
        $content = '<?php '.$content.' ?>';
3✔
163

164
        // Because we are not really parsing code, the tokenizer can throw all sorts
165
        // of errors that don't mean anything, so ignore them.
166
        $oldErrors = ini_get('error_reporting');
3✔
167
        ini_set('error_reporting', 0);
3✔
168
        try {
169
            $tokenizer    = new PHP($content, $phpcsFile->config, $phpcsFile->eolChar);
3✔
170
            $stringTokens = $tokenizer->getTokens();
3✔
UNCOV
171
        } catch (TokenizerException $e) {
×
172
            // We couldn't check the comment, so ignore it.
173
            ini_set('error_reporting', $oldErrors);
×
174
            return ($lastCommentBlockToken + 1);
×
175
        }
176

177
        ini_set('error_reporting', $oldErrors);
3✔
178

179
        $numTokens = count($stringTokens);
3✔
180

181
        /*
182
            We know what the first two and last two tokens should be
183
            (because we put them there) so ignore this comment if those
184
            tokens were not parsed correctly. It obviously means this is not
185
            valid code.
186
        */
187

188
        // First token is always the opening tag.
189
        if ($stringTokens[0]['code'] !== T_OPEN_TAG) {
3✔
190
            return ($lastCommentBlockToken + 1);
×
191
        } else {
192
            array_shift($stringTokens);
3✔
193
            --$numTokens;
3✔
194
        }
195

196
        // Last token is always the closing tag, unless something went wrong.
197
        if (isset($stringTokens[($numTokens - 1)]) === false
3✔
198
            || $stringTokens[($numTokens - 1)]['code'] !== T_CLOSE_TAG
3✔
199
        ) {
200
            return ($lastCommentBlockToken + 1);
3✔
201
        } else {
202
            array_pop($stringTokens);
3✔
203
            --$numTokens;
3✔
204
        }
205

206
        // The second last token is always whitespace or a comment, depending
207
        // on the code inside the comment.
208
        if (isset(Tokens::$emptyTokens[$stringTokens[($numTokens - 1)]['code']]) === false) {
3✔
NEW
209
            return ($lastCommentBlockToken + 1);
×
210
        }
211

212
        if ($stringTokens[($numTokens - 1)]['code'] === T_WHITESPACE) {
3✔
213
            array_pop($stringTokens);
3✔
214
            --$numTokens;
3✔
215
        }
216

217
        $emptyTokens  = [
2✔
218
            T_WHITESPACE              => true,
3✔
219
            T_STRING                  => true,
3✔
220
            T_STRING_CONCAT           => true,
3✔
221
            T_ENCAPSED_AND_WHITESPACE => true,
3✔
222
            T_NONE                    => true,
3✔
223
            T_COMMENT                 => true,
3✔
224
        ];
2✔
225
        $emptyTokens += Tokens::$phpcsCommentTokens;
3✔
226

227
        $numCode          = 0;
3✔
228
        $numNonWhitespace = 0;
3✔
229

230
        for ($i = 0; $i < $numTokens; $i++) {
3✔
231
            // Do not count comments.
232
            if (isset($emptyTokens[$stringTokens[$i]['code']]) === false
3✔
233
                // Commented out HTML/XML and other docs contain a lot of these
234
                // characters, so it is best to not use them directly.
235
                && isset(Tokens::$comparisonTokens[$stringTokens[$i]['code']]) === false
3✔
236
                && isset(Tokens::$arithmeticTokens[$stringTokens[$i]['code']]) === false
3✔
237
                && $stringTokens[$i]['code'] !== T_GOTO_LABEL
3✔
238
            ) {
239
                // Looks like code.
240
                $numCode++;
3✔
241
            }
242

243
            if ($stringTokens[$i]['code'] !== T_WHITESPACE) {
3✔
244
                ++$numNonWhitespace;
3✔
245
            }
246
        }
247

248
        // Ignore comments with only two or less non-whitespace tokens.
249
        // Sample size too small for a reliably determination.
250
        if ($numNonWhitespace <= 2) {
3✔
251
            return ($lastCommentBlockToken + 1);
3✔
252
        }
253

254
        $percentCode = ceil((($numCode / $numTokens) * 100));
3✔
255
        if ($percentCode > $this->maxPercentage) {
3✔
256
            // Just in case.
257
            $percentCode = min(100, $percentCode);
3✔
258

259
            $error = 'This comment is %s%% valid code; is this commented out code?';
3✔
260
            $data  = [$percentCode];
3✔
261
            $phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
3✔
262
        }
263

264
        return ($lastCommentBlockToken + 1);
3✔
265

266
    }//end process()
267

268

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