|
@@ -14,23 +14,19 @@ declare(strict_types=1);
|
|
|
|
|
|
namespace PhpCsFixer\Fixer\Basic;
|
|
|
|
|
|
-use PhpCsFixer\AbstractFixer;
|
|
|
+use PhpCsFixer\AbstractProxyFixer;
|
|
|
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
|
|
+use PhpCsFixer\Fixer\DeprecatedFixerInterface;
|
|
|
use PhpCsFixer\Fixer\Indentation;
|
|
|
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
|
|
|
-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;
|
|
|
-use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
|
|
|
|
|
-final class CurlyBracesPositionFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
|
|
|
+/**
|
|
|
+ * @deprecated
|
|
|
+ */
|
|
|
+final class CurlyBracesPositionFixer extends AbstractProxyFixer implements ConfigurableFixerInterface, DeprecatedFixerInterface, WhitespacesAwareFixerInterface
|
|
|
{
|
|
|
use Indentation;
|
|
|
|
|
@@ -44,93 +40,32 @@ final class CurlyBracesPositionFixer extends AbstractFixer implements Configurab
|
|
|
*/
|
|
|
public const SAME_LINE = 'same_line';
|
|
|
|
|
|
- public function getDefinition(): FixerDefinitionInterface
|
|
|
- {
|
|
|
- return new FixerDefinition(
|
|
|
- 'Curly braces must be placed as configured.',
|
|
|
- [
|
|
|
- new CodeSample(
|
|
|
- '<?php
|
|
|
-class Foo {
|
|
|
-}
|
|
|
+ private BracesPositionFixer $bracesPositionFixer;
|
|
|
|
|
|
-function foo() {
|
|
|
-}
|
|
|
+ public function __construct()
|
|
|
+ {
|
|
|
+ $this->bracesPositionFixer = new BracesPositionFixer();
|
|
|
|
|
|
-$foo = function()
|
|
|
-{
|
|
|
-};
|
|
|
+ parent::__construct();
|
|
|
+ }
|
|
|
|
|
|
-if (foo())
|
|
|
-{
|
|
|
- bar();
|
|
|
-}
|
|
|
+ public function getDefinition(): FixerDefinitionInterface
|
|
|
+ {
|
|
|
+ $fixerDefinition = $this->bracesPositionFixer->getDefinition();
|
|
|
|
|
|
-$foo = new class
|
|
|
-{
|
|
|
-};
|
|
|
-'
|
|
|
- ),
|
|
|
- new CodeSample(
|
|
|
- '<?php
|
|
|
-if (foo()) {
|
|
|
- bar();
|
|
|
-}
|
|
|
-',
|
|
|
- ['control_structures_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
|
|
|
- ),
|
|
|
- new CodeSample(
|
|
|
- '<?php
|
|
|
-function foo()
|
|
|
-{
|
|
|
-}
|
|
|
-',
|
|
|
- ['functions_opening_brace' => self::SAME_LINE]
|
|
|
- ),
|
|
|
- new CodeSample(
|
|
|
- '<?php
|
|
|
-$foo = function () {
|
|
|
-};
|
|
|
-',
|
|
|
- ['anonymous_functions_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
|
|
|
- ),
|
|
|
- new CodeSample(
|
|
|
- '<?php
|
|
|
-class Foo
|
|
|
-{
|
|
|
-}
|
|
|
-',
|
|
|
- ['classes_opening_brace' => self::SAME_LINE]
|
|
|
- ),
|
|
|
- new CodeSample(
|
|
|
- '<?php
|
|
|
-$foo = new class {
|
|
|
-};
|
|
|
-',
|
|
|
- ['anonymous_classes_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
|
|
|
- ),
|
|
|
- new CodeSample(
|
|
|
- '<?php
|
|
|
-$foo = new class { };
|
|
|
-$bar = new class { private $baz; };
|
|
|
-',
|
|
|
- ['allow_single_line_empty_anonymous_classes' => true]
|
|
|
- ),
|
|
|
- new CodeSample(
|
|
|
- '<?php
|
|
|
-$foo = function () { return true; };
|
|
|
-$bar = function () { $result = true;
|
|
|
- return $result; };
|
|
|
-',
|
|
|
- ['allow_single_line_anonymous_functions' => true]
|
|
|
- ),
|
|
|
- ]
|
|
|
+ return new FixerDefinition(
|
|
|
+ 'Curly braces must be placed as configured.',
|
|
|
+ $fixerDefinition->getCodeSamples(),
|
|
|
+ $fixerDefinition->getDescription(),
|
|
|
+ $fixerDefinition->getRiskyDescription()
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- public function isCandidate(Tokens $tokens): bool
|
|
|
+ public function configure(array $configuration): void
|
|
|
{
|
|
|
- return $tokens->isTokenKindFound('{');
|
|
|
+ $this->bracesPositionFixer->configure($configuration);
|
|
|
+
|
|
|
+ parent::configure($configuration);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -141,270 +76,25 @@ $bar = function () { $result = true;
|
|
|
*/
|
|
|
public function getPriority(): int
|
|
|
{
|
|
|
- return -2;
|
|
|
- }
|
|
|
-
|
|
|
- protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
|
|
- {
|
|
|
- $classyTokens = Token::getClassyTokenKinds();
|
|
|
- $controlStructureTokens = [T_DECLARE, T_DO, T_ELSE, T_ELSEIF, T_FINALLY, T_FOR, T_FOREACH, T_IF, T_WHILE, T_TRY, T_CATCH, T_SWITCH];
|
|
|
- // @TODO: drop condition when PHP 8.0+ is required
|
|
|
- if (\defined('T_MATCH')) {
|
|
|
- $controlStructureTokens[] = T_MATCH;
|
|
|
- }
|
|
|
-
|
|
|
- $tokensAnalyzer = new TokensAnalyzer($tokens);
|
|
|
-
|
|
|
- $allowSingleLineUntil = null;
|
|
|
-
|
|
|
- foreach ($tokens as $index => $token) {
|
|
|
- $allowSingleLine = false;
|
|
|
- $allowSingleLineIfEmpty = false;
|
|
|
-
|
|
|
- if ($token->isGivenKind($classyTokens)) {
|
|
|
- $openBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
|
|
|
-
|
|
|
- if ($tokensAnalyzer->isAnonymousClass($index)) {
|
|
|
- $allowSingleLineIfEmpty = $this->configuration['allow_single_line_empty_anonymous_classes'];
|
|
|
- $positionOption = 'anonymous_classes_opening_brace';
|
|
|
- } else {
|
|
|
- $positionOption = 'classes_opening_brace';
|
|
|
- }
|
|
|
- } elseif ($token->isGivenKind(T_FUNCTION)) {
|
|
|
- $openBraceIndex = $tokens->getNextTokenOfKind($index, ['{', ';']);
|
|
|
-
|
|
|
- if ($tokens[$openBraceIndex]->equals(';')) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if ($tokensAnalyzer->isLambda($index)) {
|
|
|
- $allowSingleLine = $this->configuration['allow_single_line_anonymous_functions'];
|
|
|
- $positionOption = 'anonymous_functions_opening_brace';
|
|
|
- } else {
|
|
|
- $positionOption = 'functions_opening_brace';
|
|
|
- }
|
|
|
- } elseif ($token->isGivenKind($controlStructureTokens)) {
|
|
|
- $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
|
|
|
- $openBraceIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
|
|
|
-
|
|
|
- if (!$tokens[$openBraceIndex]->equals('{')) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- $positionOption = 'control_structures_opening_brace';
|
|
|
- } else {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- $closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openBraceIndex);
|
|
|
-
|
|
|
- $addNewlinesInsideBraces = true;
|
|
|
- if ($allowSingleLine || $allowSingleLineIfEmpty || $index < $allowSingleLineUntil) {
|
|
|
- $addNewlinesInsideBraces = false;
|
|
|
-
|
|
|
- for ($indexInsideBraces = $openBraceIndex + 1; $indexInsideBraces < $closeBraceIndex; ++$indexInsideBraces) {
|
|
|
- $tokenInsideBraces = $tokens[$indexInsideBraces];
|
|
|
-
|
|
|
- if (
|
|
|
- ($allowSingleLineIfEmpty && !$tokenInsideBraces->isWhitespace() && !$tokenInsideBraces->isComment())
|
|
|
- || ($tokenInsideBraces->isWhitespace() && Preg::match('/\R/', $tokenInsideBraces->getContent()))
|
|
|
- ) {
|
|
|
- $addNewlinesInsideBraces = true;
|
|
|
-
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!$addNewlinesInsideBraces && null === $allowSingleLineUntil) {
|
|
|
- $allowSingleLineUntil = $closeBraceIndex;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (
|
|
|
- $addNewlinesInsideBraces
|
|
|
- && !$this->isFollowedByNewLine($tokens, $openBraceIndex)
|
|
|
- && !$this->hasCommentOnSameLine($tokens, $openBraceIndex)
|
|
|
- && !$tokens[$tokens->getNextMeaningfulToken($openBraceIndex)]->isGivenKind(T_CLOSE_TAG)
|
|
|
- ) {
|
|
|
- $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex);
|
|
|
- if ($tokens->ensureWhitespaceAtIndex($openBraceIndex + 1, 0, $whitespace)) {
|
|
|
- ++$closeBraceIndex;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- $whitespace = ' ';
|
|
|
- if (self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END === $this->configuration[$positionOption]) {
|
|
|
- $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $index);
|
|
|
-
|
|
|
- $previousTokenIndex = $openBraceIndex;
|
|
|
- do {
|
|
|
- $previousTokenIndex = $tokens->getPrevMeaningfulToken($previousTokenIndex);
|
|
|
- } while ($tokens[$previousTokenIndex]->isGivenKind([CT::T_TYPE_COLON, CT::T_NULLABLE_TYPE, T_STRING, T_NS_SEPARATOR, CT::T_ARRAY_TYPEHINT, T_STATIC, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION, T_CALLABLE, CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_OPEN, CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_CLOSE]));
|
|
|
-
|
|
|
- if ($tokens[$previousTokenIndex]->equals(')')) {
|
|
|
- if ($tokens[--$previousTokenIndex]->isComment()) {
|
|
|
- --$previousTokenIndex;
|
|
|
- }
|
|
|
- if (
|
|
|
- $tokens[$previousTokenIndex]->isWhitespace()
|
|
|
- && Preg::match('/\R/', $tokens[$previousTokenIndex]->getContent())
|
|
|
- ) {
|
|
|
- $whitespace = ' ';
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- $moveBraceToIndex = null;
|
|
|
-
|
|
|
- if (' ' === $whitespace) {
|
|
|
- $previousMeaningfulIndex = $tokens->getPrevMeaningfulToken($openBraceIndex);
|
|
|
- for ($indexBeforeOpenBrace = $openBraceIndex - 1; $indexBeforeOpenBrace > $previousMeaningfulIndex; --$indexBeforeOpenBrace) {
|
|
|
- if (!$tokens[$indexBeforeOpenBrace]->isComment()) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- $tokenBeforeOpenBrace = $tokens[--$indexBeforeOpenBrace];
|
|
|
- if ($tokenBeforeOpenBrace->isWhitespace()) {
|
|
|
- $moveBraceToIndex = $indexBeforeOpenBrace;
|
|
|
- } elseif ($indexBeforeOpenBrace === $previousMeaningfulIndex) {
|
|
|
- $moveBraceToIndex = $previousMeaningfulIndex + 1;
|
|
|
- }
|
|
|
- }
|
|
|
- } elseif (!$tokens[$openBraceIndex - 1]->isWhitespace() || !Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) {
|
|
|
- for ($indexAfterOpenBrace = $openBraceIndex + 1; $indexAfterOpenBrace < $closeBraceIndex; ++$indexAfterOpenBrace) {
|
|
|
- if ($tokens[$indexAfterOpenBrace]->isWhitespace() && Preg::match('/\R/', $tokens[$indexAfterOpenBrace]->getContent())) {
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if ($tokens[$indexAfterOpenBrace]->isComment() && !str_starts_with($tokens[$indexAfterOpenBrace]->getContent(), '/*')) {
|
|
|
- $moveBraceToIndex = $indexAfterOpenBrace + 1;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (null !== $moveBraceToIndex) {
|
|
|
- /** @var Token $movedToken */
|
|
|
- $movedToken = clone $tokens[$openBraceIndex];
|
|
|
-
|
|
|
- $delta = $openBraceIndex < $moveBraceToIndex ? 1 : -1;
|
|
|
-
|
|
|
- if ($tokens[$openBraceIndex + $delta]->isWhitespace()) {
|
|
|
- if (-1 === $delta && Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) {
|
|
|
- $content = Preg::replace('/^(\h*?\R)?\h*/', '', $tokens[$openBraceIndex + 1]->getContent());
|
|
|
- if ('' !== $content) {
|
|
|
- $tokens[$openBraceIndex + 1] = new Token([T_WHITESPACE, $content]);
|
|
|
- } else {
|
|
|
- $tokens->clearAt($openBraceIndex + 1);
|
|
|
- }
|
|
|
- } elseif ($tokens[$openBraceIndex - 1]->isWhitespace()) {
|
|
|
- $tokens->clearAt($openBraceIndex - 1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (; $openBraceIndex !== $moveBraceToIndex; $openBraceIndex += $delta) {
|
|
|
- /** @var Token $siblingToken */
|
|
|
- $siblingToken = $tokens[$openBraceIndex + $delta];
|
|
|
- $tokens[$openBraceIndex] = $siblingToken;
|
|
|
- }
|
|
|
-
|
|
|
- $tokens[$openBraceIndex] = $movedToken;
|
|
|
-
|
|
|
- $openBraceIndex = $moveBraceToIndex;
|
|
|
- }
|
|
|
-
|
|
|
- if ($tokens->ensureWhitespaceAtIndex($openBraceIndex - 1, 1, $whitespace)) {
|
|
|
- ++$closeBraceIndex;
|
|
|
- if (null !== $allowSingleLineUntil) {
|
|
|
- ++$allowSingleLineUntil;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (
|
|
|
- !$addNewlinesInsideBraces
|
|
|
- || $tokens[$tokens->getPrevMeaningfulToken($closeBraceIndex)]->isGivenKind(T_OPEN_TAG)
|
|
|
- ) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- for ($prevIndex = $closeBraceIndex - 1; $tokens->isEmptyAt($prevIndex); --$prevIndex);
|
|
|
-
|
|
|
- $prevToken = $tokens[$prevIndex];
|
|
|
- if ($prevToken->isWhitespace() && Preg::match('/\R/', $prevToken->getContent())) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- $whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex);
|
|
|
- $tokens->ensureWhitespaceAtIndex($prevIndex, 1, $whitespace);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
|
|
- {
|
|
|
- return new FixerConfigurationResolver([
|
|
|
- (new FixerOptionBuilder('control_structures_opening_brace', 'The position of the opening brace of control structures‘ body.'))
|
|
|
- ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
|
|
- ->setDefault(self::SAME_LINE)
|
|
|
- ->getOption(),
|
|
|
- (new FixerOptionBuilder('functions_opening_brace', 'The position of the opening brace of functions‘ body.'))
|
|
|
- ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
|
|
- ->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END)
|
|
|
- ->getOption(),
|
|
|
- (new FixerOptionBuilder('anonymous_functions_opening_brace', 'The position of the opening brace of anonymous functions‘ body.'))
|
|
|
- ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
|
|
- ->setDefault(self::SAME_LINE)
|
|
|
- ->getOption(),
|
|
|
- (new FixerOptionBuilder('classes_opening_brace', 'The position of the opening brace of classes‘ body.'))
|
|
|
- ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
|
|
- ->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END)
|
|
|
- ->getOption(),
|
|
|
- (new FixerOptionBuilder('anonymous_classes_opening_brace', 'The position of the opening brace of anonymous classes‘ body.'))
|
|
|
- ->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
|
|
- ->setDefault(self::SAME_LINE)
|
|
|
- ->getOption(),
|
|
|
- (new FixerOptionBuilder('allow_single_line_empty_anonymous_classes', 'Allow anonymous classes to have opening and closing braces on the same line.'))
|
|
|
- ->setAllowedTypes(['bool'])
|
|
|
- ->setDefault(true)
|
|
|
- ->getOption(),
|
|
|
- (new FixerOptionBuilder('allow_single_line_anonymous_functions', 'Allow anonymous functions to have opening and closing braces on the same line.'))
|
|
|
- ->setAllowedTypes(['bool'])
|
|
|
- ->setDefault(true)
|
|
|
- ->getOption(),
|
|
|
- ]);
|
|
|
+ return $this->bracesPositionFixer->getPriority();
|
|
|
}
|
|
|
|
|
|
- private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
|
|
|
+ public function getSuccessorsNames(): array
|
|
|
{
|
|
|
- $nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
|
|
|
- $nextToken = $tokens[$nextIndex];
|
|
|
-
|
|
|
- // return if next token is not opening parenthesis
|
|
|
- if (!$nextToken->equals('(')) {
|
|
|
- return $structureTokenIndex;
|
|
|
- }
|
|
|
-
|
|
|
- return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
|
|
|
+ return [
|
|
|
+ $this->bracesPositionFixer->getName(),
|
|
|
+ ];
|
|
|
}
|
|
|
|
|
|
- private function isFollowedByNewLine(Tokens $tokens, int $index): bool
|
|
|
+ protected function createProxyFixers(): array
|
|
|
{
|
|
|
- for (++$index, $max = \count($tokens) - 1; $index < $max; ++$index) {
|
|
|
- $token = $tokens[$index];
|
|
|
- if (!$token->isComment()) {
|
|
|
- return $token->isWhitespace() && Preg::match('/\R/', $token->getContent());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
+ return [
|
|
|
+ $this->bracesPositionFixer,
|
|
|
+ ];
|
|
|
}
|
|
|
|
|
|
- private function hasCommentOnSameLine(Tokens $tokens, int $index): bool
|
|
|
+ protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
|
|
{
|
|
|
- $token = $tokens[$index + 1];
|
|
|
-
|
|
|
- if ($token->isWhitespace() && !Preg::match('/\R/', $token->getContent())) {
|
|
|
- $token = $tokens[$index + 2];
|
|
|
- }
|
|
|
-
|
|
|
- return $token->isComment();
|
|
|
+ return $this->bracesPositionFixer->createConfigurationDefinition();
|
|
|
}
|
|
|
}
|