Browse Source

feat: Deprecate `CurlyBracesPositionFixer` and proxy to `BracesPositionFixer` (#7334)

Andreas Möller 1 year ago
parent
commit
0fa21a26b1

+ 4 - 4
dev-tools/phpstan/baseline.php

@@ -39,22 +39,22 @@ $ignoreErrors[] = [
 $ignoreErrors[] = [
 	'message' => '#^Only booleans are allowed in &&, mixed given on the left side\\.$#',
 	'count' => 1,
-	'path' => __DIR__ . '/../../src/Fixer/Basic/CurlyBracesPositionFixer.php',
+	'path' => __DIR__ . '/../../src/Fixer/Basic/BracesPositionFixer.php',
 ];
 $ignoreErrors[] = [
 	'message' => '#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#',
 	'count' => 1,
-	'path' => __DIR__ . '/../../src/Fixer/Basic/CurlyBracesPositionFixer.php',
+	'path' => __DIR__ . '/../../src/Fixer/Basic/BracesPositionFixer.php',
 ];
 $ignoreErrors[] = [
 	'message' => '#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#',
 	'count' => 1,
-	'path' => __DIR__ . '/../../src/Fixer/Basic/CurlyBracesPositionFixer.php',
+	'path' => __DIR__ . '/../../src/Fixer/Basic/BracesPositionFixer.php',
 ];
 $ignoreErrors[] = [
 	'message' => '#^Variable \\$prevIndex might not be defined\\.$#',
 	'count' => 2,
-	'path' => __DIR__ . '/../../src/Fixer/Basic/CurlyBracesPositionFixer.php',
+	'path' => __DIR__ . '/../../src/Fixer/Basic/BracesPositionFixer.php',
 ];
 $ignoreErrors[] = [
 	'message' => '#^Only booleans are allowed in &&, mixed given on the left side\\.$#',

+ 41 - 2
doc/list.rst

@@ -186,6 +186,45 @@ List of Available Rules
 
 
    `Source PhpCsFixer\\Fixer\\Basic\\BracesFixer <./../src/Fixer/Basic/BracesFixer.php>`_
+-  `braces_position <./rules/basic/braces_position.rst>`_
+
+   Braces must be placed as configured.
+
+   Configuration options:
+
+   - | ``control_structures_opening_brace``
+     | The position of the opening brace of control structures‘ body.
+     | Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+     | Default value: ``'same_line'``
+   - | ``functions_opening_brace``
+     | The position of the opening brace of functions‘ body.
+     | Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+     | Default value: ``'next_line_unless_newline_at_signature_end'``
+   - | ``anonymous_functions_opening_brace``
+     | The position of the opening brace of anonymous functions‘ body.
+     | Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+     | Default value: ``'same_line'``
+   - | ``classes_opening_brace``
+     | The position of the opening brace of classes‘ body.
+     | Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+     | Default value: ``'next_line_unless_newline_at_signature_end'``
+   - | ``anonymous_classes_opening_brace``
+     | The position of the opening brace of anonymous classes‘ body.
+     | Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+     | Default value: ``'same_line'``
+   - | ``allow_single_line_empty_anonymous_classes``
+     | Allow anonymous classes to have opening and closing braces on the same line.
+     | Allowed types: ``bool``
+     | Default value: ``true``
+   - | ``allow_single_line_anonymous_functions``
+     | Allow anonymous functions to have opening and closing braces on the same line.
+     | Allowed types: ``bool``
+     | Default value: ``true``
+
+
+   Part of rule sets `@PER <./ruleSets/PER.rst>`_ `@PER-CS <./ruleSets/PER-CS.rst>`_ `@PER-CS1.0 <./ruleSets/PER-CS1.0.rst>`_ `@PER-CS2.0 <./ruleSets/PER-CS2.0.rst>`_ `@PSR2 <./ruleSets/PSR2.rst>`_ `@PSR12 <./ruleSets/PSR12.rst>`_ `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_
+
+   `Source PhpCsFixer\\Fixer\\Basic\\BracesPositionFixer <./../src/Fixer/Basic/BracesPositionFixer.php>`_
 -  `cast_spaces <./rules/cast_notation/cast_spaces.rst>`_
 
    A single space or none should be between cast and variable.
@@ -383,6 +422,8 @@ List of Available Rules
 
    Curly braces must be placed as configured.
 
+   *warning deprecated*   Use ``braces_position`` instead.
+
    Configuration options:
 
    - | ``control_structures_opening_brace``
@@ -415,8 +456,6 @@ List of Available Rules
      | Default value: ``true``
 
 
-   Part of rule sets `@PER <./ruleSets/PER.rst>`_ `@PER-CS <./ruleSets/PER-CS.rst>`_ `@PER-CS1.0 <./ruleSets/PER-CS1.0.rst>`_ `@PER-CS2.0 <./ruleSets/PER-CS2.0.rst>`_ `@PSR2 <./ruleSets/PSR2.rst>`_ `@PSR12 <./ruleSets/PSR12.rst>`_ `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_
-
    `Source PhpCsFixer\\Fixer\\Basic\\CurlyBracesPositionFixer <./../src/Fixer/Basic/CurlyBracesPositionFixer.php>`_
 -  `date_time_create_from_format_call <./rules/function_notation/date_time_create_from_format_call.rst>`_
 

+ 4 - 4
doc/ruleSets/PSR12.rst

@@ -15,15 +15,15 @@ Rules
 - `blank_line_after_opening_tag <./../rules/php_tag/blank_line_after_opening_tag.rst>`_
 - `blank_line_between_import_groups <./../rules/whitespace/blank_line_between_import_groups.rst>`_
 - `blank_lines_before_namespace <./../rules/namespace_notation/blank_lines_before_namespace.rst>`_
+- `braces_position <./../rules/basic/braces_position.rst>`_ with config:
+
+  ``['allow_single_line_empty_anonymous_classes' => true]``
+
 - `class_definition <./../rules/class_notation/class_definition.rst>`_ with config:
 
   ``['inline_constructor_arguments' => false, 'space_before_parenthesis' => true]``
 
 - `compact_nullable_type_declaration <./../rules/whitespace/compact_nullable_type_declaration.rst>`_
-- `curly_braces_position <./../rules/basic/curly_braces_position.rst>`_ with config:
-
-  ``['allow_single_line_empty_anonymous_classes' => true]``
-
 - `declare_equal_normalize <./../rules/language_construct/declare_equal_normalize.rst>`_
 - `lowercase_cast <./../rules/cast_notation/lowercase_cast.rst>`_
 - `lowercase_static_reference <./../rules/casing/lowercase_static_reference.rst>`_

+ 1 - 1
doc/ruleSets/PSR2.rst

@@ -9,11 +9,11 @@ Rules
 
 - `@PSR1 <./PSR1.rst>`_
 - `blank_line_after_namespace <./../rules/namespace_notation/blank_line_after_namespace.rst>`_
+- `braces_position <./../rules/basic/braces_position.rst>`_
 - `class_definition <./../rules/class_notation/class_definition.rst>`_
 - `constant_case <./../rules/casing/constant_case.rst>`_
 - `control_structure_braces <./../rules/control_structure/control_structure_braces.rst>`_
 - `control_structure_continuation_position <./../rules/control_structure/control_structure_continuation_position.rst>`_
-- `curly_braces_position <./../rules/basic/curly_braces_position.rst>`_
 - `elseif <./../rules/control_structure/elseif.rst>`_
 - `function_declaration <./../rules/function_notation/function_declaration.rst>`_
 - `indentation_type <./../rules/whitespace/indentation_type.rst>`_

+ 4 - 4
doc/ruleSets/Symfony.rst

@@ -16,6 +16,10 @@ Rules
 
   ``['statements' => ['return']]``
 
+- `braces_position <./../rules/basic/braces_position.rst>`_ with config:
+
+  ``['allow_single_line_anonymous_functions' => true, 'allow_single_line_empty_anonymous_classes' => true]``
+
 - `cast_spaces <./../rules/cast_notation/cast_spaces.rst>`_
 - `class_attributes_separation <./../rules/class_notation/class_attributes_separation.rst>`_ with config:
 
@@ -28,10 +32,6 @@ Rules
 - `class_reference_name_casing <./../rules/casing/class_reference_name_casing.rst>`_
 - `clean_namespace <./../rules/namespace_notation/clean_namespace.rst>`_
 - `concat_space <./../rules/operator/concat_space.rst>`_
-- `curly_braces_position <./../rules/basic/curly_braces_position.rst>`_ with config:
-
-  ``['allow_single_line_anonymous_functions' => true, 'allow_single_line_empty_anonymous_classes' => true]``
-
 - `declare_parentheses <./../rules/language_construct/declare_parentheses.rst>`_
 - `echo_tag_syntax <./../rules/php_tag/echo_tag_syntax.rst>`_
 - `empty_loop_body <./../rules/control_structure/empty_loop_body.rst>`_ with config:

+ 256 - 0
doc/rules/basic/braces_position.rst

@@ -0,0 +1,256 @@
+========================
+Rule ``braces_position``
+========================
+
+Braces must be placed as configured.
+
+Configuration
+-------------
+
+``control_structures_opening_brace``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The position of the opening brace of control structures‘ body.
+
+Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+
+Default value: ``'same_line'``
+
+``functions_opening_brace``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The position of the opening brace of functions‘ body.
+
+Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+
+Default value: ``'next_line_unless_newline_at_signature_end'``
+
+``anonymous_functions_opening_brace``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The position of the opening brace of anonymous functions‘ body.
+
+Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+
+Default value: ``'same_line'``
+
+``classes_opening_brace``
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The position of the opening brace of classes‘ body.
+
+Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+
+Default value: ``'next_line_unless_newline_at_signature_end'``
+
+``anonymous_classes_opening_brace``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The position of the opening brace of anonymous classes‘ body.
+
+Allowed values: ``'next_line_unless_newline_at_signature_end'`` and ``'same_line'``
+
+Default value: ``'same_line'``
+
+``allow_single_line_empty_anonymous_classes``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Allow anonymous classes to have opening and closing braces on the same line.
+
+Allowed types: ``bool``
+
+Default value: ``true``
+
+``allow_single_line_anonymous_functions``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Allow anonymous functions to have opening and closing braces on the same line.
+
+Allowed types: ``bool``
+
+Default value: ``true``
+
+Examples
+--------
+
+Example #1
+~~~~~~~~~~
+
+*Default* configuration.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+   -class Foo {
+   +class Foo
+   +{
+    }
+
+   -function foo() {
+   +function foo()
+   +{
+    }
+
+   -$foo = function()
+   -{
+   +$foo = function() {
+    };
+
+   -if (foo())
+   -{
+   +if (foo()) {
+        bar();
+    }
+
+   -$foo = new class
+   -{
+   +$foo = new class {
+    };
+
+Example #2
+~~~~~~~~~~
+
+With configuration: ``['control_structures_opening_brace' => 'next_line_unless_newline_at_signature_end']``.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+   -if (foo()) {
+   +if (foo())
+   +{
+        bar();
+    }
+
+Example #3
+~~~~~~~~~~
+
+With configuration: ``['functions_opening_brace' => 'same_line']``.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+   -function foo()
+   -{
+   +function foo() {
+    }
+
+Example #4
+~~~~~~~~~~
+
+With configuration: ``['anonymous_functions_opening_brace' => 'next_line_unless_newline_at_signature_end']``.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+   -$foo = function () {
+   +$foo = function ()
+   +{
+    };
+
+Example #5
+~~~~~~~~~~
+
+With configuration: ``['classes_opening_brace' => 'same_line']``.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+   -class Foo
+   -{
+   +class Foo {
+    }
+
+Example #6
+~~~~~~~~~~
+
+With configuration: ``['anonymous_classes_opening_brace' => 'next_line_unless_newline_at_signature_end']``.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+   -$foo = new class {
+   +$foo = new class
+   +{
+    };
+
+Example #7
+~~~~~~~~~~
+
+With configuration: ``['allow_single_line_empty_anonymous_classes' => true]``.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+    $foo = new class { };
+   -$bar = new class { private $baz; };
+   +$bar = new class {
+   +private $baz;
+   +};
+
+Example #8
+~~~~~~~~~~
+
+With configuration: ``['allow_single_line_anonymous_functions' => true]``.
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+    $foo = function () { return true; };
+   -$bar = function () { $result = true;
+   -    return $result; };
+   +$bar = function () {
+   +$result = true;
+   +    return $result;
+   +};
+
+Rule sets
+---------
+
+The rule is part of the following rule sets:
+
+- `@PER <./../../ruleSets/PER.rst>`_ with config:
+
+  ``['allow_single_line_empty_anonymous_classes' => true]``
+
+- `@PER-CS <./../../ruleSets/PER-CS.rst>`_ with config:
+
+  ``['allow_single_line_empty_anonymous_classes' => true]``
+
+- `@PER-CS1.0 <./../../ruleSets/PER-CS1.0.rst>`_ with config:
+
+  ``['allow_single_line_empty_anonymous_classes' => true]``
+
+- `@PER-CS2.0 <./../../ruleSets/PER-CS2.0.rst>`_ with config:
+
+  ``['allow_single_line_empty_anonymous_classes' => true]``
+
+- `@PSR2 <./../../ruleSets/PSR2.rst>`_
+- `@PSR12 <./../../ruleSets/PSR12.rst>`_ with config:
+
+  ``['allow_single_line_empty_anonymous_classes' => true]``
+
+- `@PhpCsFixer <./../../ruleSets/PhpCsFixer.rst>`_ with config:
+
+  ``['allow_single_line_anonymous_functions' => true, 'allow_single_line_empty_anonymous_classes' => true]``
+
+- `@Symfony <./../../ruleSets/Symfony.rst>`_ with config:
+
+  ``['allow_single_line_anonymous_functions' => true, 'allow_single_line_empty_anonymous_classes' => true]``
+
+

+ 8 - 36
doc/rules/basic/curly_braces_position.rst

@@ -4,6 +4,14 @@ Rule ``curly_braces_position``
 
 Curly braces must be placed as configured.
 
+Warning
+-------
+
+This rule is deprecated and will be removed in the next major version
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You should use ``braces_position`` instead.
+
 Configuration
 -------------
 
@@ -218,39 +226,3 @@ With configuration: ``['allow_single_line_anonymous_functions' => true]``.
    +$result = true;
    +    return $result;
    +};
-
-Rule sets
----------
-
-The rule is part of the following rule sets:
-
-- `@PER <./../../ruleSets/PER.rst>`_ with config:
-
-  ``['allow_single_line_empty_anonymous_classes' => true]``
-
-- `@PER-CS <./../../ruleSets/PER-CS.rst>`_ with config:
-
-  ``['allow_single_line_empty_anonymous_classes' => true]``
-
-- `@PER-CS1.0 <./../../ruleSets/PER-CS1.0.rst>`_ with config:
-
-  ``['allow_single_line_empty_anonymous_classes' => true]``
-
-- `@PER-CS2.0 <./../../ruleSets/PER-CS2.0.rst>`_ with config:
-
-  ``['allow_single_line_empty_anonymous_classes' => true]``
-
-- `@PSR2 <./../../ruleSets/PSR2.rst>`_
-- `@PSR12 <./../../ruleSets/PSR12.rst>`_ with config:
-
-  ``['allow_single_line_empty_anonymous_classes' => true]``
-
-- `@PhpCsFixer <./../../ruleSets/PhpCsFixer.rst>`_ with config:
-
-  ``['allow_single_line_anonymous_functions' => true, 'allow_single_line_empty_anonymous_classes' => true]``
-
-- `@Symfony <./../../ruleSets/Symfony.rst>`_ with config:
-
-  ``['allow_single_line_anonymous_functions' => true, 'allow_single_line_empty_anonymous_classes' => true]``
-
-

+ 4 - 1
doc/rules/index.rst

@@ -83,7 +83,10 @@ Basic
 - `braces <./basic/braces.rst>`_ *(deprecated)*
 
   The body of each structure MUST be enclosed by braces. Braces should be properly placed. Body of braces should be properly indented.
-- `curly_braces_position <./basic/curly_braces_position.rst>`_
+- `braces_position <./basic/braces_position.rst>`_
+
+  Braces must be placed as configured.
+- `curly_braces_position <./basic/curly_braces_position.rst>`_ *(deprecated)*
 
   Curly braces must be placed as configured.
 - `encoding <./basic/encoding.rst>`_

+ 410 - 0
src/Fixer/Basic/BracesPositionFixer.php

@@ -0,0 +1,410 @@
+<?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\Basic;
+
+use PhpCsFixer\AbstractFixer;
+use PhpCsFixer\Fixer\ConfigurableFixerInterface;
+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 BracesPositionFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
+{
+    use Indentation;
+
+    /**
+     * @internal
+     */
+    public const NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END = 'next_line_unless_newline_at_signature_end';
+
+    /**
+     * @internal
+     */
+    public const SAME_LINE = 'same_line';
+
+    public function getDefinition(): FixerDefinitionInterface
+    {
+        return new FixerDefinition(
+            'Braces must be placed as configured.',
+            [
+                new CodeSample(
+                    '<?php
+class Foo {
+}
+
+function foo() {
+}
+
+$foo = function()
+{
+};
+
+if (foo())
+{
+    bar();
+}
+
+$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]
+                ),
+            ]
+        );
+    }
+
+    public function isCandidate(Tokens $tokens): bool
+    {
+        return $tokens->isTokenKindFound('{');
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * Must run before SingleLineEmptyBodyFixer, StatementIndentationFixer.
+     * Must run after ControlStructureBracesFixer, NoMultipleStatementsPerLineFixer.
+     */
+    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(),
+        ]);
+    }
+
+    private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
+    {
+        $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);
+    }
+
+    private function isFollowedByNewLine(Tokens $tokens, int $index): bool
+    {
+        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;
+    }
+
+    private function hasCommentOnSameLine(Tokens $tokens, int $index): bool
+    {
+        $token = $tokens[$index + 1];
+
+        if ($token->isWhitespace() && !Preg::match('/\R/', $token->getContent())) {
+            $token = $tokens[$index + 2];
+        }
+
+        return $token->isComment();
+    }
+}

+ 35 - 345
src/Fixer/Basic/CurlyBracesPositionFixer.php

@@ -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();
     }
 }

Some files were not shown because too many files changed in this diff