Missing dependancies

This commit is contained in:
tokslaw7
2023-06-21 12:19:22 +00:00
parent fbf3f180c2
commit 421f25c80d
2356 changed files with 342670 additions and 4743 deletions
@@ -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
));
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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']);
}
}
@@ -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, '}']),
]);
}
}
}
@@ -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,
];
}
}
@@ -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);
}
}
@@ -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;
}
}
}
@@ -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']),
]
);
}
}
}
@@ -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);
}
}