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

PHPCompatibility / PHPCompatibility / 19804455605

30 Nov 2025 08:31PM UTC coverage: 98.306% (-0.04%) from 98.346%
19804455605

push

github

web-flow
Merge pull request #2014 from PHPCompatibility/php-8.5/new-removedterminatingcasewithsemicolon-sniff

PHP 8.5 | ✨ New `PHPCompatibility.ControlStructures.RemovedTerminatingCaseWithSemicolon` sniff (RFC)

30 of 34 new or added lines in 1 file covered. (88.24%)

21 existing lines in 15 files now uncovered.

8355 of 8499 relevant lines covered (98.31%)

20.51 hits per line

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

98.04
/PHPCompatibility/Sniffs/Numbers/ValidIntegersSniff.php
1
<?php
2
/**
3
 * PHPCompatibility, an external standard for PHP_CodeSniffer.
4
 *
5
 * @package   PHPCompatibility
6
 * @copyright 2012-2020 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\Numbers;
12

13
use PHPCompatibility\Helpers\ScannedCode;
14
use PHPCompatibility\Sniff;
15
use PHP_CodeSniffer\Files\File;
16
use PHPCSUtils\Utils\GetTokensAsString;
17
use PHPCSUtils\Utils\MessageHelper;
18
use PHPCSUtils\Utils\Numbers;
19

20
/**
21
 * Check for valid integer types and values.
22
 *
23
 * Checks:
24
 * - PHP 5.4 introduced binary integers.
25
 * - PHP 7.0 removed tolerance for invalid octals. These were truncated prior to PHP 7
26
 *   and give a parse error since PHP 7.
27
 *
28
 * PHP version 5.4+
29
 *
30
 * @link https://wiki.php.net/rfc/binnotation4ints
31
 * @link https://www.php.net/manual/en/language.types.integer.php
32
 *
33
 * @since 7.0.3
34
 * @since 7.0.8  This sniff now throws a warning instead of an error for invalid binary integers.
35
 * @since 10.0.0 - The sniff has been moved from the `Miscellaneous` category to `Numbers`.
36
 *               - The check for hexadecimal numeric strings has been split off to its own sniff.
37
 *               - This class is now `final`.
38
 */
