Skip to content

Commit 10d4dbf

Browse files
committed
✨ PHP 8.1 | New PHPCompatibility.Numbers.NewExplicitOctalNotation sniff
> **Integer Octal Literal Prefix** > > Octal integers can now use an explicit `0o`/`0O` prefix in integer literals, similarly to binary and hexadecimal integer literals. Includes unit tests. Includes documentation. Refs: * https://www.php.net/manual/en/migration81.new-features.php#migration81.new-features.core.octal-literal-prefix * https://wiki.php.net/rfc/explicit_octal_notation * php/php-src#6360 * php/php-src@589bdf3
1 parent 970ecec commit 10d4dbf

4 files changed

Lines changed: 228 additions & 0 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0"?>
2+
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
4+
title="New Explicit Octal Notation"
5+
>
6+
<standard>
7+
<![CDATA[
8+
Since PHP 8.1, octal integers can use an explicit 0o/0O prefix in integer literals, similarly to binary and hexadecimal integer literals.
9+
]]>
10+
</standard>
11+
<code_comparison>
12+
<code title="Cross-version compatible: octal integer without the explicit octal prefix.">
13+
<![CDATA[
14+
$octal = <em>014</em>;
15+
]]>
16+
</code>
17+
<code title="PHP &gt;= 8.1: octal integer using the explicit octal prefix.">
18+
<![CDATA[
19+
$octal = <em>0o14</em>;
20+
]]>
21+
</code>
22+
</code_comparison>
23+
</documentation>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
/**
3+
* PHPCompatibility, an external standard for PHP_CodeSniffer.
4+
*
5+
* @package PHPCompatibility
6+
* @copyright 2012-2022 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 PHP_CodeSniffer\Files\File;
14+
use PHP_CodeSniffer\Exceptions\RuntimeException;
15+
use PHPCompatibility\Sniff;
16+
use PHPCSUtils\Utils\Numbers;
17+
18+
/**
19+
* Support for octal integers using an explicit "0o"/"0O" prefix in integer literals is available since PHP 8.1.
20+
*
21+
* PHP version 8.1
22+
*
23+
* @link https://www.php.net/manual/en/migration81.new-features.php#migration81.new-features.core.octal-literal-prefix
24+
* @link https://wiki.php.net/rfc/explicit_octal_notation
25+
*
26+
* @since 10.0.0
27+
*/
28+
class NewExplicitOctalNotationSniff extends Sniff
29+
{
30+
31+
/**
32+
* Returns an array of tokens this test wants to listen for.
33+
*
34+
* @since 10.0.0
35+
*
36+
* @return array
37+
*/
38+
public function register()
39+
{
40+
return [\T_LNUMBER];
41+
}
42+
43+
/**
44+
* Processes this test, when one of its tokens is encountered.
45+
*
46+
* @since 10.0.0
47+
*
48+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
49+
* @param int $stackPtr The position of the current token in the
50+
* stack passed in $tokens.
51+
*
52+
* @return int|void Integer stack pointer to skip forward or void to continue
53+
* normal file processing.
54+
*/
55+
public function process(File $phpcsFile, $stackPtr)
56+
{
57+
if ($this->supportsBelow('8.0') === false) {
58+
return;
59+
}
60+
61+
$numberInfo = Numbers::getCompleteNumber($phpcsFile, $stackPtr);
62+
if (\stripos($numberInfo['content'], '0o') !== 0) {
63+
// Not using explicit octal notation.
64+
return;
65+
}
66+
67+
$phpcsFile->addError(
68+
'The explicit integer octal literal prefix "0o" is not supported in PHP 8.0 or lower. Found: %s',
69+
$stackPtr,
70+
'Found',
71+
[$numberInfo['orig_content']]
72+
);
73+
74+
// Skip past the parts we've already taken into account to prevent double reporting.
75+
return ($numberInfo['last_token'] + 1);
76+
}
77+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* Okay cross-version: literal octal notation.
5+
*/
6+
$octal = 052;
7+
8+
// PHP 7.4 octal with numeric literal separator. Not the concern of this sniff.
9+
$octal = 0137_041;
10+
11+
// Invalid octal. Parse error. Not the concern of this sniff.
12+
$octal = 0o_;
13+
14+
// Invalid octal. Parse error. Not the concern of this sniff.
15+
$octal = 0o9;
16+
17+
/*
18+
* PHP 8.1 explicit octal notation.
19+
*/
20+
$octal = 0o137041;
21+
$octal = 0O137041; // Testing uppercase O.
22+
$octal = 0o137_041; // Explicit notation with separator.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
/**
3+
* PHPCompatibility, an external standard for PHP_CodeSniffer.
4+
*
5+
* @package PHPCompatibility
6+
* @copyright 2012-2022 PHPCompatibility Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCompatibility/PHPCompatibility
9+
*/
10+
11+
namespace PHPCompatibility\Tests\Numbers;
12+
13+
use PHPCompatibility\Tests\BaseSniffTest;
14+
use PHPCSUtils\BackCompat\Helper;
15+
16+
/**
17+
* Tests for the NewExplicitOctalNotationSniff sniff.
18+
*
19+
* @group newExplicitOctalNotation
20+
* @group numbers
21+
*
22+
* @covers \PHPCompatibility\Sniffs\Numbers\NewExplicitOctalNotationSniff
23+
*
24+
* @since 10.0.0
25+
*/
26+
class NewExplicitOctalNotationUnitTest extends BaseSniffTest
27+
{
28+
29+
/**
30+
* Test recognizing the explicit octal notation correctly.
31+
*
32+
* @dataProvider dataExplicitOctalNotation
33+
*
34+
* @param array $line The line number on which the error should occur.
35+
*
36+
* @return void
37+
*/
38+
public function testExplicitOctalNotation($line)
39+
{
40+
$file = $this->sniffFile(__FILE__, '8.0');
41+
$this->assertError($file, $line, 'The explicit integer octal literal prefix "0o" is not supported in PHP 8.0 or lower. Found:');
42+
}
43+
44+
/**
45+
* Data provider.
46+
*
47+
* @see testExplicitOctalNotation()
48+
*
49+
* @return array
50+
*/
51+
public function dataExplicitOctalNotation()
52+
{
53+
return [
54+
[20],
55+
[21],
56+
[22],
57+
];
58+
}
59+
60+
61+
/**
62+
* Verify there are no false positives for valid code with a PHP version on which this sniff throws errors.
63+
*
64+
* @dataProvider dataNoFalsePositives
65+
*
66+
* @param int $line The line number.
67+
*
68+
* @return void
69+
*/
70+
public function testNoFalsePositives($line)
71+
{
72+
$file = $this->sniffFile(__FILE__, '8.0');
73+
$this->assertNoViolation($file, $line);
74+
}
75+
76+
/**
77+
* Data provider.
78+
*
79+
* @see testNoFalsePositives()
80+
*
81+
* @return array
82+
*/
83+
public function dataNoFalsePositives()
84+
{
85+
$data = [];
86+
87+
// No issues expected on the first 16 lines.
88+
for ($i = 1; $i <= 16; $i++) {
89+
$data[] = [$i];
90+
}
91+
92+
return $data;
93+
}
94+
95+
96+
/**
97+
* Verify no notices are thrown at all.
98+
*
99+
* @return void
100+
*/
101+
public function testNoViolationsInFileOnValidVersion()
102+
{
103+
$file = $this->sniffFile(__FILE__, '8.1');
104+
$this->assertNoViolation($file);
105+
}
106+
}

0 commit comments

Comments
 (0)