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
/
Operator
:
NewExpressionParenthesesFixer.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\Operator; use PhpCsFixer\AbstractFixer; use PhpCsFixer\Fixer\ConfigurableFixerInterface; use PhpCsFixer\Fixer\ConfigurableFixerTrait; use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver; use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface; use PhpCsFixer\FixerConfiguration\FixerOptionBuilder; use PhpCsFixer\FixerDefinition\FixerDefinition; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\FixerDefinition\VersionSpecification; use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample; use PhpCsFixer\Tokenizer\CT; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; /** * @phpstan-type _AutogeneratedInputConfiguration array{ * use_parentheses?: bool, * } * @phpstan-type _AutogeneratedComputedConfiguration array{ * use_parentheses: bool, * } * * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> * * @author Valentin Udaltsov <udaltsov.valentin@gmail.com> * * @no-named-arguments Parameter names are not covered by the backward compatibility promise. */ final class NewExpressionParenthesesFixer extends AbstractFixer implements ConfigurableFixerInterface { /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */ use ConfigurableFixerTrait; public function getDefinition(): FixerDefinitionInterface { return new FixerDefinition( 'All `new` expressions with a further call must (not) be wrapped in parentheses.', [ new VersionSpecificCodeSample( "<?php\n\n(new Foo())->bar();\n", new VersionSpecification(8_04_00), ), new VersionSpecificCodeSample( "<?php\n\n(new class {})->bar();\n", new VersionSpecification(8_04_00), ), new VersionSpecificCodeSample( "<?php\n\nnew Foo()->bar();\n", new VersionSpecification(8_04_00), ['use_parentheses' => true], ), new VersionSpecificCodeSample( "<?php\n\nnew class {}->bar();\n", new VersionSpecification(8_04_00), ['use_parentheses' => true], ), ], ); } /** * {@inheritdoc} * * Must run after NewWithParenthesesFixer, NoUnneededControlParenthesesFixer. */ public function getPriority(): int { return 29; } public function isCandidate(Tokens $tokens): bool { return \PHP_VERSION_ID >= 8_04_00 && $tokens->isTokenKindFound(\T_NEW); } protected function createConfigurationDefinition(): FixerConfigurationResolverInterface { return new FixerConfigurationResolver([ (new FixerOptionBuilder('use_parentheses', 'Whether `new` expressions with a further call should be wrapped in parentheses or not.')) ->setAllowedTypes(['bool']) ->setDefault(false) ->getOption(), ]); } protected function applyFix(\SplFileInfo $file, Tokens $tokens): void { $useParentheses = $this->configuration['use_parentheses']; for ($index = $tokens->count() - 3; $index > 0; --$index) { if (!$tokens[$index]->isGivenKind(\T_NEW)) { continue; } $classStartIndex = $tokens->getNextMeaningfulToken($index); if (null === $classStartIndex) { return; } // anonymous class if ($tokens[$classStartIndex]->isGivenKind(\T_CLASS)) { $nextIndex = $tokens->getNextMeaningfulToken($classStartIndex); if ($tokens[$nextIndex]->equals('(')) { $nextIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex); } else { $nextIndex = $classStartIndex; } $bodyStartIndex = $tokens->getNextTokenOfKind($nextIndex, ['{']); $bodyEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $bodyStartIndex); if ($useParentheses) { $this->ensureWrappedInParentheses($tokens, $index, $bodyEndIndex); } else { $this->ensureNotWrappedInParentheses($tokens, $index, $bodyEndIndex); } continue; } // named class $classEndIndex = $this->findClassEndIndex($tokens, $classStartIndex); if (null === $classEndIndex) { continue; } $nextIndex = $tokens->getNextMeaningfulToken($classEndIndex); if (!$tokens[$nextIndex]->equals('(')) { // If arguments' parentheses are absent then either this new expression is not further called // and does not need parentheses, or we cannot omit its parentheses due to the grammar rules. continue; } $argsEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex); if ($useParentheses) { $this->ensureWrappedInParentheses($tokens, $index, $argsEndIndex); } else { $this->ensureNotWrappedInParentheses($tokens, $index, $argsEndIndex); } } } private function ensureWrappedInParentheses(Tokens $tokens, int $exprStartIndex, int $exprEndIndex): void { $prevIndex = $tokens->getPrevMeaningfulToken($exprStartIndex); $nextIndex = $tokens->getNextMeaningfulToken($exprEndIndex); if ($tokens[$prevIndex]->isGivenKind(CT::T_BRACE_CLASS_INSTANTIATION_OPEN) && $tokens[$nextIndex]->isGivenKind(CT::T_BRACE_CLASS_INSTANTIATION_CLOSE) ) { return; } if (!$tokens[$nextIndex]->isObjectOperator() && !$tokens[$nextIndex]->isGivenKind(\T_PAAMAYIM_NEKUDOTAYIM)) { return; } $tokens->insertAt($exprStartIndex, [new Token([CT::T_BRACE_CLASS_INSTANTIATION_OPEN, '('])]); $tokens->insertAt($exprEndIndex + 2, [new Token([CT::T_BRACE_CLASS_INSTANTIATION_CLOSE, ')'])]); } private function ensureNotWrappedInParentheses(Tokens $tokens, int $exprStartIndex, int $exprEndIndex): void { $prevIndex = $tokens->getPrevMeaningfulToken($exprStartIndex); $nextIndex = $tokens->getNextMeaningfulToken($exprEndIndex); if (!$tokens[$prevIndex]->isGivenKind(CT::T_BRACE_CLASS_INSTANTIATION_OPEN) || !$tokens[$nextIndex]->isGivenKind(CT::T_BRACE_CLASS_INSTANTIATION_CLOSE) ) { return; } $operatorIndex = $tokens->getNextMeaningfulToken($nextIndex); if (!$tokens[$operatorIndex]->isObjectOperator() && !$tokens[$operatorIndex]->isGivenKind(\T_PAAMAYIM_NEKUDOTAYIM)) { return; } $tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex); $tokens->clearTokenAndMergeSurroundingWhitespace($nextIndex); } private function findClassEndIndex(Tokens $tokens, int $index): ?int { // (expression) class name if ($tokens[$index]->equals('(')) { return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index); } // regular class name or $variable class name $nextTokens = [ [\T_STRING], [\T_NS_SEPARATOR], [CT::T_NAMESPACE_OPERATOR], [\T_VARIABLE], '$', [CT::T_DYNAMIC_VAR_BRACE_OPEN], '[', [\T_OBJECT_OPERATOR], [\T_NULLSAFE_OBJECT_OPERATOR], [\T_PAAMAYIM_NEKUDOTAYIM], ]; if (!$tokens[$index]->equalsAny($nextTokens)) { return null; } while ($tokens[$index]->equalsAny($nextTokens)) { $blockType = Tokens::detectBlockType($tokens[$index]); if (null !== $blockType) { $index = $tokens->findBlockEnd($blockType['type'], $index); } $index = $tokens->getNextMeaningfulToken($index); } return $index - 1; } }