Skip to content

Commit 3d1bdd5

Browse files
author
Matt Turland
committed
PHP 8.0 | New PHPCompatibility.Syntax.InterpolatedStringDereferencing sniff
> Non-interpolated strings `"foo"` are currently considered fully dereferencable, i.e. constructions such as `"foo"[0]` or `"foo"->bar()` are considered legal (syntactically at least). However, interpolated strings `"foo$bar"` are non-dereferencable. > > This RFC proposed to treat both types of strings consistently, i.e. `"foo$bar"[0]`, `"foo$bar"->baz()` etc become legal. Ref: * https://wiki.php.net/rfc/variable_syntax_tweaks#interpolated_and_non-interpolated_strings * php/php-src#5061 * php/php-src@24e365f Includes unit tests. Relates to PHPCompatibility#809
1 parent 7354cd6 commit 3d1bdd5

File tree

3 files changed

+222
-0
lines changed

3 files changed

+222
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/**
3+
* PHPCompatibility, an external standard for PHP_CodeSniffer.
4+
*
5+
* @package PHPCompatibility
6+
* @copyright 2009-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\Syntax;
12+
13+
use PHPCompatibility\Sniff;
14+
use PHPCSUtils\Utils\TextStrings;
15+
use PHP_CodeSniffer\Files\File;
16+
use PHP_CodeSniffer\Util\Tokens;
17+
18+
/**
19+
* Detect dereferencing of interpolated strings.
20+
*
21+
* PHP version 8.0
22+
*
23+
* @link https://wiki.php.net/rfc/variable_syntax_tweaks#interpolated_and_non-interpolated_strings
24+
* @link https://www.php.net/manual/en/language.types.string.php#language.types.string.parsing
25+
*
26+
* @since 10.0
27+
*/
28+
class InterpolatedStringDereferencingSniff extends Sniff
29+
{
30+
/**
31+
* Returns an array of tokens this test wants to listen for.
32+
*
33+
* @since 10.0
34+
*
35+
* @return array
36+
*/
37+
public function register()
38+
{
39+
return [
40+
\T_DOUBLE_QUOTED_STRING,
41+
\T_HEREDOC,
42+
];
43+
}
44+
45+
/**
46+
* Processes this test, when one of its tokens is encountered.
47+
*
48+
* @since 10.0
49+
*
50+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
51+
* @param int $stackPtr The position of the current token
52+
* in the stack passed in $tokens.
53+
*
54+
* @return void
55+
*/
56+
public function process(File $phpcsFile, $stackPtr)
57+
{
58+
// PHP 8.0 supports dereferencing interpolated strings
59+
if ($this->supportsAbove('8.0')) {
60+
return;
61+
}
62+
63+
$tokens = $phpcsFile->getTokens();
64+
65+
// String doesn't use interpolation
66+
$string = $tokens[$stackPtr]['content'];
67+
if ($this->stripVariables($string) === $string) {
68+
return;
69+
}
70+
71+
// String is not being dereferenced
72+
$skipTokens = Tokens::$emptyTokens + [\T_END_HEREDOC];
73+
$nextNonEmpty = $phpcsFile->findNext($skipTokens, ($stackPtr + 1), null, true);
74+
$nextNonEmptyCode = $tokens[$nextNonEmpty]['code'];
75+
if ($nextNonEmptyCode !== \T_OPEN_SHORT_ARRAY
76+
&& $nextNonEmptyCode !== \T_OBJECT_OPERATOR) {
77+
return;
78+
}
79+
80+
$error = 'Dereferencing of interpolated strings is not supported before PHP 8.0';
81+
$isError = true;
82+
$this->addMessage($phpcsFile, $error, $stackPtr, $isError);
83+
}
84+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
// OK: strings that don't use interpolation or dereferencing
4+
$a = 'foo';
5+
$a = "foo";
6+
$a = <<<EOS
7+
foo
8+
EOS;
9+
10+
// OK: strings that use interpolation, but not dereferencing
11+
$a = "$bar";
12+
$a = "foo$bar";
13+
$a = <<<EOS
14+
foo$bar
15+
EOS;
16+
17+
// OK: strings that use dereferencing, but not interpolation
18+
$a = 'foo'[0];
19+
$a = "foo"[0];
20+
$a = <<<EOS
21+
foo
22+
EOS[0];
23+
$a = 'foo'->baz();
24+
$a = "foo"->baz();
25+
$a = <<<EOS
26+
foo
27+
EOS->baz();
28+
29+
// strings that use interpolation and dereferencing
30+
$a = "foo$bar"[0];
31+
$a = "foo$bar"->baz();
32+
$a = <<<EOS
33+
foo$bar
34+
EOS[0];
35+
$a = <<<EOS
36+
foo$bar
37+
EOS->baz();
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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\Tests\Syntax;
12+
13+
use PHPCompatibility\Tests\BaseSniffTest;
14+
15+
/**
16+
* Test the InterpolatedStringDereferencing sniff.
17+
*
18+
* @group interpolatedStringDereferencing
19+
* @group syntax
20+
*
21+
* @covers \PHPCompatibility\Sniffs\Syntax\InterpolatedStringDereferencing
22+
*
23+
* @since 10.0
24+
*/
25+
class InterpolatedStringDereferencingUnitTest extends BaseSniffTest
26+
{
27+
/**
28+
* testInterpolatedStringDereferencing
29+
*
30+
* @dataProvider dataInterpolatedStringDereferencing
31+
*
32+
* @param int $line Line number where the error should occur.
33+
*
34+
* @return void
35+
*/
36+
public function testInterpolatedStringDereferencing($line)
37+
{
38+
$file = $this->sniffFile(__FILE__, '7.4');
39+
$this->assertError($file, $line, 'Dereferencing of interpolated strings is not supported before PHP 8.0');
40+
41+
$file = $this->sniffFile(__FILE__, '8.0');
42+
$this->assertNoViolation($file, $line);
43+
}
44+
45+
/**
46+
* dataInterpolatedStringDereferencing
47+
*
48+
* @see testInterpolatedStringDereferencing
49+
*
50+
* @return array
51+
*/
52+
public function dataInterpolatedStringDereferencing()
53+
{
54+
return [
55+
[30],
56+
[31],
57+
[33],
58+
[36],
59+
];
60+
}
61+
62+
/**
63+
* testNoFalsePositives
64+
*
65+
* @dataProvider dataNoFalsePositives
66+
*
67+
* @param int $line The line number.
68+
*
69+
* @return void
70+
*/
71+
public function testNoFalsePositives($line)
72+
{
73+
$file = $this->sniffFile(__FILE__, '7.4');
74+
$this->assertNoViolation($file, $line);
75+
}
76+
77+
/**
78+
* Data provider.
79+
*
80+
* @see testNoFalsePositives()
81+
*
82+
* @return array
83+
*/
84+
public function dataNoFalsePositives()
85+
{
86+
return [
87+
[4], // no interpolation, no dereferencing
88+
[5],
89+
[6],
90+
[11], // interpolation, no dereferencing
91+
[12],
92+
[13],
93+
[18], // dereferencing, no interpolation
94+
[19],
95+
[20],
96+
[23],
97+
[24],
98+
[25],
99+
];
100+
}
101+
}

0 commit comments

Comments
 (0)