Skip to content

Commit

Permalink
feat: add support for typed constants in AlphabeticallyOrderedConstan…
Browse files Browse the repository at this point in the history
…tsSniff (#142)

Co-authored-by: David Kurka <david.kurka@cdn77.com>
  • Loading branch information
davidxkurka and David Kurka committed Mar 11, 2024
1 parent be52c85 commit b422c03
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 22 deletions.
83 changes: 65 additions & 18 deletions src/Cdn77/Sniffs/Ordering/AlphabeticallyOrderedConstantsSniff.php
Expand Up @@ -30,10 +30,19 @@
use const T_WHITESPACE;

/**
* @phpstan-type NameWithValueShape array{
* @phpstan-type TypeNameValueShape array{
* type: TypeShape|null,
* name: NameShape,
* value: ValueShape
* }
* @phpstan-type TypeNameShape array{
* type: TypeShape|null,
* name: NameShape
* }
* @phpstan-type TypeShape array{
* content: string,
* ptr: int
* }
* @phpstan-type NameShape array{
* content: string,
* lowercaseContent: string,
Expand Down Expand Up @@ -89,7 +98,7 @@ public function process(File $phpcsFile, mixed $stackPtr): void
}
}

/** @param list<NameWithValueShape> $namesWithValues */
/** @param list<TypeNameValueShape> $namesWithValues */
private function fix(File $file, array $namesWithValues): void
{
$fixer = $file->fixer;
Expand All @@ -104,9 +113,19 @@ private function fix(File $file, array $namesWithValues): void
foreach ($namesWithValues as $key => $nameWithValue) {
$sortedNameAndValueToken = $sortedNameAndValueTokens[$key];

$typePointer = $nameWithValue['type']['ptr'] ?? null;
$namePointer = $nameWithValue['name']['ptr'];
FixerHelper::removeBetweenIncluding($file, $namePointer, $namePointer);
$fixer->addContent($namePointer, $sortedNameAndValueToken['name']['content']);
FixerHelper::removeBetweenIncluding($file, $typePointer ?? $namePointer, $namePointer);
$fixer->addContent(
$typePointer ?? $namePointer,
$sortedNameAndValueToken['type'] === null ?
$sortedNameAndValueToken['name']['content']
: sprintf(
'%s %s',
$sortedNameAndValueToken['type']['content'],
$sortedNameAndValueToken['name']['content'],
),
);

$value = $nameWithValue['value'];
FixerHelper::removeBetweenIncluding($file, $value['startPtr'], $value['endPtr']);
Expand All @@ -116,7 +135,7 @@ private function fix(File $file, array $namesWithValues): void
$fixer->endChangeset();
}

/** @return array<string, list<NameWithValueShape>> */
/** @return array<string, list<TypeNameValueShape>> */
private function findConstantNamesWithValuesByVisibility(File $phpcsFile): array
{
$constantNamesWithValues = [];
Expand All @@ -128,13 +147,13 @@ private function findConstantNamesWithValuesByVisibility(File $phpcsFile): array
}

$visibility = $this->getVisibility($phpcsFile, $stackPtr);
$constantName = $this->findConstantName($phpcsFile, $stackPtr);
$typeAndConstantName = $this->findTypeAndConstantName($phpcsFile, $stackPtr);

if ($constantName === null) {
if ($typeAndConstantName === null) {
continue;
}

$equalsTokenPointer = $this->findEqualsPointer($phpcsFile, $constantName['ptr']);
$equalsTokenPointer = $this->findEqualsPointer($phpcsFile, $typeAndConstantName['name']['ptr']);

if ($equalsTokenPointer === null) {
continue;
Expand All @@ -147,7 +166,8 @@ private function findConstantNamesWithValuesByVisibility(File $phpcsFile): array
}

$constantNamesWithValues[$visibility][] = [
'name' => $constantName,
'type' => $typeAndConstantName['type'],
'name' => $typeAndConstantName['name'],
'value' => $value,
];
}
Expand All @@ -170,25 +190,52 @@ private function getVisibility(File $phpcsFile, int $constStackPtr): string
: 'public';
}

/** @phpstan-return NameShape|null */
private function findConstantName(File $phpcsFile, int $constStackPtr): array|null
/** @phpstan-return TypeNameShape|null */
private function findTypeAndConstantName(File $phpcsFile, int $constStackPtr): array|null
{
$tokens = $phpcsFile->getTokens();
$constantNameTokenPointer = $phpcsFile->findNext(
types: Tokens::$emptyTokens,
$assignmentOperatorTokenPtr = $phpcsFile->findNext(
types: [T_EQUAL, T_SEMICOLON],
start: $constStackPtr + 1,
);

if ($assignmentOperatorTokenPtr === false || $tokens[$assignmentOperatorTokenPtr]['code'] !== T_EQUAL) {
return null;
}

$constNameTokenPtr = $phpcsFile->findPrevious(
types: Tokens::$emptyTokens,
start: $assignmentOperatorTokenPtr - 1,
end: $constStackPtr + 1,
exclude: true,
local: true,
);

if ($constantNameTokenPointer === false || $tokens[$constantNameTokenPointer]['code'] !== T_STRING) {
if ($constNameTokenPtr === false || $tokens[$constNameTokenPtr]['code'] !== T_STRING) {
return null;
}

$type = null;
$typeTokenPtr = $phpcsFile->findPrevious(
types: Tokens::$emptyTokens,
start: $constNameTokenPtr - 1,
end: $constStackPtr,
exclude: true,
);

if ($typeTokenPtr !== false && $tokens[$typeTokenPtr]['code'] === T_STRING) {
$type = [
'content' => $tokens[$typeTokenPtr]['content'],
'ptr' => $typeTokenPtr,
];
}

return [
'content' => $tokens[$constantNameTokenPointer]['content'],
'lowercaseContent' => strtolower($tokens[$constantNameTokenPointer]['content']),
'ptr' => $constantNameTokenPointer,
'type' => $type,
'name' => [
'content' => $tokens[$constNameTokenPtr]['content'],
'lowercaseContent' => strtolower($tokens[$constNameTokenPtr]['content']),
'ptr' => $constNameTokenPtr,
],
];
}

Expand Down
Expand Up @@ -16,8 +16,8 @@ final class TestClass
public const C = 'c' . PHP_EOL;
public const D = [123, 'test'];

protected const E = 'e';
protected const F = 'f';
protected const string E = 'e';
protected const int F = 123;
protected const G = 'g';
protected const Ha = 'h';
protected const HB = 'h';
Expand Down
Expand Up @@ -16,9 +16,9 @@ final class TestClass
public const D = [123, 'test'];
public const B = 'b';

protected const E = 'e';
protected const G = 'g';
protected const F = 'f';
protected const string E = 'e';
protected const int F = 123;
protected const HB = 'h';
protected const Ha = 'h';

Expand Down

0 comments on commit b422c03

Please sign in to comment.