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
:
NewWithParenthesesFixer.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\CodeSample; use PhpCsFixer\FixerDefinition\FixerDefinition; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Future; use PhpCsFixer\Tokenizer\CT; use PhpCsFixer\Tokenizer\FCT; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; /** * @phpstan-type _AutogeneratedInputConfiguration array{ * anonymous_class?: bool, * named_class?: bool, * } * @phpstan-type _AutogeneratedComputedConfiguration array{ * anonymous_class: bool, * named_class: bool, * } * * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> * * @author Dariusz Rumiński <dariusz.ruminski@gmail.com> * * @no-named-arguments Parameter names are not covered by the backward compatibility promise. */ final class NewWithParenthesesFixer extends AbstractFixer implements ConfigurableFixerInterface { /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */ use ConfigurableFixerTrait; private const NEXT_TOKEN_KINDS = [ '?', ';', ',', '(', ')', '[', ']', ':', '<', '>', '+', '-', '*', '/', '%', '&', '^', '|', [\T_CLASS], [\T_IS_SMALLER_OR_EQUAL], [\T_IS_GREATER_OR_EQUAL], [\T_IS_EQUAL], [\T_IS_NOT_EQUAL], [\T_IS_IDENTICAL], [\T_IS_NOT_IDENTICAL], [\T_CLOSE_TAG], [\T_LOGICAL_AND], [\T_LOGICAL_OR], [\T_LOGICAL_XOR], [\T_BOOLEAN_AND], [\T_BOOLEAN_OR], [\T_SL], [\T_SR], [\T_INSTANCEOF], [\T_AS], [\T_DOUBLE_ARROW], [\T_POW], [\T_SPACESHIP], [CT::T_ARRAY_SQUARE_BRACE_OPEN], [CT::T_ARRAY_SQUARE_BRACE_CLOSE], [CT::T_BRACE_CLASS_INSTANTIATION_OPEN], [CT::T_BRACE_CLASS_INSTANTIATION_CLOSE], [FCT::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG], [FCT::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG], ]; public function getDefinition(): FixerDefinitionInterface { return new FixerDefinition( 'All instances created with `new` keyword must (not) be followed by parentheses.', [ new CodeSample("<?php\n\n\$x = new X;\n\$y = new class {};\n"), new CodeSample( "<?php\n\n\$y = new class() {};\n", ['anonymous_class' => false], ), new CodeSample( "<?php\n\n\$x = new X();\n", ['named_class' => false], ), ], ); } /** * {@inheritdoc} * * Must run before ClassDefinitionFixer, NewExpressionParenthesesFixer. */ public function getPriority(): int { return 38; } public function isCandidate(Tokens $tokens): bool { return $tokens->isTokenKindFound(\T_NEW); } /** * @protected * * @TODO 4.0 move visibility from annotation to code when `NewWithBracesFixer` is removed */ public function createConfigurationDefinition(): FixerConfigurationResolverInterface { return new FixerConfigurationResolver([ (new FixerOptionBuilder('named_class', 'Whether named classes should be followed by parentheses.')) ->setAllowedTypes(['bool']) ->setDefault(true) ->getOption(), (new FixerOptionBuilder('anonymous_class', 'Whether anonymous classes should be followed by parentheses.')) ->setAllowedTypes(['bool']) ->setDefault(Future::getV4OrV3(false, true)) ->getOption(), ]); } protected function applyFix(\SplFileInfo $file, Tokens $tokens): void { for ($index = $tokens->count() - 3; $index > 0; --$index) { if (!$tokens[$index]->isGivenKind(\T_NEW)) { continue; } $nextIndex = $tokens->getNextTokenOfKind($index, self::NEXT_TOKEN_KINDS); // new anonymous class definition if ($tokens[$nextIndex]->isGivenKind(\T_CLASS)) { $nextIndex = $tokens->getNextMeaningfulToken($nextIndex); if (true === $this->configuration['anonymous_class']) { $this->ensureParenthesesAt($tokens, $nextIndex); } else { $this->ensureNoParenthesesAt($tokens, $nextIndex); } continue; } // entrance into array index syntax - need to look for exit while ($tokens[$nextIndex]->equals('[') || $tokens[$nextIndex]->isGivenKind(CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN)) { $nextIndex = $tokens->findBlockEnd(Tokens::detectBlockType($tokens[$nextIndex])['type'], $nextIndex); $nextIndex = $tokens->getNextMeaningfulToken($nextIndex); } if (true === $this->configuration['named_class']) { $this->ensureParenthesesAt($tokens, $nextIndex); } else { $this->ensureNoParenthesesAt($tokens, $nextIndex); } } } private function ensureParenthesesAt(Tokens $tokens, int $index): void { $token = $tokens[$index]; if (!$token->equals('(') && !$token->isObjectOperator()) { $tokens->insertAt( $tokens->getPrevMeaningfulToken($index) + 1, [new Token('('), new Token(')')], ); } } private function ensureNoParenthesesAt(Tokens $tokens, int $index): void { if (!$tokens[$index]->equals('(')) { return; } $closingIndex = $tokens->getNextMeaningfulToken($index); // constructor has arguments - parentheses can not be removed if (!$tokens[$closingIndex]->equals(')')) { return; } // Check if there's an object operator after the closing parenthesis // Preserve parentheses in expressions like "new A()->method()" as per RFC $afterClosingIndex = $tokens->getNextMeaningfulToken($closingIndex); if ($tokens[$afterClosingIndex]->isObjectOperator()) { return; } $tokens->clearTokenAndMergeSurroundingWhitespace($closingIndex); $tokens->clearTokenAndMergeSurroundingWhitespace($index); } }