Failed Conditions
Push — feature/scrutinizer-send-pass-... ( 5c2dd9 )
by Juliette
02:12
created

ValidIntegersSniff::getBinaryInteger()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 2
nop 3
1
<?php
2
/**
3
 * \PHPCompatibility\Sniffs\PHP\ValidIntegersSniff.
4
 *
5
 * @category PHP
6
 * @package  PHPCompatibility
7
 * @author   Juliette Reinders Folmer <[email protected]>
8
 */
9
10
namespace PHPCompatibility\Sniffs\PHP;
11
12
use PHPCompatibility\Sniff;
13
14
/**
15
 * \PHPCompatibility\Sniffs\PHP\ValidIntegersSniff.
16
 *
17
 * @category PHP
18
 * @package  PHPCompatibility
19
 * @author   Juliette Reinders Folmer <[email protected]>
20
 */
21
class ValidIntegersSniff extends Sniff
22
{
23
24
    /**
25
     * Whether PHPCS is run on a PHP < 5.4.
26
     *
27
     * @var bool
28
     */
29
    protected $isLowPHPVersion = false;
30
31
    /**
32
     * Returns an array of tokens this test wants to listen for.
33
     *
34
     * @return array
35
     */
36
    public function register()
37
    {
38
        $this->isLowPHPVersion = version_compare(PHP_VERSION_ID, '50400', '<');
39
40
        return array(
41
            T_LNUMBER, // Binary, octal integers.
42
            T_CONSTANT_ENCAPSED_STRING, // Hex numeric string.
43
        );
44
45
    }//end register()
46
47
48
    /**
49
     * Processes this test, when one of its tokens is encountered.
50
     *
51
     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
52
     * @param int                   $stackPtr  The position of the current token in
53
     *                                         the stack.
54
     *
55
     * @return void
56
     */
57
    public function process(\PHP_CodeSniffer_File $phpcsFile, $stackPtr)
0 ignored issues
show
Bug introduced by
The type PHP_CodeSniffer_File was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
58
    {
59
        $tokens = $phpcsFile->getTokens();
60
        $token  = $tokens[$stackPtr];
61
62
        if ($this->couldBeBinaryInteger($tokens, $stackPtr) === true) {
63
            if ($this->supportsBelow('5.3')) {
64
                $error = 'Binary integer literals were not present in PHP version 5.3 or earlier. Found: %s';
65
                if ($this->isLowPHPVersion === false) {
66
                    $data = array($token['content']);
67
                } else {
68
                    $data = array($this->getBinaryInteger($phpcsFile, $tokens, $stackPtr));
69
                }
70
                $phpcsFile->addError($error, $stackPtr, 'BinaryIntegerFound', $data);
71
            }
72
73
            if ($this->isInvalidBinaryInteger($tokens, $stackPtr) === true) {
74
                $error = 'Invalid binary integer detected. Found: %s';
75
                $data  = array($this->getBinaryInteger($phpcsFile, $tokens, $stackPtr));
76
                $phpcsFile->addWarning($error, $stackPtr, 'InvalidBinaryIntegerFound', $data);
77
            }
78
            return;
79
        }
80
81
        $isError = $this->supportsAbove('7.0');
82
        $data    = array( $token['content'] );
83
84
        if ($this->isInvalidOctalInteger($tokens, $stackPtr) === true) {
85
            $this->addMessage(
86
                $phpcsFile,
87
                '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',
88
                $stackPtr,
89
                $isError,
90
                'InvalidOctalIntegerFound',
91
                $data
92
            );
93
            return;
94
        }
95
96
        if ($this->isHexidecimalNumericString($tokens, $stackPtr) === true) {
97
            $this->addMessage(
98
                $phpcsFile,
99
                'The behaviour of hexadecimal numeric strings was inconsistent prior to PHP 7 and support has been removed in PHP 7. Found: %s',
100
                $stackPtr,
101
                $isError,
102
                'HexNumericStringFound',
103
                $data
104
            );
105
            return;
106
        }
107
108
    }//end process()
109
110
111
    /**
112
     * Could the current token an potentially be a binary integer ?
113
     *
114
     * @param array $tokens   Token stack.
115
     * @param int   $stackPtr The current position in the token stack.
116
     *
117
     * @return bool
118
     */
119
    private function couldBeBinaryInteger($tokens, $stackPtr)
120
    {
121
        $token = $tokens[$stackPtr];
122
123
        if ($token['code'] !== T_LNUMBER) {
124
            return false;
125
        }
126
127
        if ($this->isLowPHPVersion === false) {
128
            return (preg_match('`^0b[0-1]+$`D', $token['content']) === 1);
129
        }
130
        // Pre-5.4, binary strings are tokenized as T_LNUMBER (0) + T_STRING ("b01010101").
131
        // At this point, we don't yet care whether it's a valid binary int, that's a separate check.
132
        else {
133
            return($token['content'] === '0' && $tokens[$stackPtr+1]['code'] === T_STRING && preg_match('`^b[0-9]+$`D', $tokens[$stackPtr+1]['content']) === 1);
134
        }
135
    }
136
137
    /**
138
     * Is the current token an invalid binary integer ?
139
     *
140
     * @param array $tokens   Token stack.
141
     * @param int   $stackPtr The current position in the token stack.
142
     *
143
     * @return bool
144
     */
145
    private function isInvalidBinaryInteger($tokens, $stackPtr)
146
    {
147
        if ($this->couldBeBinaryInteger($tokens, $stackPtr) === false) {
148
            return false;
149
        }
150
151
        if ($this->isLowPHPVersion === false) {
152
            // If it's an invalid binary int, the token will be split into two T_LNUMBER tokens.
153
            return ($tokens[$stackPtr+1]['code'] === T_LNUMBER);
154
        } else {
155
            return (preg_match('`^b[0-1]+$`D', $tokens[$stackPtr+1]['content']) === 0);
156
        }
157
    }
158
159
    /**
160
     * Retrieve the content of the tokens which together look like a binary integer.
161
     *
162
     * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
163
     * @param array                 $tokens    Token stack.
164
     * @param int                   $stackPtr  The position of the current token in
165
     *                                         the stack.
166
     *
167
     * @return string
168
     */
169
    private function getBinaryInteger(\PHP_CodeSniffer_File $phpcsFile, $tokens, $stackPtr)
170
    {
171
        $length = 2; // PHP < 5.4 T_LNUMBER + T_STRING.
172
173
        if ($this->isLowPHPVersion === false) {
174
            $i = $stackPtr;
175
            while ($tokens[$i]['code'] === T_LNUMBER) {
176
                $i++;
177
            }
178
            $length = ($i - $stackPtr);
179
        }
180
181
        return $phpcsFile->getTokensAsString($stackPtr, $length);
182
    }
183
184
    /**
185
     * Is the current token an invalid octal integer ?
186
     *
187
     * @param array $tokens   Token stack.
188
     * @param int   $stackPtr The current position in the token stack.
189
     *
190
     * @return bool
191
     */
192
    private function isInvalidOctalInteger($tokens, $stackPtr)
193
    {
194
        $token = $tokens[$stackPtr];
195
196
        if ($token['code'] === T_LNUMBER && preg_match('`^0[0-7]*[8-9]+[0-9]*$`D', $token['content']) === 1) {
197
            return true;
198
        }
199
200
        return false;
201
    }
202
203
    /**
204
     * Is the current token a hexidecimal numeric string ?
205
     *
206
     * @param array $tokens   Token stack.
207
     * @param int   $stackPtr The current position in the token stack.
208
     *
209
     * @return bool
210
     */
211
    private function isHexidecimalNumericString($tokens, $stackPtr)
212
    {
213
        $token = $tokens[$stackPtr];
214
215
        if ($token['code'] === T_CONSTANT_ENCAPSED_STRING && preg_match('`^0x[a-f0-9]+$`iD', $this->stripQuotes($token['content'])) === 1) {
216
            return true;
217
        }
218
219
        return false;
220
    }
221
222
}//end class
223