• 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

96.51
/src/Standards/Generic/Sniffs/PHP/LowerCaseConstantSniff.php
1
<?php
2
/**
3
 * Checks that all uses of true, false and null are lowercase.
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\Generic\Sniffs\PHP;
11

12
use PHP_CodeSniffer\Files\File;
13
use PHP_CodeSniffer\Sniffs\Sniff;
14
use PHP_CodeSniffer\Util\Tokens;
15

16
class LowerCaseConstantSniff implements Sniff
17
{
18

19
    /**
20
     * The tokens this sniff is targetting.
21
     *
22
     * @var array
23
     */
24
    private $targets = [
25
        T_TRUE  => T_TRUE,
26
        T_FALSE => T_FALSE,
27
        T_NULL  => T_NULL,
28
    ];
29

30
    /**
31
     * Token types which can be encountered in a property type declaration.
32
     *
33
     * @var array<int|string, int|string>
34
     */
35
    private $propertyTypeTokens = [
36
        T_CALLABLE               => T_CALLABLE,
37
        T_SELF                   => T_SELF,
38
        T_PARENT                 => T_PARENT,
39
        T_FALSE                  => T_FALSE,
40
        T_TRUE                   => T_TRUE,
41
        T_NULL                   => T_NULL,
42
        T_STRING                 => T_STRING,
43
        T_NAME_QUALIFIED         => T_NAME_QUALIFIED,
44
        T_NAME_FULLY_QUALIFIED   => T_NAME_FULLY_QUALIFIED,
45
        T_NAME_RELATIVE          => T_NAME_RELATIVE,
46
        T_TYPE_UNION             => T_TYPE_UNION,
47
        T_TYPE_INTERSECTION      => T_TYPE_INTERSECTION,
48
        T_TYPE_OPEN_PARENTHESIS  => T_TYPE_OPEN_PARENTHESIS,
49
        T_TYPE_CLOSE_PARENTHESIS => T_TYPE_CLOSE_PARENTHESIS,
50
        T_NULLABLE               => T_NULLABLE,
51
    ];
52

53

54
    /**
55
     * Returns an array of tokens this test wants to listen for.
56
     *
57
     * @return array<int|string>
58
     */
59
    public function register()
3✔
60
    {
61
        $targets = $this->targets;
3✔
62

63
        // Allow for "fully qualified" true/false/null.
64
        $targets[] = T_NAME_FULLY_QUALIFIED;
3✔
65

66
        // Register scope modifiers to filter out property type declarations.
67
        $targets  += Tokens::SCOPE_MODIFIERS;
3✔
68
        $targets[] = T_VAR;
3✔
69
        $targets[] = T_STATIC;
3✔
70
        $targets[] = T_READONLY;
3✔
71
        $targets[] = T_FINAL;
3✔
72

73
        // Register function keywords to filter out param/return type declarations.
74
        $targets[] = T_FUNCTION;
3✔
75
        $targets[] = T_CLOSURE;
3✔
76
        $targets[] = T_FN;
3✔
77

78
        // Register constant keyword to filter out type declarations.
79
        $targets[] = T_CONST;
3✔
80

81
        return $targets;
3✔
82

83
    }//end register()
84

85

86
    /**
87
     * Processes this sniff, when one of its tokens is encountered.
88
     *
89
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
90
     * @param int                         $stackPtr  The position of the current token in the
91
     *                                               stack passed in $tokens.
92
     *
93
     * @return void|int Optionally returns a stack pointer. The sniff will not be
94
     *                  called again on the current file until the returned stack
95
     *                  pointer is reached.
96
     */
97
    public function process(File $phpcsFile, $stackPtr)
