Missing dependancies
This commit is contained in:
Vendored
+253
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @author Sullivan Senechal <soullivaneuh@gmail.com>
|
||||
*/
|
||||
final class ClassKeywordRemoveFixer extends AbstractFixer implements DeprecatedFixerInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $imports = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Converts `::class` keywords to FQCN strings.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
|
||||
use Foo\Bar\Baz;
|
||||
|
||||
$className = Baz::class;
|
||||
'
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSuccessorsNames(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before NoUnusedImportsFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(CT::T_CLASS_CONSTANT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$namespacesAnalyzer = new NamespacesAnalyzer();
|
||||
|
||||
$previousNamespaceScopeEndIndex = 0;
|
||||
foreach ($namespacesAnalyzer->getDeclarations($tokens) as $declaration) {
|
||||
$this->replaceClassKeywordsSection($tokens, '', $previousNamespaceScopeEndIndex, $declaration->getStartIndex());
|
||||
$this->replaceClassKeywordsSection($tokens, $declaration->getFullName(), $declaration->getStartIndex(), $declaration->getScopeEndIndex());
|
||||
$previousNamespaceScopeEndIndex = $declaration->getScopeEndIndex();
|
||||
}
|
||||
|
||||
$this->replaceClassKeywordsSection($tokens, '', $previousNamespaceScopeEndIndex, $tokens->count() - 1);
|
||||
}
|
||||
|
||||
private function storeImports(Tokens $tokens, int $startIndex, int $endIndex): void
|
||||
{
|
||||
$tokensAnalyzer = new TokensAnalyzer($tokens);
|
||||
$this->imports = [];
|
||||
|
||||
/** @var int $index */
|
||||
foreach ($tokensAnalyzer->getImportUseIndexes() as $index) {
|
||||
if ($index < $startIndex || $index > $endIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$import = '';
|
||||
while ($index = $tokens->getNextMeaningfulToken($index)) {
|
||||
if ($tokens[$index]->equalsAny([';', [CT::T_GROUP_IMPORT_BRACE_OPEN]]) || $tokens[$index]->isGivenKind(T_AS)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$import .= $tokens[$index]->getContent();
|
||||
}
|
||||
|
||||
// Imports group (PHP 7 spec)
|
||||
if ($tokens[$index]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) {
|
||||
$groupEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_GROUP_IMPORT_BRACE, $index);
|
||||
$groupImports = array_map(
|
||||
static function (string $import): string {
|
||||
return trim($import);
|
||||
},
|
||||
explode(',', $tokens->generatePartialCode($index + 1, $groupEndIndex - 1))
|
||||
);
|
||||
foreach ($groupImports as $groupImport) {
|
||||
$groupImportParts = array_map(static function (string $import): string {
|
||||
return trim($import);
|
||||
}, explode(' as ', $groupImport));
|
||||
if (2 === \count($groupImportParts)) {
|
||||
$this->imports[$groupImportParts[1]] = $import.$groupImportParts[0];
|
||||
} else {
|
||||
$this->imports[] = $import.$groupImport;
|
||||
}
|
||||
}
|
||||
} elseif ($tokens[$index]->isGivenKind(T_AS)) {
|
||||
$aliasIndex = $tokens->getNextMeaningfulToken($index);
|
||||
$alias = $tokens[$aliasIndex]->getContent();
|
||||
$this->imports[$alias] = $import;
|
||||
} else {
|
||||
$this->imports[] = $import;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function replaceClassKeywordsSection(Tokens $tokens, string $namespace, int $startIndex, int $endIndex): void
|
||||
{
|
||||
if ($endIndex - $startIndex < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->storeImports($tokens, $startIndex, $endIndex);
|
||||
|
||||
$ctClassTokens = $tokens->findGivenKind(CT::T_CLASS_CONSTANT, $startIndex, $endIndex);
|
||||
foreach (array_reverse(array_keys($ctClassTokens)) as $classIndex) {
|
||||
$this->replaceClassKeyword($tokens, $namespace, $classIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private function replaceClassKeyword(Tokens $tokens, string $namespacePrefix, int $classIndex): void
|
||||
{
|
||||
$classEndIndex = $tokens->getPrevMeaningfulToken($classIndex);
|
||||
$classEndIndex = $tokens->getPrevMeaningfulToken($classEndIndex);
|
||||
|
||||
if (!$tokens[$classEndIndex]->isGivenKind(T_STRING)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$classEndIndex]->equalsAny([[T_STRING, 'self'], [T_STATIC, 'static'], [T_STRING, 'parent']], false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classBeginIndex = $classEndIndex;
|
||||
while (true) {
|
||||
$prev = $tokens->getPrevMeaningfulToken($classBeginIndex);
|
||||
if (!$tokens[$prev]->isGivenKind([T_NS_SEPARATOR, T_STRING])) {
|
||||
break;
|
||||
}
|
||||
|
||||
$classBeginIndex = $prev;
|
||||
}
|
||||
|
||||
$classString = $tokens->generatePartialCode(
|
||||
$tokens[$classBeginIndex]->isGivenKind(T_NS_SEPARATOR)
|
||||
? $tokens->getNextMeaningfulToken($classBeginIndex)
|
||||
: $classBeginIndex,
|
||||
$classEndIndex
|
||||
);
|
||||
|
||||
$classImport = false;
|
||||
if ($tokens[$classBeginIndex]->isGivenKind(T_NS_SEPARATOR)) {
|
||||
$namespacePrefix = '';
|
||||
} else {
|
||||
foreach ($this->imports as $alias => $import) {
|
||||
if ($classString === $alias) {
|
||||
$classImport = $import;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$classStringArray = explode('\\', $classString);
|
||||
$namespaceToTest = $classStringArray[0];
|
||||
|
||||
if (0 === strcmp($namespaceToTest, substr($import, -\strlen($namespaceToTest)))) {
|
||||
$classImport = $import;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = $classBeginIndex; $i <= $classIndex; ++$i) {
|
||||
if (!$tokens[$i]->isComment() && !($tokens[$i]->isWhitespace() && str_contains($tokens[$i]->getContent(), "\n"))) {
|
||||
$tokens->clearAt($i);
|
||||
}
|
||||
}
|
||||
|
||||
$tokens->insertAt($classBeginIndex, new Token([
|
||||
T_CONSTANT_ENCAPSED_STRING,
|
||||
"'".$this->makeClassFQN($namespacePrefix, $classImport, $classString)."'",
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param false|string $classImport
|
||||
*/
|
||||
private function makeClassFQN(string $namespacePrefix, $classImport, string $classString): string
|
||||
{
|
||||
if (false === $classImport) {
|
||||
return ('' !== $namespacePrefix ? ($namespacePrefix.'\\') : '').$classString;
|
||||
}
|
||||
|
||||
$classStringArray = explode('\\', $classString);
|
||||
$classStringLength = \count($classStringArray);
|
||||
$classImportArray = explode('\\', $classImport);
|
||||
$classImportLength = \count($classImportArray);
|
||||
|
||||
if (1 === $classStringLength) {
|
||||
return $classImport;
|
||||
}
|
||||
|
||||
return implode('\\', array_merge(
|
||||
\array_slice($classImportArray, 0, $classImportLength - $classStringLength + 1),
|
||||
$classStringArray
|
||||
));
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class CombineConsecutiveIssetsFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Using `isset($var) &&` multiple times should be done in one call.',
|
||||
[new CodeSample("<?php\n\$a = isset(\$a) && isset(\$b);\n")]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before MultilineWhitespaceBeforeSemicolonsFixer, NoSinglelineWhitespaceBeforeSemicolonsFixer, NoSpacesInsideParenthesisFixer, NoTrailingWhitespaceFixer, NoWhitespaceInBlankLineFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isAllTokenKindsFound([T_ISSET, T_BOOLEAN_AND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$tokenCount = $tokens->count();
|
||||
|
||||
for ($index = 1; $index < $tokenCount; ++$index) {
|
||||
if (!$tokens[$index]->isGivenKind(T_ISSET)
|
||||
|| !$tokens[$tokens->getPrevMeaningfulToken($index)]->equalsAny(['(', '{', ';', '=', [T_OPEN_TAG], [T_BOOLEAN_AND], [T_BOOLEAN_OR]])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$issetInfo = $this->getIssetInfo($tokens, $index);
|
||||
$issetCloseBraceIndex = end($issetInfo); // ')' token
|
||||
$insertLocation = prev($issetInfo) + 1; // one index after the previous meaningful of ')'
|
||||
|
||||
$booleanAndTokenIndex = $tokens->getNextMeaningfulToken($issetCloseBraceIndex);
|
||||
|
||||
while ($tokens[$booleanAndTokenIndex]->isGivenKind(T_BOOLEAN_AND)) {
|
||||
$issetIndex = $tokens->getNextMeaningfulToken($booleanAndTokenIndex);
|
||||
if (!$tokens[$issetIndex]->isGivenKind(T_ISSET)) {
|
||||
$index = $issetIndex;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// fetch info about the 'isset' statement that we're merging
|
||||
$nextIssetInfo = $this->getIssetInfo($tokens, $issetIndex);
|
||||
|
||||
$nextMeaningfulTokenIndex = $tokens->getNextMeaningfulToken(end($nextIssetInfo));
|
||||
$nextMeaningfulToken = $tokens[$nextMeaningfulTokenIndex];
|
||||
|
||||
if (!$nextMeaningfulToken->equalsAny([')', '}', ';', [T_CLOSE_TAG], [T_BOOLEAN_AND], [T_BOOLEAN_OR]])) {
|
||||
$index = $nextMeaningfulTokenIndex;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// clone what we want to move, do not clone '(' and ')' of the 'isset' statement we're merging
|
||||
$clones = $this->getTokenClones($tokens, \array_slice($nextIssetInfo, 1, -1));
|
||||
|
||||
// clean up now the tokens of the 'isset' statement we're merging
|
||||
$this->clearTokens($tokens, array_merge($nextIssetInfo, [$issetIndex, $booleanAndTokenIndex]));
|
||||
|
||||
// insert the tokens to create the new statement
|
||||
array_unshift($clones, new Token(','), new Token([T_WHITESPACE, ' ']));
|
||||
$tokens->insertAt($insertLocation, $clones);
|
||||
|
||||
// correct some counts and offset based on # of tokens inserted
|
||||
$numberOfTokensInserted = \count($clones);
|
||||
$tokenCount += $numberOfTokensInserted;
|
||||
$issetCloseBraceIndex += $numberOfTokensInserted;
|
||||
$insertLocation += $numberOfTokensInserted;
|
||||
|
||||
$booleanAndTokenIndex = $tokens->getNextMeaningfulToken($issetCloseBraceIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $indices
|
||||
*/
|
||||
private function clearTokens(Tokens $tokens, array $indices): void
|
||||
{
|
||||
foreach ($indices as $index) {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index of T_ISSET
|
||||
*
|
||||
* @return int[] indices of meaningful tokens belonging to the isset statement
|
||||
*/
|
||||
private function getIssetInfo(Tokens $tokens, int $index): array
|
||||
{
|
||||
$openIndex = $tokens->getNextMeaningfulToken($index);
|
||||
|
||||
$braceOpenCount = 1;
|
||||
$meaningfulTokenIndices = [$openIndex];
|
||||
|
||||
for ($i = $openIndex + 1;; ++$i) {
|
||||
if ($tokens[$i]->isWhitespace() || $tokens[$i]->isComment()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$meaningfulTokenIndices[] = $i;
|
||||
|
||||
if ($tokens[$i]->equals(')')) {
|
||||
--$braceOpenCount;
|
||||
if (0 === $braceOpenCount) {
|
||||
break;
|
||||
}
|
||||
} elseif ($tokens[$i]->equals('(')) {
|
||||
++$braceOpenCount;
|
||||
}
|
||||
}
|
||||
|
||||
return $meaningfulTokenIndices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $indices
|
||||
*
|
||||
* @return Token[]
|
||||
*/
|
||||
private function getTokenClones(Tokens $tokens, array $indices): array
|
||||
{
|
||||
$clones = [];
|
||||
|
||||
foreach ($indices as $i) {
|
||||
$clones[] = clone $tokens[$i];
|
||||
}
|
||||
|
||||
return $clones;
|
||||
}
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class CombineConsecutiveUnsetsFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Calling `unset` on multiple items should be done in one call.',
|
||||
[new CodeSample("<?php\nunset(\$a); unset(\$b);\n")]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before NoExtraBlankLinesFixer, NoTrailingWhitespaceFixer, NoWhitespaceInBlankLineFixer, SpaceAfterSemicolonFixer.
|
||||
* Must run after NoEmptyStatementFixer, NoUnsetOnPropertyFixer, NoUselessElseFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 24;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_UNSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
|
||||
if (!$tokens[$index]->isGivenKind(T_UNSET)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$previousUnsetCall = $this->getPreviousUnsetCall($tokens, $index);
|
||||
if (\is_int($previousUnsetCall)) {
|
||||
$index = $previousUnsetCall;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
[$previousUnset, , $previousUnsetBraceEnd] = $previousUnsetCall;
|
||||
|
||||
// Merge the tokens inside the 'unset' call into the previous one 'unset' call.
|
||||
$tokensAddCount = $this->moveTokens(
|
||||
$tokens,
|
||||
$nextUnsetContentStart = $tokens->getNextTokenOfKind($index, ['(']),
|
||||
$nextUnsetContentEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextUnsetContentStart),
|
||||
$previousUnsetBraceEnd - 1
|
||||
);
|
||||
|
||||
if (!$tokens[$previousUnsetBraceEnd]->isWhitespace()) {
|
||||
$tokens->insertAt($previousUnsetBraceEnd, new Token([T_WHITESPACE, ' ']));
|
||||
++$tokensAddCount;
|
||||
}
|
||||
|
||||
$tokens->insertAt($previousUnsetBraceEnd, new Token(','));
|
||||
++$tokensAddCount;
|
||||
|
||||
// Remove 'unset', '(', ')' and (possibly) ';' from the merged 'unset' call.
|
||||
$this->clearOffsetTokens($tokens, $tokensAddCount, [$index, $nextUnsetContentStart, $nextUnsetContentEnd]);
|
||||
|
||||
$nextUnsetSemicolon = $tokens->getNextMeaningfulToken($nextUnsetContentEnd);
|
||||
if (null !== $nextUnsetSemicolon && $tokens[$nextUnsetSemicolon]->equals(';')) {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($nextUnsetSemicolon);
|
||||
}
|
||||
|
||||
$index = $previousUnset + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $indices
|
||||
*/
|
||||
private function clearOffsetTokens(Tokens $tokens, int $offset, array $indices): void
|
||||
{
|
||||
foreach ($indices as $index) {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($index + $offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a previous call to unset directly before the index.
|
||||
*
|
||||
* Returns an array with
|
||||
* * unset index
|
||||
* * opening brace index
|
||||
* * closing brace index
|
||||
* * end semicolon index
|
||||
*
|
||||
* Or the index to where the method looked for a call.
|
||||
*
|
||||
* @return int|int[]
|
||||
*/
|
||||
private function getPreviousUnsetCall(Tokens $tokens, int $index)
|
||||
{
|
||||
$previousUnsetSemicolon = $tokens->getPrevMeaningfulToken($index);
|
||||
if (null === $previousUnsetSemicolon) {
|
||||
return $index;
|
||||
}
|
||||
|
||||
if (!$tokens[$previousUnsetSemicolon]->equals(';')) {
|
||||
return $previousUnsetSemicolon;
|
||||
}
|
||||
|
||||
$previousUnsetBraceEnd = $tokens->getPrevMeaningfulToken($previousUnsetSemicolon);
|
||||
if (null === $previousUnsetBraceEnd) {
|
||||
return $index;
|
||||
}
|
||||
|
||||
if (!$tokens[$previousUnsetBraceEnd]->equals(')')) {
|
||||
return $previousUnsetBraceEnd;
|
||||
}
|
||||
|
||||
$previousUnsetBraceStart = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $previousUnsetBraceEnd);
|
||||
$previousUnset = $tokens->getPrevMeaningfulToken($previousUnsetBraceStart);
|
||||
if (null === $previousUnset) {
|
||||
return $index;
|
||||
}
|
||||
|
||||
if (!$tokens[$previousUnset]->isGivenKind(T_UNSET)) {
|
||||
return $previousUnset;
|
||||
}
|
||||
|
||||
return [
|
||||
$previousUnset,
|
||||
$previousUnsetBraceStart,
|
||||
$previousUnsetBraceEnd,
|
||||
$previousUnsetSemicolon,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $start Index previous of the first token to move
|
||||
* @param int $end Index of the last token to move
|
||||
* @param int $to Upper boundary index
|
||||
*
|
||||
* @return int Number of tokens inserted
|
||||
*/
|
||||
private function moveTokens(Tokens $tokens, int $start, int $end, int $to): int
|
||||
{
|
||||
$added = 0;
|
||||
for ($i = $start + 1; $i < $end; $i += 2) {
|
||||
if ($tokens[$i]->isWhitespace() && $tokens[$to + 1]->isWhitespace()) {
|
||||
$tokens[$to + 1] = new Token([T_WHITESPACE, $tokens[$to + 1]->getContent().$tokens[$i]->getContent()]);
|
||||
} else {
|
||||
$tokens->insertAt(++$to, clone $tokens[$i]);
|
||||
++$end;
|
||||
++$added;
|
||||
}
|
||||
|
||||
$tokens->clearAt($i + 1);
|
||||
}
|
||||
|
||||
return $added;
|
||||
}
|
||||
}
|
||||
Vendored
+148
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*/
|
||||
final class DeclareEqualNormalizeFixer extends AbstractFixer implements ConfigurableFixerInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
parent::configure($configuration);
|
||||
|
||||
$this->callback = 'none' === $this->configuration['space'] ? 'removeWhitespaceAroundToken' : 'ensureWhitespaceAroundToken';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Equal sign in declare statement should be surrounded by spaces or not following configuration.',
|
||||
[
|
||||
new CodeSample("<?php\ndeclare(ticks = 1);\n"),
|
||||
new CodeSample("<?php\ndeclare(ticks=1);\n", ['space' => 'single']),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run after DeclareStrictTypesFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_DECLARE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$callback = $this->callback;
|
||||
for ($index = 0, $count = $tokens->count(); $index < $count - 6; ++$index) {
|
||||
if (!$tokens[$index]->isGivenKind(T_DECLARE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$openParenthesisIndex = $tokens->getNextMeaningfulToken($index);
|
||||
$closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex);
|
||||
|
||||
for ($i = $closeParenthesisIndex; $i > $openParenthesisIndex; --$i) {
|
||||
if ($tokens[$i]->equals('=')) {
|
||||
$this->{$callback}($tokens, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('space', 'Spacing to apply around the equal sign.'))
|
||||
->setAllowedValues(['single', 'none'])
|
||||
->setDefault('none')
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index of `=` token
|
||||
*/
|
||||
private function ensureWhitespaceAroundToken(Tokens $tokens, int $index): void
|
||||
{
|
||||
if ($tokens[$index + 1]->isWhitespace()) {
|
||||
if (' ' !== $tokens[$index + 1]->getContent()) {
|
||||
$tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
|
||||
}
|
||||
} else {
|
||||
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
|
||||
}
|
||||
|
||||
if ($tokens[$index - 1]->isWhitespace()) {
|
||||
if (' ' !== $tokens[$index - 1]->getContent() && !$tokens[$tokens->getPrevNonWhitespace($index - 1)]->isComment()) {
|
||||
$tokens[$index - 1] = new Token([T_WHITESPACE, ' ']);
|
||||
}
|
||||
} else {
|
||||
$tokens->insertAt($index, new Token([T_WHITESPACE, ' ']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index of `=` token
|
||||
*/
|
||||
private function removeWhitespaceAroundToken(Tokens $tokens, int $index): void
|
||||
{
|
||||
if (!$tokens[$tokens->getPrevNonWhitespace($index)]->isComment()) {
|
||||
$tokens->removeLeadingWhitespace($index);
|
||||
}
|
||||
|
||||
$tokens->removeTrailingWhitespace($index);
|
||||
}
|
||||
}
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class DeclareParenthesesFixer extends AbstractFixer
|
||||
{
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'There must not be spaces around `declare` statement parentheses.',
|
||||
[new CodeSample("<?php declare ( strict_types=1 );\n")]
|
||||
);
|
||||
}
|
||||
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_DECLARE);
|
||||
}
|
||||
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
|
||||
$token = $tokens[$index];
|
||||
|
||||
if (!$token->isGivenKind(T_DECLARE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokens->removeTrailingWhitespace($index);
|
||||
|
||||
$startParenthesisIndex = $tokens->getNextTokenOfKind($index, ['(']);
|
||||
$tokens->removeTrailingWhitespace($startParenthesisIndex);
|
||||
|
||||
$endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex);
|
||||
$tokens->removeLeadingWhitespace($endParenthesisIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFunctionReferenceFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Vladimir Reznichenko <kalessil@gmail.com>
|
||||
*/
|
||||
final class DirConstantFixer extends AbstractFunctionReferenceFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Replaces `dirname(__FILE__)` expression with equivalent `__DIR__` constant.',
|
||||
[new CodeSample("<?php\n\$a = dirname(__FILE__);\n")],
|
||||
null,
|
||||
'Risky when the function `dirname` is overridden.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isAllTokenKindsFound([T_STRING, T_FILE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before CombineNestedDirnameFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 40;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$currIndex = 0;
|
||||
|
||||
do {
|
||||
$boundaries = $this->find('dirname', $tokens, $currIndex, $tokens->count() - 1);
|
||||
if (null === $boundaries) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$functionNameIndex, $openParenthesis, $closeParenthesis] = $boundaries;
|
||||
|
||||
// analysing cursor shift, so nested expressions kept processed
|
||||
$currIndex = $openParenthesis;
|
||||
|
||||
// ensure __FILE__ is in between (...)
|
||||
|
||||
$fileCandidateRightIndex = $tokens->getPrevMeaningfulToken($closeParenthesis);
|
||||
$trailingCommaIndex = null;
|
||||
|
||||
if ($tokens[$fileCandidateRightIndex]->equals(',')) {
|
||||
$trailingCommaIndex = $fileCandidateRightIndex;
|
||||
$fileCandidateRightIndex = $tokens->getPrevMeaningfulToken($fileCandidateRightIndex);
|
||||
}
|
||||
|
||||
$fileCandidateRight = $tokens[$fileCandidateRightIndex];
|
||||
|
||||
if (!$fileCandidateRight->isGivenKind(T_FILE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fileCandidateLeftIndex = $tokens->getNextMeaningfulToken($openParenthesis);
|
||||
$fileCandidateLeft = $tokens[$fileCandidateLeftIndex];
|
||||
|
||||
if (!$fileCandidateLeft->isGivenKind(T_FILE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get rid of root namespace when it used
|
||||
$namespaceCandidateIndex = $tokens->getPrevMeaningfulToken($functionNameIndex);
|
||||
$namespaceCandidate = $tokens[$namespaceCandidateIndex];
|
||||
|
||||
if ($namespaceCandidate->isGivenKind(T_NS_SEPARATOR)) {
|
||||
$tokens->removeTrailingWhitespace($namespaceCandidateIndex);
|
||||
$tokens->clearAt($namespaceCandidateIndex);
|
||||
}
|
||||
|
||||
if (null !== $trailingCommaIndex) {
|
||||
if (!$tokens[$tokens->getNextNonWhitespace($trailingCommaIndex)]->isComment()) {
|
||||
$tokens->removeTrailingWhitespace($trailingCommaIndex);
|
||||
}
|
||||
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($trailingCommaIndex);
|
||||
}
|
||||
|
||||
// closing parenthesis removed with leading spaces
|
||||
if (!$tokens[$tokens->getNextNonWhitespace($closeParenthesis)]->isComment()) {
|
||||
$tokens->removeLeadingWhitespace($closeParenthesis);
|
||||
}
|
||||
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($closeParenthesis);
|
||||
|
||||
// opening parenthesis removed with trailing and leading spaces
|
||||
if (!$tokens[$tokens->getNextNonWhitespace($openParenthesis)]->isComment()) {
|
||||
$tokens->removeLeadingWhitespace($openParenthesis);
|
||||
}
|
||||
|
||||
$tokens->removeTrailingWhitespace($openParenthesis);
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($openParenthesis);
|
||||
|
||||
// replace constant and remove function name
|
||||
$tokens[$fileCandidateLeftIndex] = new Token([T_DIR, '__DIR__']);
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($functionNameIndex);
|
||||
} while (null !== $currIndex);
|
||||
}
|
||||
}
|
||||
Vendored
+186
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
* @author Kuba Werłos <werlos@gmail.com>
|
||||
*/
|
||||
final class ErrorSuppressionFixer extends AbstractFixer implements ConfigurableFixerInterface
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const OPTION_MUTE_DEPRECATION_ERROR = 'mute_deprecation_error';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const OPTION_NOISE_REMAINING_USAGES = 'noise_remaining_usages';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const OPTION_NOISE_REMAINING_USAGES_EXCLUDE = 'noise_remaining_usages_exclude';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Error control operator should be added to deprecation notices and/or removed from other cases.',
|
||||
[
|
||||
new CodeSample("<?php\ntrigger_error('Warning.', E_USER_DEPRECATED);\n"),
|
||||
new CodeSample(
|
||||
"<?php\n@mkdir(\$dir);\n@unlink(\$path);\n",
|
||||
[self::OPTION_NOISE_REMAINING_USAGES => true]
|
||||
),
|
||||
new CodeSample(
|
||||
"<?php\n@mkdir(\$dir);\n@unlink(\$path);\n",
|
||||
[
|
||||
self::OPTION_NOISE_REMAINING_USAGES => true,
|
||||
self::OPTION_NOISE_REMAINING_USAGES_EXCLUDE => ['unlink'],
|
||||
]
|
||||
),
|
||||
],
|
||||
null,
|
||||
'Risky because adding/removing `@` might cause changes to code behaviour or if `trigger_error` function is overridden.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder(self::OPTION_MUTE_DEPRECATION_ERROR, 'Whether to add `@` in deprecation notices.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(true)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder(self::OPTION_NOISE_REMAINING_USAGES, 'Whether to remove `@` in remaining usages.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(false)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder(self::OPTION_NOISE_REMAINING_USAGES_EXCLUDE, 'List of global functions to exclude from removing `@`'))
|
||||
->setAllowedTypes(['array'])
|
||||
->setDefault([])
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$functionsAnalyzer = new FunctionsAnalyzer();
|
||||
$excludedFunctions = array_map(static function (string $function): string {
|
||||
return strtolower($function);
|
||||
}, $this->configuration[self::OPTION_NOISE_REMAINING_USAGES_EXCLUDE]);
|
||||
|
||||
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
|
||||
$token = $tokens[$index];
|
||||
|
||||
if (true === $this->configuration[self::OPTION_NOISE_REMAINING_USAGES] && $token->equals('@')) {
|
||||
$tokens->clearAt($index);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$functionIndex = $index;
|
||||
$startIndex = $index;
|
||||
$prevIndex = $tokens->getPrevMeaningfulToken($index);
|
||||
|
||||
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
|
||||
$startIndex = $prevIndex;
|
||||
$prevIndex = $tokens->getPrevMeaningfulToken($startIndex);
|
||||
}
|
||||
|
||||
$index = $prevIndex;
|
||||
|
||||
if ($this->isDeprecationErrorCall($tokens, $functionIndex)) {
|
||||
if (false === $this->configuration[self::OPTION_MUTE_DEPRECATION_ERROR]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$prevIndex]->equals('@')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokens->insertAt($startIndex, new Token('@'));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$tokens[$prevIndex]->equals('@')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (true === $this->configuration[self::OPTION_NOISE_REMAINING_USAGES] && !\in_array($tokens[$functionIndex]->getContent(), $excludedFunctions, true)) {
|
||||
$tokens->clearAt($index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isDeprecationErrorCall(Tokens $tokens, int $index): bool
|
||||
{
|
||||
if ('trigger_error' !== strtolower($tokens[$index]->getContent())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$endBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $tokens->getNextTokenOfKind($index, [[T_STRING], '(']));
|
||||
$prevIndex = $tokens->getPrevMeaningfulToken($endBraceIndex);
|
||||
|
||||
if ($tokens[$prevIndex]->equals(',')) {
|
||||
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
|
||||
}
|
||||
|
||||
return $tokens[$prevIndex]->equals([T_STRING, 'E_USER_DEPRECATED']);
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Filippo Tessarotto <zoeslam@gmail.com>
|
||||
*/
|
||||
final class ExplicitIndirectVariableFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Add curly braces to indirect variables to make them clear to understand. Requires PHP >= 7.0.',
|
||||
[
|
||||
new CodeSample(
|
||||
<<<'EOT'
|
||||
<?php
|
||||
echo $$foo;
|
||||
echo $$foo['bar'];
|
||||
echo $foo->$bar['baz'];
|
||||
echo $foo->$callback($baz);
|
||||
|
||||
EOT
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_VARIABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = $tokens->count() - 1; $index > 1; --$index) {
|
||||
$token = $tokens[$index];
|
||||
if (!$token->isGivenKind(T_VARIABLE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$prevIndex = $tokens->getPrevMeaningfulToken($index);
|
||||
$prevToken = $tokens[$prevIndex];
|
||||
if (!$prevToken->equals('$') && !$prevToken->isObjectOperator()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$openingBrace = CT::T_DYNAMIC_VAR_BRACE_OPEN;
|
||||
$closingBrace = CT::T_DYNAMIC_VAR_BRACE_CLOSE;
|
||||
if ($prevToken->isObjectOperator()) {
|
||||
$openingBrace = CT::T_DYNAMIC_PROP_BRACE_OPEN;
|
||||
$closingBrace = CT::T_DYNAMIC_PROP_BRACE_CLOSE;
|
||||
}
|
||||
|
||||
$tokens->overrideRange($index, $index, [
|
||||
new Token([$openingBrace, '{']),
|
||||
new Token([T_VARIABLE, $token->getContent()]),
|
||||
new Token([$closingBrace, '}']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+301
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class FunctionToConstantFixer extends AbstractFixer implements ConfigurableFixerInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, Token[]>
|
||||
*/
|
||||
private static $availableFunctions;
|
||||
|
||||
/**
|
||||
* @var array<string, Token[]>
|
||||
*/
|
||||
private array $functionsFixMap;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (null === self::$availableFunctions) {
|
||||
self::$availableFunctions = [
|
||||
'get_called_class' => [
|
||||
new Token([T_STATIC, 'static']),
|
||||
new Token([T_DOUBLE_COLON, '::']),
|
||||
new Token([CT::T_CLASS_CONSTANT, 'class']),
|
||||
],
|
||||
'get_class' => [new Token([T_CLASS_C, '__CLASS__'])],
|
||||
'get_class_this' => [
|
||||
new Token([T_STATIC, 'static']),
|
||||
new Token([T_DOUBLE_COLON, '::']),
|
||||
new Token([CT::T_CLASS_CONSTANT, 'class']),
|
||||
],
|
||||
'php_sapi_name' => [new Token([T_STRING, 'PHP_SAPI'])],
|
||||
'phpversion' => [new Token([T_STRING, 'PHP_VERSION'])],
|
||||
'pi' => [new Token([T_STRING, 'M_PI'])],
|
||||
];
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
parent::configure($configuration);
|
||||
|
||||
$this->functionsFixMap = [];
|
||||
|
||||
foreach ($this->configuration['functions'] as $key) {
|
||||
$this->functionsFixMap[$key] = self::$availableFunctions[$key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Replace core functions calls returning constants with the constants.',
|
||||
[
|
||||
new CodeSample(
|
||||
"<?php\necho phpversion();\necho pi();\necho php_sapi_name();\nclass Foo\n{\n public function Bar()\n {\n echo get_class();\n echo get_called_class();\n }\n}\n"
|
||||
),
|
||||
new CodeSample(
|
||||
"<?php\necho phpversion();\necho pi();\nclass Foo\n{\n public function Bar()\n {\n echo get_class();\n get_class(\$this);\n echo get_called_class();\n }\n}\n",
|
||||
['functions' => ['get_called_class', 'get_class_this', 'phpversion']]
|
||||
),
|
||||
],
|
||||
null,
|
||||
'Risky when any of the configured functions to replace are overridden.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before NativeFunctionCasingFixer, NoExtraBlankLinesFixer, NoSinglelineWhitespaceBeforeSemicolonsFixer, NoTrailingWhitespaceFixer, NoWhitespaceInBlankLineFixer, SelfStaticAccessorFixer.
|
||||
* Must run after NoSpacesAfterFunctionNameFixer, NoSpacesInsideParenthesisFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$functionAnalyzer = new FunctionsAnalyzer();
|
||||
|
||||
for ($index = $tokens->count() - 4; $index > 0; --$index) {
|
||||
$candidate = $this->getReplaceCandidate($tokens, $functionAnalyzer, $index);
|
||||
if (null === $candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->fixFunctionCallToConstant(
|
||||
$tokens,
|
||||
$index,
|
||||
$candidate[0], // brace open
|
||||
$candidate[1], // brace close
|
||||
$candidate[2] // replacement
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
$functionNames = array_keys(self::$availableFunctions);
|
||||
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('functions', 'List of function names to fix.'))
|
||||
->setAllowedTypes(['array'])
|
||||
->setAllowedValues([new AllowedValueSubset($functionNames)])
|
||||
->setDefault([
|
||||
'get_called_class',
|
||||
'get_class',
|
||||
'get_class_this',
|
||||
'php_sapi_name',
|
||||
'phpversion',
|
||||
'pi',
|
||||
])
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Token[] $replacements
|
||||
*/
|
||||
private function fixFunctionCallToConstant(Tokens $tokens, int $index, int $braceOpenIndex, int $braceCloseIndex, array $replacements): void
|
||||
{
|
||||
for ($i = $braceCloseIndex; $i >= $braceOpenIndex; --$i) {
|
||||
if ($tokens[$i]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
|
||||
}
|
||||
|
||||
if ($replacements[0]->isGivenKind([T_CLASS_C, T_STATIC])) {
|
||||
$prevIndex = $tokens->getPrevMeaningfulToken($index);
|
||||
$prevToken = $tokens[$prevIndex];
|
||||
if ($prevToken->isGivenKind(T_NS_SEPARATOR)) {
|
||||
$tokens->clearAt($prevIndex);
|
||||
}
|
||||
}
|
||||
|
||||
$tokens->clearAt($index);
|
||||
$tokens->insertAt($index, $replacements);
|
||||
}
|
||||
|
||||
private function getReplaceCandidate(
|
||||
Tokens $tokens,
|
||||
FunctionsAnalyzer $functionAnalyzer,
|
||||
int $index
|
||||
): ?array {
|
||||
if (!$tokens[$index]->isGivenKind(T_STRING)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lowerContent = strtolower($tokens[$index]->getContent());
|
||||
|
||||
if ('get_class' === $lowerContent) {
|
||||
return $this->fixGetClassCall($tokens, $functionAnalyzer, $index);
|
||||
}
|
||||
|
||||
if (!isset($this->functionsFixMap[$lowerContent])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// test if function call without parameters
|
||||
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
|
||||
if (!$tokens[$braceOpenIndex]->equals('(')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$braceCloseIndex = $tokens->getNextMeaningfulToken($braceOpenIndex);
|
||||
if (!$tokens[$braceCloseIndex]->equals(')')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getReplacementTokenClones($lowerContent, $braceOpenIndex, $braceCloseIndex);
|
||||
}
|
||||
|
||||
private function fixGetClassCall(
|
||||
Tokens $tokens,
|
||||
FunctionsAnalyzer $functionAnalyzer,
|
||||
int $index
|
||||
): ?array {
|
||||
if (!isset($this->functionsFixMap['get_class']) && !isset($this->functionsFixMap['get_class_this'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
|
||||
$braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceOpenIndex);
|
||||
|
||||
if ($braceCloseIndex === $tokens->getNextMeaningfulToken($braceOpenIndex)) { // no arguments passed
|
||||
if (isset($this->functionsFixMap['get_class'])) {
|
||||
return $this->getReplacementTokenClones('get_class', $braceOpenIndex, $braceCloseIndex);
|
||||
}
|
||||
} elseif (isset($this->functionsFixMap['get_class_this'])) {
|
||||
$isThis = false;
|
||||
|
||||
for ($i = $braceOpenIndex + 1; $i < $braceCloseIndex; ++$i) {
|
||||
if ($tokens[$i]->equalsAny([[T_WHITESPACE], [T_COMMENT], [T_DOC_COMMENT], ')'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]->isGivenKind(T_VARIABLE) && '$this' === strtolower($tokens[$i]->getContent())) {
|
||||
$isThis = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false === $isThis && $tokens[$i]->equals('(')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isThis = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($isThis) {
|
||||
return $this->getReplacementTokenClones('get_class_this', $braceOpenIndex, $braceCloseIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getReplacementTokenClones(string $lowerContent, int $braceOpenIndex, int $braceCloseIndex): array
|
||||
{
|
||||
$clones = [];
|
||||
foreach ($this->functionsFixMap[$lowerContent] as $token) {
|
||||
$clones[] = clone $token;
|
||||
}
|
||||
|
||||
return [
|
||||
$braceOpenIndex,
|
||||
$braceCloseIndex,
|
||||
$clones,
|
||||
];
|
||||
}
|
||||
}
|
||||
www-api/vendor/friendsofphp/php-cs-fixer/src/Fixer/LanguageConstruct/GetClassToClassKeywordFixer.php
Vendored
+170
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\FixerDefinition\VersionSpecification;
|
||||
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
|
||||
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author John Paul E. Balandan, CPA <paulbalandan@gmail.com>
|
||||
*/
|
||||
final class GetClassToClassKeywordFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Replace `get_class` calls on object variables with class keyword syntax.',
|
||||
[
|
||||
new VersionSpecificCodeSample(
|
||||
"<?php\nget_class(\$a);\n",
|
||||
new VersionSpecification(80000)
|
||||
),
|
||||
new VersionSpecificCodeSample(
|
||||
"<?php\n\n\$date = new \\DateTimeImmutable();\n\$class = get_class(\$date);\n",
|
||||
new VersionSpecification(80000)
|
||||
),
|
||||
],
|
||||
null,
|
||||
'Risky if the `get_class` function is overridden.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before MultilineWhitespaceBeforeSemicolonsFixer.
|
||||
* Must run after NoSpacesAfterFunctionNameFixer, NoSpacesInsideParenthesisFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return \PHP_VERSION_ID >= 80000 && $tokens->isAllTokenKindsFound([T_STRING, T_VARIABLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$functionsAnalyzer = new FunctionsAnalyzer();
|
||||
$indicesToClear = [];
|
||||
$tokenSlices = [];
|
||||
|
||||
for ($index = $tokens->count() - 1; $index > 0; --$index) {
|
||||
if (!$tokens[$index]->equals([T_STRING, 'get_class'], false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
|
||||
$braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceOpenIndex);
|
||||
|
||||
if ($braceCloseIndex === $tokens->getNextMeaningfulToken($braceOpenIndex)) {
|
||||
continue; // get_class with no arguments
|
||||
}
|
||||
|
||||
$meaningfulTokensCount = 0;
|
||||
$variableTokensIndices = [];
|
||||
|
||||
for ($i = $braceOpenIndex + 1; $i < $braceCloseIndex; ++$i) {
|
||||
if (!$tokens[$i]->equalsAny([[T_WHITESPACE], [T_COMMENT], [T_DOC_COMMENT], '(', ')'])) {
|
||||
++$meaningfulTokensCount;
|
||||
}
|
||||
|
||||
if (!$tokens[$i]->isGivenKind(T_VARIABLE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('$this' === strtolower($tokens[$i]->getContent())) {
|
||||
continue 2; // get_class($this)
|
||||
}
|
||||
|
||||
$variableTokensIndices[] = $i;
|
||||
}
|
||||
|
||||
if ($meaningfulTokensCount > 1 || 1 !== \count($variableTokensIndices)) {
|
||||
continue; // argument contains more logic, or more arguments, or no variable argument
|
||||
}
|
||||
|
||||
$indicesToClear[$index] = [$braceOpenIndex, current($variableTokensIndices), $braceCloseIndex];
|
||||
}
|
||||
|
||||
foreach ($indicesToClear as $index => $items) {
|
||||
$tokenSlices[$index] = $this->getReplacementTokenSlices($tokens, $items[1]);
|
||||
$this->clearGetClassCall($tokens, $index, $items[0], $items[2]);
|
||||
}
|
||||
|
||||
$tokens->insertSlices($tokenSlices);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Token>
|
||||
*/
|
||||
private function getReplacementTokenSlices(Tokens $tokens, int $variableIndex): array
|
||||
{
|
||||
return [
|
||||
new Token([T_VARIABLE, $tokens[$variableIndex]->getContent()]),
|
||||
new Token([T_DOUBLE_COLON, '::']),
|
||||
new Token([CT::T_CLASS_CONSTANT, 'class']),
|
||||
];
|
||||
}
|
||||
|
||||
private function clearGetClassCall(Tokens $tokens, int $index, int $braceOpenIndex, int $braceCloseIndex): void
|
||||
{
|
||||
for ($i = $braceOpenIndex; $i <= $braceCloseIndex; ++$i) {
|
||||
if ($tokens[$i]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
|
||||
}
|
||||
|
||||
$prevIndex = $tokens->getPrevMeaningfulToken($index);
|
||||
|
||||
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
|
||||
$tokens->clearAt($prevIndex);
|
||||
}
|
||||
|
||||
$tokens->clearAt($index);
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Vladimir Reznichenko <kalessil@gmail.com>
|
||||
*/
|
||||
final class IsNullFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Replaces `is_null($var)` expression with `null === $var`.',
|
||||
[
|
||||
new CodeSample("<?php\n\$a = is_null(\$b);\n"),
|
||||
],
|
||||
null,
|
||||
'Risky when the function `is_null` is overridden.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before YodaStyleFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
static $sequenceNeeded = [[T_STRING, 'is_null'], '('];
|
||||
$functionsAnalyzer = new FunctionsAnalyzer();
|
||||
$currIndex = 0;
|
||||
|
||||
while (true) {
|
||||
// recalculate "end" because we might have added tokens in previous iteration
|
||||
$matches = $tokens->findSequence($sequenceNeeded, $currIndex, $tokens->count() - 1, false);
|
||||
|
||||
// stop looping if didn't find any new matches
|
||||
if (null === $matches) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 0 and 1 accordingly are "is_null", "(" tokens
|
||||
$matches = array_keys($matches);
|
||||
|
||||
// move the cursor just after the sequence
|
||||
[$isNullIndex, $currIndex] = $matches;
|
||||
|
||||
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $matches[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$next = $tokens->getNextMeaningfulToken($currIndex);
|
||||
|
||||
if ($tokens[$next]->equals(')')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$prevTokenIndex = $tokens->getPrevMeaningfulToken($matches[0]);
|
||||
|
||||
// handle function references with namespaces
|
||||
if ($tokens[$prevTokenIndex]->isGivenKind(T_NS_SEPARATOR)) {
|
||||
$tokens->removeTrailingWhitespace($prevTokenIndex);
|
||||
$tokens->clearAt($prevTokenIndex);
|
||||
|
||||
$prevTokenIndex = $tokens->getPrevMeaningfulToken($prevTokenIndex);
|
||||
}
|
||||
|
||||
// check if inversion being used, text comparison is due to not existing constant
|
||||
$isInvertedNullCheck = false;
|
||||
|
||||
if ($tokens[$prevTokenIndex]->equals('!')) {
|
||||
$isInvertedNullCheck = true;
|
||||
|
||||
// get rid of inverting for proper transformations
|
||||
$tokens->removeTrailingWhitespace($prevTokenIndex);
|
||||
$tokens->clearAt($prevTokenIndex);
|
||||
}
|
||||
|
||||
// before getting rind of `()` around a parameter, ensure it's not assignment/ternary invariant
|
||||
$referenceEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $matches[1]);
|
||||
$isContainingDangerousConstructs = false;
|
||||
|
||||
for ($paramTokenIndex = $matches[1]; $paramTokenIndex <= $referenceEnd; ++$paramTokenIndex) {
|
||||
if (\in_array($tokens[$paramTokenIndex]->getContent(), ['?', '?:', '=', '??'], true)) {
|
||||
$isContainingDangerousConstructs = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// edge cases: is_null() followed/preceded by ==, ===, !=, !==, <>, (int-or-other-casting)
|
||||
$parentLeftToken = $tokens[$tokens->getPrevMeaningfulToken($isNullIndex)];
|
||||
$parentRightToken = $tokens[$tokens->getNextMeaningfulToken($referenceEnd)];
|
||||
$parentOperations = [T_IS_EQUAL, T_IS_NOT_EQUAL, T_IS_IDENTICAL, T_IS_NOT_IDENTICAL];
|
||||
$wrapIntoParentheses = $parentLeftToken->isCast() || $parentLeftToken->isGivenKind($parentOperations) || $parentRightToken->isGivenKind($parentOperations);
|
||||
|
||||
// possible trailing comma removed
|
||||
$prevIndex = $tokens->getPrevMeaningfulToken($referenceEnd);
|
||||
|
||||
if ($tokens[$prevIndex]->equals(',')) {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);
|
||||
}
|
||||
|
||||
if (!$isContainingDangerousConstructs) {
|
||||
// closing parenthesis removed with leading spaces
|
||||
$tokens->removeLeadingWhitespace($referenceEnd);
|
||||
$tokens->clearAt($referenceEnd);
|
||||
|
||||
// opening parenthesis removed with trailing spaces
|
||||
$tokens->removeLeadingWhitespace($matches[1]);
|
||||
$tokens->removeTrailingWhitespace($matches[1]);
|
||||
$tokens->clearAt($matches[1]);
|
||||
}
|
||||
|
||||
// sequence which we'll use as a replacement
|
||||
$replacement = [
|
||||
new Token([T_STRING, 'null']),
|
||||
new Token([T_WHITESPACE, ' ']),
|
||||
new Token($isInvertedNullCheck ? [T_IS_NOT_IDENTICAL, '!=='] : [T_IS_IDENTICAL, '===']),
|
||||
new Token([T_WHITESPACE, ' ']),
|
||||
];
|
||||
|
||||
if ($wrapIntoParentheses) {
|
||||
array_unshift($replacement, new Token('('));
|
||||
$tokens->insertAt($referenceEnd + 1, new Token(')'));
|
||||
}
|
||||
|
||||
$tokens->overrideRange($isNullIndex, $isNullIndex, $replacement);
|
||||
|
||||
// nested is_null calls support
|
||||
$currIndex = $isNullIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+229
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Gert de Pagter <BackEndTea@gmail.com>
|
||||
*/
|
||||
final class NoUnsetOnPropertyFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Properties should be set to `null` instead of using `unset`.',
|
||||
[new CodeSample("<?php\nunset(\$this->a);\n")],
|
||||
null,
|
||||
'Risky when relying on attributes to be removed using `unset` rather than be set to `null`.'.
|
||||
' Changing variables to `null` instead of unsetting means these still show up when looping over class variables'.
|
||||
' and reference properties remain unbroken.'.
|
||||
' With PHP 7.4, this rule might introduce `null` assignments to properties whose type declaration does not allow it.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_UNSET)
|
||||
&& $tokens->isAnyTokenKindsFound([T_OBJECT_OPERATOR, T_PAAMAYIM_NEKUDOTAYIM]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before CombineConsecutiveUnsetsFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 25;
|
||||
}
|
||||
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
|
||||
if (!$tokens[$index]->isGivenKind(T_UNSET)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$unsetsInfo = $this->getUnsetsInfo($tokens, $index);
|
||||
|
||||
if (!$this->isAnyUnsetToTransform($unsetsInfo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isLastUnset = true; // "last" as we reverse the array below
|
||||
|
||||
foreach (array_reverse($unsetsInfo) as $unsetInfo) {
|
||||
$this->updateTokens($tokens, $unsetInfo, $isLastUnset);
|
||||
$isLastUnset = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array<string, bool|int>>
|
||||
*/
|
||||
private function getUnsetsInfo(Tokens $tokens, int $index): array
|
||||
{
|
||||
$argumentsAnalyzer = new ArgumentsAnalyzer();
|
||||
|
||||
$unsetStart = $tokens->getNextTokenOfKind($index, ['(']);
|
||||
$unsetEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $unsetStart);
|
||||
$isFirst = true;
|
||||
$unsets = [];
|
||||
|
||||
foreach ($argumentsAnalyzer->getArguments($tokens, $unsetStart, $unsetEnd) as $startIndex => $endIndex) {
|
||||
$startIndex = $tokens->getNextMeaningfulToken($startIndex - 1);
|
||||
$endIndex = $tokens->getPrevMeaningfulToken($endIndex + 1);
|
||||
$unsets[] = [
|
||||
'startIndex' => $startIndex,
|
||||
'endIndex' => $endIndex,
|
||||
'isToTransform' => $this->isProperty($tokens, $startIndex, $endIndex),
|
||||
'isFirst' => $isFirst,
|
||||
];
|
||||
$isFirst = false;
|
||||
}
|
||||
|
||||
return $unsets;
|
||||
}
|
||||
|
||||
private function isProperty(Tokens $tokens, int $index, int $endIndex): bool
|
||||
{
|
||||
if ($tokens[$index]->isGivenKind(T_VARIABLE)) {
|
||||
$nextIndex = $tokens->getNextMeaningfulToken($index);
|
||||
|
||||
if (null === $nextIndex || !$tokens[$nextIndex]->isGivenKind(T_OBJECT_OPERATOR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
|
||||
$nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex);
|
||||
|
||||
if (null !== $nextNextIndex && $nextNextIndex < $endIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return null !== $nextIndex && $tokens[$nextIndex]->isGivenKind(T_STRING);
|
||||
}
|
||||
|
||||
if ($tokens[$index]->isGivenKind([T_NS_SEPARATOR, T_STRING])) {
|
||||
$nextIndex = $tokens->getTokenNotOfKindsSibling($index, 1, [T_DOUBLE_COLON, T_NS_SEPARATOR, T_STRING]);
|
||||
$nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex);
|
||||
|
||||
if (null !== $nextNextIndex && $nextNextIndex < $endIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return null !== $nextIndex && $tokens[$nextIndex]->isGivenKind(T_VARIABLE);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array<string, bool|int>> $unsetsInfo
|
||||
*/
|
||||
private function isAnyUnsetToTransform(array $unsetsInfo): bool
|
||||
{
|
||||
foreach ($unsetsInfo as $unsetInfo) {
|
||||
if ($unsetInfo['isToTransform']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool|int> $unsetInfo
|
||||
*/
|
||||
private function updateTokens(Tokens $tokens, array $unsetInfo, bool $isLastUnset): void
|
||||
{
|
||||
// if entry is first and to be transformed we remove leading "unset("
|
||||
if ($unsetInfo['isFirst'] && $unsetInfo['isToTransform']) {
|
||||
$braceIndex = $tokens->getPrevTokenOfKind($unsetInfo['startIndex'], ['(']);
|
||||
$unsetIndex = $tokens->getPrevTokenOfKind($braceIndex, [[T_UNSET]]);
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($braceIndex);
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($unsetIndex);
|
||||
}
|
||||
|
||||
// if entry is last and to be transformed we remove trailing ")"
|
||||
if ($isLastUnset && $unsetInfo['isToTransform']) {
|
||||
$braceIndex = $tokens->getNextTokenOfKind($unsetInfo['endIndex'], [')']);
|
||||
$previousIndex = $tokens->getPrevMeaningfulToken($braceIndex);
|
||||
if ($tokens[$previousIndex]->equals(',')) {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($previousIndex); // trailing ',' in function call (PHP 7.3)
|
||||
}
|
||||
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($braceIndex);
|
||||
}
|
||||
|
||||
// if entry is not last we replace comma with semicolon (last entry already has semicolon - from original unset)
|
||||
if (!$isLastUnset) {
|
||||
$commaIndex = $tokens->getNextTokenOfKind($unsetInfo['endIndex'], [',']);
|
||||
$tokens[$commaIndex] = new Token(';');
|
||||
}
|
||||
|
||||
// if entry is to be unset and is not last we add trailing ")"
|
||||
if (!$unsetInfo['isToTransform'] && !$isLastUnset) {
|
||||
$tokens->insertAt($unsetInfo['endIndex'] + 1, new Token(')'));
|
||||
}
|
||||
|
||||
// if entry is to be unset and is not first we add leading "unset("
|
||||
if (!$unsetInfo['isToTransform'] && !$unsetInfo['isFirst']) {
|
||||
$tokens->insertAt(
|
||||
$unsetInfo['startIndex'],
|
||||
[
|
||||
new Token([T_UNSET, 'unset']),
|
||||
new Token('('),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// and finally
|
||||
// if entry is to be transformed we add trailing " = null"
|
||||
if ($unsetInfo['isToTransform']) {
|
||||
$tokens->insertAt(
|
||||
$unsetInfo['endIndex'] + 1,
|
||||
[
|
||||
new Token([T_WHITESPACE, ' ']),
|
||||
new Token('='),
|
||||
new Token([T_WHITESPACE, ' ']),
|
||||
new Token([T_STRING, 'null']),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Fixer\LanguageConstruct;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Andreas Möller <am@localheinz.com>
|
||||
*/
|
||||
final class SingleSpaceAfterConstructFixer extends AbstractFixer implements ConfigurableFixerInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, null|int>
|
||||
*/
|
||||
private static array $tokenMap = [
|
||||
'abstract' => T_ABSTRACT,
|
||||
'as' => T_AS,
|
||||
'attribute' => CT::T_ATTRIBUTE_CLOSE,
|
||||
'break' => T_BREAK,
|
||||
'case' => T_CASE,
|
||||
'catch' => T_CATCH,
|
||||
'class' => T_CLASS,
|
||||
'clone' => T_CLONE,
|
||||
'comment' => T_COMMENT,
|
||||
'const' => T_CONST,
|
||||
'const_import' => CT::T_CONST_IMPORT,
|
||||
'continue' => T_CONTINUE,
|
||||
'do' => T_DO,
|
||||
'echo' => T_ECHO,
|
||||
'else' => T_ELSE,
|
||||
'elseif' => T_ELSEIF,
|
||||
'enum' => null,
|
||||
'extends' => T_EXTENDS,
|
||||
'final' => T_FINAL,
|
||||
'finally' => T_FINALLY,
|
||||
'for' => T_FOR,
|
||||
'foreach' => T_FOREACH,
|
||||
'function' => T_FUNCTION,
|
||||
'function_import' => CT::T_FUNCTION_IMPORT,
|
||||
'global' => T_GLOBAL,
|
||||
'goto' => T_GOTO,
|
||||
'if' => T_IF,
|
||||
'implements' => T_IMPLEMENTS,
|
||||
'include' => T_INCLUDE,
|
||||
'include_once' => T_INCLUDE_ONCE,
|
||||
'instanceof' => T_INSTANCEOF,
|
||||
'insteadof' => T_INSTEADOF,
|
||||
'interface' => T_INTERFACE,
|
||||
'match' => null,
|
||||
'named_argument' => CT::T_NAMED_ARGUMENT_COLON,
|
||||
'namespace' => T_NAMESPACE,
|
||||
'new' => T_NEW,
|
||||
'open_tag_with_echo' => T_OPEN_TAG_WITH_ECHO,
|
||||
'php_doc' => T_DOC_COMMENT,
|
||||
'php_open' => T_OPEN_TAG,
|
||||
'print' => T_PRINT,
|
||||
'private' => T_PRIVATE,
|
||||
'protected' => T_PROTECTED,
|
||||
'public' => T_PUBLIC,
|
||||
'readonly' => null,
|
||||
'require' => T_REQUIRE,
|
||||
'require_once' => T_REQUIRE_ONCE,
|
||||
'return' => T_RETURN,
|
||||
'static' => T_STATIC,
|
||||
'switch' => T_SWITCH,
|
||||
'throw' => T_THROW,
|
||||
'trait' => T_TRAIT,
|
||||
'try' => T_TRY,
|
||||
'type_colon' => CT::T_TYPE_COLON,
|
||||
'use' => T_USE,
|
||||
'use_lambda' => CT::T_USE_LAMBDA,
|
||||
'use_trait' => CT::T_USE_TRAIT,
|
||||
'var' => T_VAR,
|
||||
'while' => T_WHILE,
|
||||
'yield' => T_YIELD,
|
||||
'yield_from' => T_YIELD_FROM,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, int>
|
||||
*/
|
||||
private array $fixTokenMap = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
parent::configure($configuration);
|
||||
|
||||
if (\defined('T_MATCH')) { // @TODO: drop condition when PHP 8.0+ is required
|
||||
self::$tokenMap['match'] = T_MATCH;
|
||||
}
|
||||
|
||||
if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
|
||||
self::$tokenMap['readonly'] = T_READONLY;
|
||||
}
|
||||
|
||||
if (\defined('T_ENUM')) { // @TODO: drop condition when PHP 8.1+ is required
|
||||
self::$tokenMap['enum'] = T_ENUM;
|
||||
}
|
||||
|
||||
$this->fixTokenMap = [];
|
||||
|
||||
foreach ($this->configuration['constructs'] as $key) {
|
||||
if (null !== self::$tokenMap[$key]) {
|
||||
$this->fixTokenMap[$key] = self::$tokenMap[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->fixTokenMap['public'])) {
|
||||
$this->fixTokenMap['constructor_public'] = CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC;
|
||||
}
|
||||
|
||||
if (isset($this->fixTokenMap['protected'])) {
|
||||
$this->fixTokenMap['constructor_protected'] = CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED;
|
||||
}
|
||||
|
||||
if (isset($this->fixTokenMap['private'])) {
|
||||
$this->fixTokenMap['constructor_private'] = CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Ensures a single space after language constructs.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
|
||||
throw new \Exception();
|
||||
'
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
|
||||
echo "Hello!";
|
||||
',
|
||||
[
|
||||
'constructs' => [
|
||||
'echo',
|
||||
],
|
||||
]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
|
||||
yield from baz();
|
||||
',
|
||||
[
|
||||
'constructs' => [
|
||||
'yield_from',
|
||||
],
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before BracesFixer, FunctionDeclarationFixer.
|
||||
* Must run after ModernizeStrposFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 36;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isAnyTokenKindsFound(array_values($this->fixTokenMap)) && !$tokens->hasAlternativeSyntax();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$tokenKinds = array_values($this->fixTokenMap);
|
||||
|
||||
for ($index = $tokens->count() - 2; $index >= 0; --$index) {
|
||||
$token = $tokens[$index];
|
||||
|
||||
if (!$token->isGivenKind($tokenKinds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$whitespaceTokenIndex = $index + 1;
|
||||
|
||||
if ($tokens[$whitespaceTokenIndex]->equalsAny([',', ';', ')', [CT::T_ARRAY_SQUARE_BRACE_CLOSE], [CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE]])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
$token->isGivenKind(T_STATIC)
|
||||
&& !$tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind([T_FUNCTION, T_VARIABLE])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->isGivenKind(T_OPEN_TAG)) {
|
||||
if ($tokens[$whitespaceTokenIndex]->equals([T_WHITESPACE]) && !str_contains($tokens[$whitespaceTokenIndex]->getContent(), "\n") && !str_contains($token->getContent(), "\n")) {
|
||||
$tokens->clearAt($whitespaceTokenIndex);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->isGivenKind(T_CLASS) && $tokens[$tokens->getNextMeaningfulToken($index)]->equals('(')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->isGivenKind([T_EXTENDS, T_IMPLEMENTS]) && $this->isMultilineExtendsOrImplementsWithMoreThanOneAncestor($tokens, $index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->isGivenKind(T_RETURN) && $this->isMultiLineReturn($tokens, $index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->isGivenKind(T_CONST) && $this->isMultilineConstant($tokens, $index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->isComment() || $token->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
|
||||
if ($tokens[$whitespaceTokenIndex]->equals([T_WHITESPACE]) && str_contains($tokens[$whitespaceTokenIndex]->getContent(), "\n")) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$tokens->ensureWhitespaceAtIndex($whitespaceTokenIndex, 0, ' ');
|
||||
|
||||
if (
|
||||
$token->isGivenKind(T_YIELD_FROM)
|
||||
&& 'yield from' !== strtolower($token->getContent())
|
||||
) {
|
||||
$tokens[$index] = new Token([T_YIELD_FROM, Preg::replace(
|
||||
'/\s+/',
|
||||
' ',
|
||||
$token->getContent()
|
||||
)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
$defaults = self::$tokenMap;
|
||||
$tokens = array_keys($defaults);
|
||||
|
||||
unset($defaults['type_colon']);
|
||||
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('constructs', 'List of constructs which must be followed by a single space.'))
|
||||
->setAllowedTypes(['array'])
|
||||
->setAllowedValues([new AllowedValueSubset($tokens)])
|
||||
->setDefault(array_keys($defaults))
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function isMultiLineReturn(Tokens $tokens, int $index): bool
|
||||
{
|
||||
++$index;
|
||||
$tokenFollowingReturn = $tokens[$index];
|
||||
|
||||
if (
|
||||
!$tokenFollowingReturn->isGivenKind(T_WHITESPACE)
|
||||
|| !str_contains($tokenFollowingReturn->getContent(), "\n")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$nestedCount = 0;
|
||||
|
||||
for ($indexEnd = \count($tokens) - 1, ++$index; $index < $indexEnd; ++$index) {
|
||||
if (str_contains($tokens[$index]->getContent(), "\n")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($tokens[$index]->equals('{')) {
|
||||
++$nestedCount;
|
||||
} elseif ($tokens[$index]->equals('}')) {
|
||||
--$nestedCount;
|
||||
} elseif (0 === $nestedCount && $tokens[$index]->equalsAny([';', [T_CLOSE_TAG]])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isMultilineExtendsOrImplementsWithMoreThanOneAncestor(Tokens $tokens, int $index): bool
|
||||
{
|
||||
$hasMoreThanOneAncestor = false;
|
||||
|
||||
while (++$index) {
|
||||
$token = $tokens[$index];
|
||||
|
||||
if ($token->equals(',')) {
|
||||
$hasMoreThanOneAncestor = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->equals('{')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($hasMoreThanOneAncestor && str_contains($token->getContent(), "\n")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isMultilineConstant(Tokens $tokens, int $index): bool
|
||||
{
|
||||
$scopeEnd = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]) - 1;
|
||||
$hasMoreThanOneConstant = null !== $tokens->findSequence([new Token(',')], $index + 1, $scopeEnd);
|
||||
|
||||
return $hasMoreThanOneConstant && $tokens->isPartialCodeMultiline($index, $scopeEnd);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user