Skip to content

Commit

Permalink
SlevomatCodingStandard.Classes.DisallowStringExpressionPropertyFetch:…
Browse files Browse the repository at this point in the history
… New sniff that disallows string expression property fetch $object->{'foo'}
  • Loading branch information
kukulich committed Apr 10, 2023
1 parent 91ab3d3 commit c4e213e
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Slevomat Coding Standard for [PHP_CodeSniffer](https://github.com/squizlabs/PHP_
- [SlevomatCodingStandard.Classes.DisallowLateStaticBindingForConstants](doc/classes.md#slevomatcodingstandardclassesdisallowlatestaticbindingforconstants-) 🔧
- [SlevomatCodingStandard.Classes.DisallowMultiConstantDefinition](doc/classes.md#slevomatcodingstandardclassesdisallowmulticonstantdefinition-) 🔧
- [SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition](doc/classes.md#slevomatcodingstandardclassesdisallowmultipropertydefinition-) 🔧
- [SlevomatCodingStandard.Classes.DisallowStringExpressionPropertyFetch](doc/classes.md#slevomatcodingstandardclassesdisallowstringexpressionpropertyfetch-) 🔧
- [SlevomatCodingStandard.Classes.EmptyLinesAroundClassBraces](doc/classes.md#slevomatcodingstandardclassesemptylinesaroundclassbraces-) 🔧
- [SlevomatCodingStandard.Classes.EnumCaseSpacing](doc/classes.md#slevomatcodingstandardclassesenumcasespacing-) 🔧
- [SlevomatCodingStandard.Classes.ForbiddenPublicProperty](doc/classes.md#slevomatcodingstandardclassesforbiddenpublicproperty)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php declare(strict_types = 1);

namespace SlevomatCodingStandard\Sniffs\Classes;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\FixerHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function preg_match;
use const T_CONSTANT_ENCAPSED_STRING;
use const T_OBJECT_OPERATOR;
use const T_OPEN_CURLY_BRACKET;
use const T_OPEN_PARENTHESIS;

class DisallowStringExpressionPropertyFetchSniff implements Sniff
{

public const CODE_DISALLOWED_STRING_EXPRESSION_PROPERTY_FETCH = 'DisallowedStringExpressionPropertyFetch';

/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return [T_OBJECT_OPERATOR];
}

/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $objectOperatorPointer
*/
public function process(File $phpcsFile, $objectOperatorPointer): void
{
$tokens = $phpcsFile->getTokens();

$curlyBracketOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $objectOperatorPointer + 1);

if ($tokens[$curlyBracketOpenerPointer]['code'] !== T_OPEN_CURLY_BRACKET) {
return;
}

$curlyBracketCloserPointer = $tokens[$curlyBracketOpenerPointer]['bracket_closer'];

if (TokenHelper::findNextExcluding(
$phpcsFile,
T_CONSTANT_ENCAPSED_STRING,
$curlyBracketOpenerPointer + 1,
$curlyBracketCloserPointer
) !== null) {
return;
}

$pointerAfterCurlyBracketCloser = TokenHelper::findNextEffective($phpcsFile, $curlyBracketCloserPointer + 1);

if ($tokens[$pointerAfterCurlyBracketCloser]['code'] === T_OPEN_PARENTHESIS) {
return;
}

if (preg_match(
'~^(["\'])([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\1$~',
$tokens[$curlyBracketOpenerPointer + 1]['content'],
$matches
) !== 1) {
return;
}

$fix = $phpcsFile->addFixableError(
'String expression property fetch is disallowed, use identifier property fetch.',
$curlyBracketOpenerPointer,
self::CODE_DISALLOWED_STRING_EXPRESSION_PROPERTY_FETCH
);

if (!$fix) {
return;
}

$phpcsFile->fixer->beginChangeset();

$phpcsFile->fixer->replaceToken($curlyBracketOpenerPointer, $matches[2]);
FixerHelper::removeBetweenIncluding($phpcsFile, $curlyBracketOpenerPointer + 1, $curlyBracketCloserPointer);

$phpcsFile->fixer->endChangeset();
}

}
8 changes: 8 additions & 0 deletions doc/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ Disallows multi constant definition.

Disallows multi property definition.

#### SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition 🔧

Disallows multi property definition.

#### SlevomatCodingStandard.Classes.DisallowStringExpressionPropertyFetch 🔧

Disallows string expression property fetch `$object->{'foo'}` when the property name is compatible with identifier access.

#### SlevomatCodingStandard.Classes.EmptyLinesAroundClassBraces 🔧

Enforces one configurable number of lines after opening class/interface/trait brace and one empty line before the closing brace.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types = 1);

namespace SlevomatCodingStandard\Sniffs\Classes;

use SlevomatCodingStandard\Sniffs\TestCase;

class DisallowStringExpressionPropertyFetchSniffTest extends TestCase
{

public function testNoErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/disallowStringExpressionPropertyFetchNoErrors.php');
self::assertNoSniffErrorInFile($report);
}

public function testErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/disallowStringExpressionPropertyFetchErrors.php');

self::assertSame(3, $report->getErrorCount());

self::assertSniffError($report, 3, DisallowStringExpressionPropertyFetchSniff::CODE_DISALLOWED_STRING_EXPRESSION_PROPERTY_FETCH);
self::assertSniffError($report, 5, DisallowStringExpressionPropertyFetchSniff::CODE_DISALLOWED_STRING_EXPRESSION_PROPERTY_FETCH);
self::assertSniffError($report, 7, DisallowStringExpressionPropertyFetchSniff::CODE_DISALLOWED_STRING_EXPRESSION_PROPERTY_FETCH);

self::assertAllFixedInFile($report);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

$a->foo;

$a->boo;

$a->foo_boo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

$a->{'foo'};

$a->{"boo"};

$a->{"foo_boo"};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

$a->foo;

$b->boo();

$c->{'coo'}();

$d->{'doo' . $c};

if (isset($a->{'not-compatible'})) {

}

0 comments on commit c4e213e

Please sign in to comment.