3✔
98
    {
99
        $tokens = $phpcsFile->getTokens();
3✔
100

101
        // If this is a fully qualified name, check if it is FQN true/false/null.
102
        if ($tokens[$stackPtr]['code'] === T_NAME_FULLY_QUALIFIED
3✔
103
            && $this->isFQNTrueFalseNull($phpcsFile, $stackPtr) === false
3✔
104
        ) {
105
            return;
3✔
106
        }
107

108
        // Skip over potential type declarations for constants.
109
        if ($tokens[$stackPtr]['code'] === T_CONST) {
3✔
110
            // Constant must always have a value assigned to it, so we can just look for the assignment
111
            // operator. Anything between the const keyword and the assignment can be safely ignored.
112
            $skipTo = $phpcsFile->findNext(T_EQUAL, ($stackPtr + 1));
3✔
113
            if ($skipTo !== false) {
3✔
114
                return $skipTo;
3✔
115
            }
116

117
            // If we're at the end of the file, just return.
118
            return;
×
119
        }
120

121
        /*
122
         * Skip over type declarations for properties.
123
         *
124
         * Note: for other uses of the visibility modifiers (functions, constants, trait use),
125
         * nothing relevant will be skipped as the next non-empty token will be an "non-skippable"
126
         * one.
127
         * Functions are handled separately below (and then skip to their scope opener), so
128
         * this should also not cause any confusion for constructor property promotion.
129
         *
130
         * For other uses of the "static" keyword, it also shouldn't be problematic as the only
131
         * time the next non-empty token will be a "skippable" token will be in return type
132
         * declarations, in which case, it is correct to skip over them.
133
         */
134

135
        if (isset(Tokens::SCOPE_MODIFIERS[$tokens[$stackPtr]['code']]) === true
3✔
136
            || $tokens[$stackPtr]['code'] === T_VAR
3✔
137
            || $tokens[$stackPtr]['code'] === T_STATIC
3✔
138
            || $tokens[$stackPtr]['code'] === T_READONLY
3✔
139
            || $tokens[$stackPtr]['code'] === T_FINAL
3✔
140
        ) {
141
            $skipOver = (Tokens::EMPTY_TOKENS + $this->propertyTypeTokens);
3✔
142
            $skipTo   = $phpcsFile->findNext($skipOver, ($stackPtr + 1), null, true);
3✔
143
            if ($skipTo !== false) {
3✔
144
                return $skipTo;
3✔
145
            }
146

147
            // If we're at the end of the file, just return.
148
            return;
×
149
        }
150

151
        // Handle function declarations separately as they may contain the keywords in type declarations.
152
        if ($tokens[$stackPtr]['code'] === T_FUNCTION
3✔
153
            || $tokens[$stackPtr]['code'] === T_CLOSURE
3✔
154
            || $tokens[$stackPtr]['code'] === T_FN
3✔
155
        ) {
156
            if (isset($tokens[$stackPtr]['parenthesis_closer']) === false) {
3✔
157
                return;
×
158
            }
159

160
            // Make sure to skip over return type declarations.
161
            $end = $tokens[$stackPtr]['parenthesis_closer'];
3✔
162
            if (isset($tokens[$stackPtr]['scope_opener']) === true) {
3✔
163
                $end = $tokens[$stackPtr]['scope_opener'];
3✔
164
            } else {
165
                $skipTo = $phpcsFile->findNext([T_SEMICOLON, T_OPEN_CURLY_BRACKET], ($end + 1), null, false, null, true);
3✔
166
                if ($skipTo !== false) {
3✔
167
                    $end = $skipTo;
3✔
168
                }
169
            }
170

171
            // Do a quick check if any of the targets exist in the declaration.
172
            $found = $phpcsFile->findNext($this->targets, $tokens[$stackPtr]['parenthesis_opener'], $end);
3✔
173
            if ($found === false) {
3✔
174
                // Skip forward, no need to examine these tokens again.
175
                return $end;
3✔
176
            }
177

178
            // Handle the whole function declaration in one go.
179
            $params = $phpcsFile->getMethodParameters($stackPtr);
3✔
180
            foreach ($params as $param) {
3✔
181
                if (isset($param['default_token']) === false) {
3✔
182
                    continue;
3✔
183
                }
184

185
                $paramEnd = $param['comma_token'];
3✔
186
                if ($param['comma_token'] === false) {
3✔
187
                    $paramEnd = $tokens[$stackPtr]['parenthesis_closer'];
3✔
188
                }
189

190
                for ($i = $param['default_token']; $i < $paramEnd; $i++) {
3✔
191
                    if (isset($this->targets[$tokens[$i]['code']]) === true
3✔
192
                        || ($tokens[$i]['code'] === T_NAME_FULLY_QUALIFIED
3✔
193
                        && $this->isFQNTrueFalseNull($phpcsFile, $i) === true)
3✔
194
                    ) {
195
                        $this->processConstant($phpcsFile, $i);
3✔
196
                    }
197
                }
198
            }
199

200
            // Skip over return type declarations.
201
            return $end;
3✔
202
        }//end if
203

204
        // Handle everything else.
205
        $this->processConstant($phpcsFile, $stackPtr);
3✔
206

207
    }//end process()
1✔
208

209

210
    /**
211
     * Check if a fully qualified name is a fully qualified true/false/null.
212
     *
213
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
214
     * @param int                         $stackPtr  The position of the T_NAME_FULLY_QUALIFIED token in the
215
     *                                               stack passed in $tokens.
216
     *
217
     * @return bool
218
     */
219
    protected function isFQNTrueFalseNull(File $phpcsFile, $stackPtr)
3✔
220
    {
221
        $tokens = $phpcsFile->getTokens();
3✔
222

223
        // Check for fully qualified true/false/null only.
224
        $compareReadyKeyword = strtolower($tokens[$stackPtr]['content']);
3✔
225
        return ($compareReadyKeyword === '\true'
3✔
226
            || $compareReadyKeyword === '\false'
3✔
227
            || $compareReadyKeyword === '\null');
3✔
228

229
    }//end isFQNTrueFalseNull()
230

231

232
    /**
233
     * Processes a non-type declaration constant.
234
     *
235
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
236
     * @param int                         $stackPtr  The position of the current token in the
237
     *                                               stack passed in $tokens.
238
     *
239
     * @return void
240
     */
241
    protected function processConstant(File $phpcsFile, $stackPtr)
3✔
242
    {
243
        $tokens   = $phpcsFile->getTokens();
3✔
244
        $keyword  = $tokens[$stackPtr]['content'];
3✔
245
        $expected = strtolower($keyword);
3✔
246

247
        if ($keyword !== $expected) {
3✔
248
            if ($keyword === strtoupper($keyword)) {
3✔
249
                $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'upper');
3✔
250
            } else {
251
                $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'mixed');
3✔
252
            }
253

254
            $error = 'TRUE, FALSE and NULL must be lowercase; expected "%s" but found "%s"';
3✔
255
            $data  = [
2✔
256
                $expected,
3✔
257
                $keyword,
3✔
258
            ];
2✔
259

260
            $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found', $data);
3✔
261
            if ($fix === true) {
3✔
262
                $phpcsFile->fixer->replaceToken($stackPtr, $expected);
3✔
263
            }
264
        } else {
265
            $phpcsFile->recordMetric($stackPtr, 'PHP constant case', 'lower');
3✔
266
        }
267

268
    }//end processConstant()
1✔
269

270

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