-
-
Notifications
You must be signed in to change notification settings - Fork 180
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SlevomatCodingStandard.Strings.DisallowVariableParsing: New sniff to …
…disallow variable parsing inside strings
- Loading branch information
Showing
6 changed files
with
355 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
SlevomatCodingStandard/Sniffs/Strings/DisallowVariableParsingSniff.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace SlevomatCodingStandard\Sniffs\Strings; | ||
|
||
use PHP_CodeSniffer\Files\File; | ||
use PHP_CodeSniffer\Sniffs\Sniff; | ||
use UnexpectedValueException; | ||
use function preg_match; | ||
use function sprintf; | ||
use const T_DOUBLE_QUOTED_STRING; | ||
use const T_HEREDOC; | ||
|
||
class DisallowVariableParsingSniff implements Sniff | ||
{ | ||
|
||
public const CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX = 'DisallowedDollarCurlySyntax'; | ||
|
||
public const CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX = 'DisallowedCurlyDollarSyntax'; | ||
|
||
public const CODE_DISALLOWED_SIMPLE_SYNTAX = 'DisallowedSimpleSyntax'; | ||
|
||
private const DOLLAR_CURLY_SYNTAX_PATTERN = '~\${[\w\[\]]+}~'; | ||
private const CURLY_DOLLAR_SYNTAX_PATTERN = '~{\$[\w\[\]\->]+}~'; | ||
private const SIMPLE_SYNTAX_PATTERN = '~(?<!{)\$[\w\[\]\->]+(?!})~'; | ||
|
||
/** @var bool */ | ||
public $disallowDollarCurlySyntax = true; | ||
|
||
/** @var bool */ | ||
public $disallowCurlyDollarSyntax = false; | ||
|
||
/** @var bool */ | ||
public $disallowSimpleSyntax = false; | ||
|
||
/** | ||
* @return array<int, (int|string)> | ||
*/ | ||
public function register(): array | ||
{ | ||
return [ | ||
T_DOUBLE_QUOTED_STRING, | ||
T_HEREDOC, | ||
]; | ||
} | ||
|
||
/** | ||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint | ||
* @param int $stringPointer | ||
*/ | ||
public function process(File $phpcsFile, $stringPointer): void | ||
{ | ||
if (!$this->disallowDollarCurlySyntax && !$this->disallowCurlyDollarSyntax && !$this->disallowSimpleSyntax) { | ||
throw new UnexpectedValueException('No option is set.'); | ||
} | ||
|
||
$tokens = $phpcsFile->getTokens(); | ||
$tokenContent = $tokens[$stringPointer]['content']; | ||
|
||
// Cover strings where ${...} syntax is used | ||
if ($this->disallowDollarCurlySyntax && preg_match(self::DOLLAR_CURLY_SYNTAX_PATTERN, $tokenContent, $invalidFragments) === 1) { | ||
foreach ($invalidFragments as $fragment) { | ||
$phpcsFile->addError( | ||
sprintf( | ||
'Using variable syntax "${...}" inside string is disallowed as syntax "${...}" is deprecated as of PHP 8.2, found "%s".', | ||
$fragment | ||
), | ||
$stringPointer, | ||
self::CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX | ||
); | ||
} | ||
} | ||
|
||
// Cover strings where {$...} syntax is used | ||
if ($this->disallowCurlyDollarSyntax && preg_match(self::CURLY_DOLLAR_SYNTAX_PATTERN, $tokenContent, $invalidFragments) === 1) { | ||
foreach ($invalidFragments as $fragment) { | ||
$phpcsFile->addError( | ||
sprintf( | ||
'Using variable syntax "{$...}" inside string is disallowed, found "%s".', | ||
$fragment | ||
), | ||
$stringPointer, | ||
self::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX | ||
); | ||
} | ||
} | ||
|
||
// Cover strings where $... syntax is used | ||
// phpcs:disable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed | ||
if ($this->disallowSimpleSyntax && preg_match(self::SIMPLE_SYNTAX_PATTERN, $tokenContent, $invalidFragments) === 1) { | ||
foreach ($invalidFragments as $fragment) { | ||
$phpcsFile->addError( | ||
sprintf( | ||
'Using variable syntax "$..." inside string is disallowed, found "%s".', | ||
$fragment | ||
), | ||
$stringPointer, | ||
self::CODE_DISALLOWED_SIMPLE_SYNTAX | ||
); | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
## Strings | ||
|
||
#### SlevomatCodingStandard.Strings.DisallowVariableParsing | ||
|
||
Disallows variable parsing inside strings. | ||
|
||
Sniff provides the following settings: | ||
|
||
* `disallowDollarCurlySyntax`: disallows usage of `${...}`, enabled by default. | ||
* `disallowCurlyDollarSyntax`: disallows usage of `{$...}`, disabled by default. | ||
* `disallowSimpleSyntax`: disallows usage of `$...`, disabled by default. |
183 changes: 183 additions & 0 deletions
183
tests/Sniffs/Strings/DisallowVariableParsingSniffTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace SlevomatCodingStandard\Sniffs\Strings; | ||
|
||
use SlevomatCodingStandard\Sniffs\TestCase; | ||
use UnexpectedValueException; | ||
|
||
class DisallowVariableParsingSniffTest extends TestCase | ||
{ | ||
|
||
public function testNoErrors(): void | ||
{ | ||
$report = self::checkFile(__DIR__ . '/data/disallowVariableParsingNoError.php'); | ||
self::assertNoSniffErrorInFile($report); | ||
} | ||
|
||
public function testErrorsDollarCurlySyntax(): void | ||
{ | ||
$report = self::checkFile( | ||
__DIR__ . '/data/disallowVariableParsingErrors.php', | ||
[ | ||
'disallowDollarCurlySyntax' => true, | ||
'disallowCurlyDollarSyntax' => false, | ||
'disallowSimpleSyntax' => false, | ||
] | ||
); | ||
|
||
self::assertSame(4, $report->getErrorCount()); | ||
|
||
self::assertSniffError( | ||
$report, | ||
10, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX, | ||
'Using variable syntax "${...}" inside string is disallowed as syntax "${...}" is deprecated as of PHP 8.2, found "${simpleString}".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
11, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX, | ||
'Using variable syntax "${...}" inside string is disallowed as syntax "${...}" is deprecated as of PHP 8.2, found "${array[1]}".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
17, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX, | ||
'Using variable syntax "${...}" inside string is disallowed as syntax "${...}" is deprecated as of PHP 8.2, found "${simpleString}".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
18, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX, | ||
'Using variable syntax "${...}" inside string is disallowed as syntax "${...}" is deprecated as of PHP 8.2, found "${array[1]}".' | ||
); | ||
} | ||
|
||
public function testErrorsCurlyDollarSyntax(): void | ||
{ | ||
$report = self::checkFile( | ||
__DIR__ . '/data/disallowVariableParsingErrors.php', | ||
[ | ||
'disallowCurlyDollarSyntax' => true, | ||
'disallowDollarCurlySyntax' => false, | ||
'disallowSimpleSyntax' => false, | ||
] | ||
); | ||
|
||
self::assertSame(6, $report->getErrorCount()); | ||
|
||
self::assertSniffError( | ||
$report, | ||
22, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX, | ||
'Using variable syntax "{$...}" inside string is disallowed, found "{$simpleString}".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
23, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX, | ||
'Using variable syntax "{$...}" inside string is disallowed, found "{$array[1]}".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
24, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX, | ||
'Using variable syntax "{$...}" inside string is disallowed, found "{$object->name}".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
31, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX, | ||
'Using variable syntax "{$...}" inside string is disallowed, found "{$simpleString}".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
32, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX, | ||
'Using variable syntax "{$...}" inside string is disallowed, found "{$array[1]}".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
33, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX, | ||
'Using variable syntax "{$...}" inside string is disallowed, found "{$object->name}".' | ||
); | ||
} | ||
|
||
public function testErrorsSimpleSyntax(): void | ||
{ | ||
$report = self::checkFile( | ||
__DIR__ . '/data/disallowVariableParsingErrors.php', | ||
[ | ||
'disallowSimpleSyntax' => true, | ||
'disallowDollarCurlySyntax' => false, | ||
'disallowCurlyDollarSyntax' => false, | ||
] | ||
); | ||
|
||
self::assertSame(6, $report->getErrorCount()); | ||
|
||
self::assertSniffError( | ||
$report, | ||
37, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_SIMPLE_SYNTAX, | ||
'Using variable syntax "$..." inside string is disallowed, found "$simpleString".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
38, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_SIMPLE_SYNTAX, | ||
'Using variable syntax "$..." inside string is disallowed, found "$array[1]".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
39, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_SIMPLE_SYNTAX, | ||
'Using variable syntax "$..." inside string is disallowed, found "$object->name".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
46, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_SIMPLE_SYNTAX, | ||
'Using variable syntax "$..." inside string is disallowed, found "$simpleString".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
47, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_SIMPLE_SYNTAX, | ||
'Using variable syntax "$..." inside string is disallowed, found "$array[1]".' | ||
); | ||
|
||
self::assertSniffError( | ||
$report, | ||
48, | ||
DisallowVariableParsingSniff::CODE_DISALLOWED_SIMPLE_SYNTAX, | ||
'Using variable syntax "$..." inside string is disallowed, found "$object->name".' | ||
); | ||
} | ||
|
||
public function testNoOptionIsSet(): void | ||
{ | ||
$this->expectException(UnexpectedValueException::class); | ||
$this->expectExceptionMessage('No option is set.'); | ||
|
||
self::checkFile(__DIR__ . '/data/disallowVariableParsingNoError.php', [ | ||
'disallowDollarCurlySyntax' => false, | ||
'disallowCurlyDollarSyntax' => false, | ||
'disallowSimpleSyntax' => false, | ||
]); | ||
} | ||
|
||
} |
49 changes: 49 additions & 0 deletions
49
tests/Sniffs/Strings/data/disallowVariableParsingErrors.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
$simpleString = 'foo'; | ||
$array = ['foo', 'bar', 'baz']; | ||
|
||
$object = new \stdClass(); | ||
$object->name = 'foobar'; | ||
|
||
// Covers strings where ${...} syntax is used | ||
$dollarCurlySyntaxDoubleQuotedScalar = "Some double quoted string with scalar variable ${simpleString}."; | ||
$dollarCurlySyntaxDoubleQuotedArrayItem = "Some double quoted string with array item variable ${array[1]}."; | ||
|
||
$dollarCurlySyntaxSingleQuotedScalar = 'Some single quoted string with scalar variable ${simpleString}.'; | ||
$dollarCurlySyntaxSingleQuotedArrayItem = 'Some single quoted string with array item variable ${array[1]}.'; | ||
|
||
$dollarCurlySyntaxHereDoc = <<<EOT | ||
Some heredoc line with scalar variable ${simpleString} | ||
Some heredoc line with array item variable ${array[1]} | ||
EOT; | ||
|
||
// Covers strings where {$...} syntax is used | ||
$curlyDollarSyntaxDoubleQuotedScalar = "Some double quoted string with scalar variable {$simpleString}."; | ||
$curlyDollarSyntaxDoubleQuotedArrayItem = "Some double quoted string with array item variable {$array[1]}."; | ||
$curlyDollarSyntaxDoubleQuotedObject = "Some double quoted string with object variable {$object->name}."; | ||
|
||
$curlyDollarSyntaxSingleQuotedScalar = 'Some single quoted string with scalar variable {$simpleString}.'; | ||
$curlyDollarSyntaxSingleQuotedArrayItem = 'Some single quoted string with array item variable {$array[1]}.'; | ||
$curlyDollarSyntaxSingleQuotedObject = 'Some single quoted string with object variable {$object->name}.'; | ||
|
||
$curlyDollarSyntaxHereDoc = <<<EOT | ||
Some heredoc line with scalar variable {$simpleString} | ||
Some heredoc line with array item variable {$array[1]} | ||
Some heredoc line with object variable {$object->name} | ||
EOT; | ||
|
||
// Covers strings where $... syntax is used | ||
$simpleSyntaxDoubleQuotedScalar = "Some double quoted string with scalar variable $simpleString."; | ||
$simpleSyntaxDoubleQuotedArrayItem = "Some double quoted string with array item variable $array[1]."; | ||
$simpleSyntaxDoubleQuotedObject = "Some double quoted string with object variable $object->name."; | ||
|
||
$simpleSyntaxSingleQuotedScalar = 'Some single quoted string with scalar variable $simpleString.'; | ||
$simpleSyntaxSingleQuotedArrayItem = 'Some single quoted string with array item variable $array[1].'; | ||
$simpleSyntaxSingleQuotedObject = 'Some single quoted string with object variable $object->name.'; | ||
|
||
$simpleSyntaxHereDoc = <<<EOT | ||
Some heredoc line with scalar variable $simpleString | ||
Some heredoc line with array item variable $array[1] | ||
Some heredoc line with object variable $object->name | ||
EOT; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
$noVariablesDoubleQuoted = "Some double quoted string without variables"; | ||
$noVariablesSingleQuoted = 'Some single quoted string without variables'; | ||
|
||
$noVariablesHereDoc = <<<EOT | ||
Some heredoc line without variables | ||
EOT; |