39
final class ValidIntegersSniff extends Sniff
40
{
41

42
    /**
43
     * Returns an array of tokens this test wants to listen for.
44
     *
45
     * @since 7.0.3
46
     *
47
     * @return array<int|string>
48
     */
49
    public function register()
4✔
50
    {
51
        return [
2✔
52
            \T_LNUMBER,
4✔
53
        ];
2✔
54
    }
55

56

57
    /**
58
     * Processes this test, when one of its tokens is encountered.
59
     *
60
     * @since 7.0.3
61
     *
62
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
63
     * @param int                         $stackPtr  The position of the current token in
64
     *                                               the stack.
65
     *
66
     * @return int Integer stack pointer to skip forward.
67
     */
68
    public function process(File $phpcsFile, $stackPtr)
16✔
69
    {
70
        $tokens     = $phpcsFile->getTokens();
16✔
71
        $numberInfo = Numbers::getCompleteNumber($phpcsFile, $stackPtr);
16✔
72

73
        if (Numbers::isBinaryInt($numberInfo['content']) === true) {
16✔
74
            if (ScannedCode::shouldRunOnOrBelow('5.3') === true) {
16✔
75
                $error = 'Binary integer literals were not present in PHP version 5.3 or earlier. Found: %s';
8✔
76
                $data  = [$numberInfo['orig_content']];
8✔
77
                $phpcsFile->addError($error, $stackPtr, 'BinaryIntegerFound', $data);
8✔
78
            }
79

80
            if ($this->isInvalidBinaryInteger($tokens, $numberInfo['last_token']) === true) {
16✔
81
                $error = 'Invalid binary integer detected. Found: %s';
16✔
82
                $data  = [$this->getBinaryInteger($phpcsFile, $tokens, $stackPtr)];
16✔
83
                $phpcsFile->addWarning($error, $stackPtr, 'InvalidBinaryIntegerFound', $data);
16✔
84
            }
85

86
            // If this was a PHP 7.4 numeric literal, no need to scan subsequent parts of the number again.
87
            return $numberInfo['last_token'];
16✔
88
        }
89

90
        $isError = ScannedCode::shouldRunOnOrAbove('7.0');
16✔
91

92
        $isInvalidOctal = $this->isInvalidOctalInteger($tokens, $stackPtr, $numberInfo);
16✔
93
        if ($isInvalidOctal !== false) {
16✔
94
            MessageHelper::addMessage(
16✔
95
                $phpcsFile,
16✔
96
                'Invalid octal integer detected. Prior to PHP 7 this would lead to a truncated number. From PHP 7 onwards this causes a parse error. Found: %s',
16✔
97
                $stackPtr,
16✔
98
                $isError,
16✔
99
                'InvalidOctalIntegerFound',
16✔
100
                [$isInvalidOctal]
16✔
101
            );
8✔
102
        }
103

104
        // If this was a PHP 7.4 numeric literal, no need to scan subsequent parts of the number again.
105
        return $numberInfo['last_token'];
16✔
106
    }
107

108
    /**
109
     * Is the current token an invalid binary integer ?
110
     *
111
     * @since 7.0.3
112
     *
113
     * @param array $tokens   Token stack.
114
     * @param int   $stackPtr The current position in the token stack.
115
     *
116
     * @return bool
117
     */
118
    private function isInvalidBinaryInteger($tokens, $stackPtr)
16✔
119
    {
120
        $next = $tokens[$stackPtr + 1];
16✔
121

122
        // If it's an invalid binary int, the token will be split into two T_LNUMBER tokens.
123
        if ($next['code'] === \T_LNUMBER) {
16✔
124
            return true;
16✔
125
        }
126

127
        if ($next['code'] === \T_STRING
16✔
128
            && \preg_match('`^((?<![\.e])_[0-9][0-9e\.]*)+$`iD', $next['content'])
16✔
129
        ) {
UNCOV
130
            return true;
×
131
        }
132

133
        return false;
16✔
134
    }
135

136
    /**
137
     * Retrieve the content of the tokens which together look like a binary integer.
138
     *
139
     * @since 7.0.3
140
     *
141
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
142
     * @param array                       $tokens    Token stack.
143
     * @param int                         $stackPtr  The position of the current token in
144
     *                                               the stack.
145
     *
146
     * @return string
147
     */
148
    private function getBinaryInteger(File $phpcsFile, $tokens, $stackPtr)
16✔
149
    {
150
        $i = $stackPtr;
16✔
151
        while ($tokens[($i + 1)]['code'] === \T_LNUMBER) {
16✔
152
            ++$i;
16✔
153
        }
154

155
        return GetTokensAsString::normal($phpcsFile, $stackPtr, $i);
16✔
156
    }
157

158
    /**
159
     * Is the current token an invalid octal integer ?
160
     *
161
     * @since 7.0.3
162
     *
163
     * @param array                     $tokens     Token stack.
164
     * @param int                       $stackPtr   The current position in the token stack.
165
     * @param array<string, string|int> $numberInfo The information on the number to examine
166
     *
167
     * @return string|bool The invalid octal as a string or false when this is not an invalid octal.
168
     */
169
    private function isInvalidOctalInteger($tokens, $stackPtr, $numberInfo)
16✔
170
    {
171
        // For invalid explicit octal, we need to also check the next token.
172
        if (($numberInfo['content'] === '0'
16✔
173
            && \strtolower($tokens[($stackPtr + 1)]['content'][0]) === 'o')
16✔
174
            || \stripos($numberInfo['content'], '0o') === 0
16✔
175
        ) {
176
            if (\preg_match('`^(?:[o_][0-7_]*)?[8-9]+[_0-9]*$`iD', $tokens[($stackPtr + 1)]['content']) === 1) {
16✔
177
                return $tokens[$stackPtr]['content'] . $tokens[($stackPtr + 1)]['content'];
16✔
178
            }
179
        }
180

181
        if (\preg_match('`^0[0-7]*[8-9]+[0-9]*$`iD', $numberInfo['content']) === 1) {
16✔
182
            return $numberInfo['orig_content'];
16✔
183
        }
184

185
        return false;
16✔
186
    }
187
}
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

© 2026 Coveralls, Inc