Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
tipuloidea
/
back
/
vendor
/
friendsofphp
/
php-cs-fixer
/
src
/
Fixer
/
ControlStructure
:
NoUnneededControlParenthesesFixer.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?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\ControlStructure; use PhpCsFixer\AbstractFixer; use PhpCsFixer\Fixer\ConfigurableFixerInterface; use PhpCsFixer\Fixer\ConfigurableFixerTrait; 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\CT; use PhpCsFixer\Tokenizer\FCT; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use PhpCsFixer\Tokenizer\TokensAnalyzer; /** * @phpstan-type _AutogeneratedInputConfiguration array{ * statements?: list<'break'|'clone'|'continue'|'echo_print'|'negative_instanceof'|'others'|'return'|'switch_case'|'yield'|'yield_from'>, * } * @phpstan-type _AutogeneratedComputedConfiguration array{ * statements: list<'break'|'clone'|'continue'|'echo_print'|'negative_instanceof'|'others'|'return'|'switch_case'|'yield'|'yield_from'>, * } * * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> * * @phpstan-import-type _PhpTokenPrototypePartial from Token * * @author Sullivan Senechal <soullivaneuh@gmail.com> * @author Dariusz Rumiński <dariusz.ruminski@gmail.com> * @author Gregor Harlan <gharlan@web.de> * * @no-named-arguments Parameter names are not covered by the backward compatibility promise. */ final class NoUnneededControlParenthesesFixer extends AbstractFixer implements ConfigurableFixerInterface { /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */ use ConfigurableFixerTrait; /** * @var non-empty-list<int> */ private const BLOCK_TYPES = [ Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE, Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, Tokens::BLOCK_TYPE_CURLY_BRACE, Tokens::BLOCK_TYPE_DESTRUCTURING_SQUARE_BRACE, Tokens::BLOCK_TYPE_DYNAMIC_PROP_BRACE, Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE, Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, ]; private const BEFORE_TYPES = [ ';', '{', [\T_OPEN_TAG], [\T_OPEN_TAG_WITH_ECHO], [\T_ECHO], [\T_PRINT], [\T_RETURN], [\T_THROW], [\T_YIELD], [\T_YIELD_FROM], [\T_BREAK], [\T_CONTINUE], // won't be fixed, but true in concept, helpful for fast check [\T_REQUIRE], [\T_REQUIRE_ONCE], [\T_INCLUDE], [\T_INCLUDE_ONCE], ]; private const CONFIG_OPTIONS = [ 'break', 'clone', 'continue', 'echo_print', 'negative_instanceof', 'others', 'return', 'switch_case', 'yield', 'yield_from', ]; private const TOKEN_TYPE_CONFIG_MAP = [ \T_BREAK => 'break', \T_CASE => 'switch_case', \T_CONTINUE => 'continue', \T_ECHO => 'echo_print', \T_PRINT => 'echo_print', \T_RETURN => 'return', \T_YIELD => 'yield', \T_YIELD_FROM => 'yield_from', ]; // handled by the `include` rule private const TOKEN_TYPE_NO_CONFIG = [ \T_REQUIRE, \T_REQUIRE_ONCE, \T_INCLUDE, \T_INCLUDE_ONCE, ]; private const KNOWN_NEGATIVE_PRE_TYPES = [ [CT::T_CLASS_CONSTANT], [CT::T_DYNAMIC_VAR_BRACE_CLOSE], [CT::T_RETURN_REF], [CT::T_USE_LAMBDA], [\T_ARRAY], [\T_CATCH], [\T_CLASS], [\T_DECLARE], [\T_ELSEIF], [\T_EMPTY], [\T_EXIT], [\T_EVAL], [\T_FN], [\T_FOREACH], [\T_FOR], [\T_FUNCTION], [\T_HALT_COMPILER], [\T_IF], [\T_ISSET], [\T_LIST], [\T_STRING], [\T_SWITCH], [\T_STATIC], [\T_UNSET], [\T_VARIABLE], [\T_WHILE], // handled by the `include` rule [\T_REQUIRE], [\T_REQUIRE_ONCE], [\T_INCLUDE], [\T_INCLUDE_ONCE], [FCT::T_MATCH], ]; /** * @var list<_PhpTokenPrototypePartial> */ private array $noopTypes; private TokensAnalyzer $tokensAnalyzer; public function __construct() { parent::__construct(); $this->noopTypes = [ '$', [\T_CONSTANT_ENCAPSED_STRING], [\T_DNUMBER], [\T_DOUBLE_COLON], [\T_LNUMBER], [\T_NS_SEPARATOR], [\T_STRING], [\T_VARIABLE], [\T_STATIC], // magic constants [\T_CLASS_C], [\T_DIR], [\T_FILE], [\T_FUNC_C], [\T_LINE], [\T_METHOD_C], [\T_NS_C], [\T_TRAIT_C], ]; foreach (Token::getObjectOperatorKinds() as $kind) { $this->noopTypes[] = [$kind]; } } public function getDefinition(): FixerDefinitionInterface { return new FixerDefinition( 'Removes unneeded parentheses around control statements.', [ new CodeSample( <<<'PHP' <?php while ($x) { while ($y) { break (2); } } clone($a); while ($y) { continue (2); } echo("foo"); print("foo"); return (1 + 2); switch ($a) { case($x); } yield(2); PHP, ), new CodeSample( <<<'PHP' <?php while ($x) { while ($y) { break (2); } } clone($a); while ($y) { continue (2); } PHP, ['statements' => ['break', 'continue']], ), ], ); } /** * {@inheritdoc} * * Must run before ConcatSpaceFixer, NewExpressionParenthesesFixer, NoTrailingWhitespaceFixer. * Must run after ModernizeTypesCastingFixer, NoAlternativeSyntaxFixer. */ public function getPriority(): int { return 30; } public function isCandidate(Tokens $tokens): bool { return $tokens->isAnyTokenKindsFound(['(', CT::T_BRACE_CLASS_INSTANTIATION_OPEN]); } protected function applyFix(\SplFileInfo $file, Tokens $tokens): void { $this->tokensAnalyzer = new TokensAnalyzer($tokens); foreach ($tokens as $openIndex => $token) { if ($token->equals('(')) { $closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex); } elseif ($token->isGivenKind(CT::T_BRACE_CLASS_INSTANTIATION_OPEN)) { $closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_BRACE_CLASS_INSTANTIATION, $openIndex); } else { continue; } $beforeOpenIndex = $tokens->getPrevMeaningfulToken($openIndex); $afterCloseIndex = $tokens->getNextMeaningfulToken($closeIndex); // do a cheap check for negative case: `X()` if ($tokens->getNextMeaningfulToken($openIndex) === $closeIndex) { if ($tokens[$beforeOpenIndex]->isGivenKind(\T_EXIT)) { $this->removeUselessParenthesisPair($tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, 'others'); } continue; } // do a cheap check for negative case: `foo(1,2)` if ($tokens[$beforeOpenIndex]->equalsAny(self::KNOWN_NEGATIVE_PRE_TYPES)) { continue; } // check for the simple useless wrapped cases if ($this->isUselessWrapped($tokens, $beforeOpenIndex, $afterCloseIndex)) { $this->removeUselessParenthesisPair($tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, $this->getConfigType($tokens, $beforeOpenIndex)); continue; } // handle `clone` statements if ($tokens[$beforeOpenIndex]->isGivenKind(\T_CLONE)) { if ($this->isWrappedCloneArgument($tokens, $beforeOpenIndex, $openIndex, $closeIndex, $afterCloseIndex)) { $this->removeUselessParenthesisPair($tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, 'clone'); } continue; } // handle `instance of` statements $instanceOfIndex = $this->getIndexOfInstanceOfStatement($tokens, $openIndex, $closeIndex); if (null !== $instanceOfIndex) { if ($this->isWrappedInstanceOf($tokens, $instanceOfIndex, $beforeOpenIndex, $openIndex, $closeIndex, $afterCloseIndex)) { $this->removeUselessParenthesisPair( $tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, $tokens[$beforeOpenIndex]->equals('!') ? 'negative_instanceof' : 'others', ); } continue; } // last checks deal with operators, do not swap around if ($this->isWrappedPartOfOperation($tokens, $beforeOpenIndex, $openIndex, $closeIndex, $afterCloseIndex)) { $this->removeUselessParenthesisPair($tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, $this->getConfigType($tokens, $beforeOpenIndex)); } } } protected function createConfigurationDefinition(): FixerConfigurationResolverInterface { $defaults = array_filter( self::CONFIG_OPTIONS, static fn (string $option): bool => 'negative_instanceof' !== $option && 'others' !== $option && 'yield_from' !== $option, ); return new FixerConfigurationResolver([ (new FixerOptionBuilder('statements', 'List of control statements to fix.')) ->setAllowedTypes(['string[]']) ->setAllowedValues([new AllowedValueSubset(self::CONFIG_OPTIONS)]) ->setDefault(array_values($defaults)) ->getOption(), ]); } private function isUselessWrapped(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool { return $this->isSingleStatement($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedFnBody($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedForElement($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedLanguageConstructArgument($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedSequenceElement($tokens, $beforeOpenIndex, $afterCloseIndex); } private function isWrappedCloneArgument(Tokens $tokens, int $beforeOpenIndex, int $openIndex, int $closeIndex, int $afterCloseIndex): bool { $beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); if ( !( $tokens[$beforeOpenIndex]->equals('?') // For BC reasons || $this->isSimpleAssignment($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isSingleStatement($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedFnBody($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedForElement($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedSequenceElement($tokens, $beforeOpenIndex, $afterCloseIndex) ) ) { return false; } $newCandidateIndex = $tokens->getNextMeaningfulToken($openIndex); if ($tokens[$newCandidateIndex]->isGivenKind(\T_NEW)) { $openIndex = $newCandidateIndex; // `clone (new X)`, `clone (new X())`, clone (new X(Y))` } return !$this->containsOperation($tokens, $openIndex, $closeIndex); } private function getIndexOfInstanceOfStatement(Tokens $tokens, int $openIndex, int $closeIndex): ?int { $instanceOfIndex = $tokens->findGivenKind(\T_INSTANCEOF, $openIndex, $closeIndex); return 1 === \count($instanceOfIndex) ? array_key_first($instanceOfIndex) : null; } private function isWrappedInstanceOf(Tokens $tokens, int $instanceOfIndex, int $beforeOpenIndex, int $openIndex, int $closeIndex, int $afterCloseIndex): bool { if ( $this->containsOperation($tokens, $openIndex, $instanceOfIndex) || $this->containsOperation($tokens, $instanceOfIndex, $closeIndex) ) { return false; } if ($tokens[$beforeOpenIndex]->equals('!')) { $beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); } return $this->isSimpleAssignment($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isSingleStatement($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedFnBody($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedForElement($tokens, $beforeOpenIndex, $afterCloseIndex) || $this->isWrappedSequenceElement($tokens, $beforeOpenIndex, $afterCloseIndex); } private function isWrappedPartOfOperation(Tokens $tokens, int $beforeOpenIndex, int $openIndex, int $closeIndex, int $afterCloseIndex): bool { if ($this->containsOperation($tokens, $openIndex, $closeIndex)) { return false; } $boundariesMoved = false; if ($this->isPreUnaryOperation($tokens, $beforeOpenIndex)) { $beforeOpenIndex = $this->getBeforePreUnaryOperation($tokens, $beforeOpenIndex); $boundariesMoved = true; } if ($this->isAccess($tokens, $afterCloseIndex)) { $afterCloseIndex = $this->getAfterAccess($tokens, $afterCloseIndex); $boundariesMoved = true; if ($this->tokensAnalyzer->isUnarySuccessorOperator($afterCloseIndex)) { // post unary operation are only valid here $afterCloseIndex = $tokens->getNextMeaningfulToken($afterCloseIndex); } } if ($boundariesMoved) { if ($tokens[$beforeOpenIndex]->equalsAny(self::KNOWN_NEGATIVE_PRE_TYPES)) { return false; } if ($this->isUselessWrapped($tokens, $beforeOpenIndex, $afterCloseIndex)) { return true; } } // check if part of some operation sequence $beforeIsBinaryOperation = $this->tokensAnalyzer->isBinaryOperator($beforeOpenIndex); $afterIsBinaryOperation = $this->tokensAnalyzer->isBinaryOperator($afterCloseIndex); if ($beforeIsBinaryOperation && $afterIsBinaryOperation) { return true; // `+ (x) +` } $beforeToken = $tokens[$beforeOpenIndex]; $afterToken = $tokens[$afterCloseIndex]; $beforeIsBlockOpenOrComma = $beforeToken->equals(',') || null !== $this->getBlock($tokens, $beforeOpenIndex, true); $afterIsBlockEndOrComma = $afterToken->equals(',') || null !== $this->getBlock($tokens, $afterCloseIndex, false); if (($beforeIsBlockOpenOrComma && $afterIsBinaryOperation) || ($beforeIsBinaryOperation && $afterIsBlockEndOrComma)) { // $beforeIsBlockOpenOrComma && $afterIsBlockEndOrComma is covered by `isWrappedSequenceElement` // `[ (x) +` or `+ (X) ]` or `, (X) +` or `+ (X) ,` return true; } if ($tokens[$beforeOpenIndex]->equals('}')) { $beforeIsStatementOpen = !$this->closeCurlyBelongsToDynamicElement($tokens, $beforeOpenIndex); } else { $beforeIsStatementOpen = $beforeToken->equalsAny(self::BEFORE_TYPES) || $beforeToken->isGivenKind(\T_CASE); } $afterIsStatementEnd = $afterToken->equalsAny([';', [\T_CLOSE_TAG]]); return ($beforeIsStatementOpen && $afterIsBinaryOperation) // `<?php (X) +` || ($beforeIsBinaryOperation && $afterIsStatementEnd); // `+ (X);` } // bounded `print|yield|yield from|require|require_once|include|include_once (X)` private function isWrappedLanguageConstructArgument(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool { if (!$tokens[$beforeOpenIndex]->isGivenKind([\T_PRINT, \T_YIELD, \T_YIELD_FROM, \T_REQUIRE, \T_REQUIRE_ONCE, \T_INCLUDE, \T_INCLUDE_ONCE])) { return false; } $beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); return $this->isWrappedSequenceElement($tokens, $beforeOpenIndex, $afterCloseIndex); } // any of `<?php|<?|<?=|;|throw|return|... (X) ;|T_CLOSE` private function isSingleStatement(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool { if ($tokens[$beforeOpenIndex]->isGivenKind(\T_CASE)) { return $tokens[$afterCloseIndex]->equalsAny([':', ';']); // `switch case` } if (!$tokens[$afterCloseIndex]->equalsAny([';', [\T_CLOSE_TAG]])) { return false; } if ($tokens[$beforeOpenIndex]->equals('}')) { return !$this->closeCurlyBelongsToDynamicElement($tokens, $beforeOpenIndex); } return $tokens[$beforeOpenIndex]->equalsAny(self::BEFORE_TYPES); } private function isSimpleAssignment(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool { return $tokens[$beforeOpenIndex]->equals('=') && $tokens[$afterCloseIndex]->equalsAny([';', [\T_CLOSE_TAG]]); // `= (X) ;` } private function isWrappedSequenceElement(Tokens $tokens, int $startIndex, int $endIndex): bool { $startIsComma = $tokens[$startIndex]->equals(','); $endIsComma = $tokens[$endIndex]->equals(','); if ($startIsComma && $endIsComma) { return true; // `,(X),` } $blockTypeStart = $this->getBlock($tokens, $startIndex, true); $blockTypeEnd = $this->getBlock($tokens, $endIndex, false); return ($startIsComma && null !== $blockTypeEnd) // `,(X)]` || ($endIsComma && null !== $blockTypeStart) // `[(X),` || (null !== $blockTypeEnd && null !== $blockTypeStart); // any type of `{(X)}`, `[(X)]` and `((X))` } // any of `for( (X); ;(X)) ;` note that the middle element is covered as 'single statement' as it is `; (X) ;` private function isWrappedForElement(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool { $forCandidateIndex = null; if ($tokens[$beforeOpenIndex]->equals('(') && $tokens[$afterCloseIndex]->equals(';')) { $forCandidateIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); } elseif ($tokens[$afterCloseIndex]->equals(')') && $tokens[$beforeOpenIndex]->equals(';')) { $forCandidateIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $afterCloseIndex); $forCandidateIndex = $tokens->getPrevMeaningfulToken($forCandidateIndex); } return null !== $forCandidateIndex && $tokens[$forCandidateIndex]->isGivenKind(\T_FOR); } // `fn() => (X);` private function isWrappedFnBody(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool { if (!$tokens[$beforeOpenIndex]->isGivenKind(\T_DOUBLE_ARROW)) { return false; } $beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); if ($tokens[$beforeOpenIndex]->isGivenKind(\T_STRING)) { while (true) { $beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); if (!$tokens[$beforeOpenIndex]->isGivenKind([\T_STRING, CT::T_TYPE_INTERSECTION, CT::T_TYPE_ALTERNATION])) { break; } } if (!$tokens[$beforeOpenIndex]->isGivenKind(CT::T_TYPE_COLON)) { return false; } $beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); } if (!$tokens[$beforeOpenIndex]->equals(')')) { return false; } $beforeOpenIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $beforeOpenIndex); $beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); if ($tokens[$beforeOpenIndex]->isGivenKind(CT::T_RETURN_REF)) { $beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex); } if (!$tokens[$beforeOpenIndex]->isGivenKind(\T_FN)) { return false; } return $tokens[$afterCloseIndex]->equalsAny([';', ',', [\T_CLOSE_TAG]]); } private function isPreUnaryOperation(Tokens $tokens, int $index): bool { return $this->tokensAnalyzer->isUnaryPredecessorOperator($index) || $tokens[$index]->isCast(); } private function getBeforePreUnaryOperation(Tokens $tokens, int $index): int { do { $index = $tokens->getPrevMeaningfulToken($index); } while ($this->isPreUnaryOperation($tokens, $index)); return $index; } // array access `(X)[` or `(X){` or object access `(X)->` or `(X)?->` private function isAccess(Tokens $tokens, int $index): bool { $token = $tokens[$index]; return $token->isObjectOperator() || $token->equals('[') || $token->isGivenKind(CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN); } private function getAfterAccess(Tokens $tokens, int $index): int { while (true) { $block = $this->getBlock($tokens, $index, true); if (null !== $block) { $index = $tokens->findBlockEnd($block['type'], $index); $index = $tokens->getNextMeaningfulToken($index); continue; } if ( $tokens[$index]->isObjectOperator() || $tokens[$index]->equalsAny(['$', [\T_PAAMAYIM_NEKUDOTAYIM], [\T_STRING], [\T_VARIABLE]]) ) { $index = $tokens->getNextMeaningfulToken($index); continue; } break; } return $index; } /** * @return null|array{type: Tokens::BLOCK_TYPE_*, isStart: bool} */ private function getBlock(Tokens $tokens, int $index, bool $isStart): ?array { $block = Tokens::detectBlockType($tokens[$index]); return null !== $block && $isStart === $block['isStart'] && \in_array($block['type'], self::BLOCK_TYPES, true) ? $block : null; } private function containsOperation(Tokens $tokens, int $startIndex, int $endIndex): bool { while (true) { $startIndex = $tokens->getNextMeaningfulToken($startIndex); if ($startIndex === $endIndex) { break; } $block = Tokens::detectBlockType($tokens[$startIndex]); if (null !== $block && $block['isStart']) { $startIndex = $tokens->findBlockEnd($block['type'], $startIndex); continue; } if (!$tokens[$startIndex]->equalsAny($this->noopTypes)) { return true; } } return false; } private function getConfigType(Tokens $tokens, int $beforeOpenIndex): ?string { if ($tokens[$beforeOpenIndex]->isGivenKind(self::TOKEN_TYPE_NO_CONFIG)) { return null; } foreach (self::TOKEN_TYPE_CONFIG_MAP as $type => $configItem) { if ($tokens[$beforeOpenIndex]->isGivenKind($type)) { return $configItem; } } return 'others'; } private function removeUselessParenthesisPair( Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex, int $openIndex, int $closeIndex, ?string $configType ): void { $statements = $this->configuration['statements']; if (null === $configType || !\in_array($configType, $statements, true)) { return; } $needsSpaceAfter = !$this->isAccess($tokens, $afterCloseIndex) && !$tokens[$afterCloseIndex]->equalsAny([';', ',', [\T_CLOSE_TAG]]) && null === $this->getBlock($tokens, $afterCloseIndex, false) && !($tokens[$afterCloseIndex]->equalsAny([':', ';']) && $tokens[$beforeOpenIndex]->isGivenKind(\T_CASE)); $needsSpaceBefore = !$this->isPreUnaryOperation($tokens, $beforeOpenIndex) && !$tokens[$beforeOpenIndex]->equalsAny(['}', [\T_EXIT], [\T_OPEN_TAG]]) && null === $this->getBlock($tokens, $beforeOpenIndex, true); $this->removeBrace($tokens, $closeIndex, $needsSpaceAfter); $this->removeBrace($tokens, $openIndex, $needsSpaceBefore); } private function removeBrace(Tokens $tokens, int $index, bool $needsSpace): void { if ($needsSpace) { foreach ([-1, 1] as $direction) { $siblingIndex = $tokens->getNonEmptySibling($index, $direction); if ($tokens[$siblingIndex]->isWhitespace() || $tokens[$siblingIndex]->isComment()) { $needsSpace = false; break; } } } if ($needsSpace) { $tokens[$index] = new Token([\T_WHITESPACE, ' ']); } else { $tokens->clearTokenAndMergeSurroundingWhitespace($index); } } private function closeCurlyBelongsToDynamicElement(Tokens $tokens, int $beforeOpenIndex): bool { $index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $beforeOpenIndex); $index = $tokens->getPrevMeaningfulToken($index); if ($tokens[$index]->isGivenKind(\T_DOUBLE_COLON)) { return true; } if ($tokens[$index]->equals(':')) { $index = $tokens->getPrevTokenOfKind($index, [[\T_CASE], '?']); return !$tokens[$index]->isGivenKind(\T_CASE); } return false; } }