Browse Source

OrderedClassElementsFixer - added sortAlgorithm option

Martin Lukeš 7 years ago
parent
commit
e7d701c6b2

+ 2 - 0
README.rst

@@ -1033,6 +1033,8 @@ Choose from the list of available rules:
     'constant_private', 'property_public', 'property_protected',
     'property_private', 'construct', 'destruct', 'magic', 'phpunit',
     'method_public', 'method_protected', 'method_private']``
+  - ``sortAlgorithm`` (``'alpha'``, ``'none'``): how multiple occurrences of same type
+    statements should be sorted; defaults to ``'none'``
 
 * **ordered_imports**
 

+ 50 - 3
src/Fixer/ClassNotation/OrderedClassElementsFixer.php

@@ -28,6 +28,11 @@ use PhpCsFixer\Tokenizer\Tokens;
  */
 final class OrderedClassElementsFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface
 {
+    /** @internal */
+    const SORT_ALPHA = 'alpha';
+    /** @internal */
+    const SORT_NONE = 'none';
+
     /**
      * @var array Array containing all class element base types (keys) and their parent types (values)
      */
@@ -68,6 +73,16 @@ final class OrderedClassElementsFixer extends AbstractFixer implements Configura
         'phpunit' => null,
     ];
 
+    /**
+     * Array of supported sort algorithms in configuration.
+     *
+     * @var string[]
+     */
+    private $supportedSortAlgorithms = [
+        self::SORT_NONE,
+        self::SORT_ALPHA,
+    ];
+
     /**
      * @var array Resolved configuration array (type => position)
      */
@@ -177,6 +192,18 @@ class Example
 ',
                     ['order' => ['method_private', 'method_public']]
                 ),
+                new CodeSample(
+                    '<?php
+class Example
+{
+    public function D(){}
+    public function B(){}
+    public function A(){}
+    public function C(){}
+}
+',
+                    ['order' => ['method_public'], 'sortAlgorithm' => 'alpha']
+                ),
             ]
         );
     }
@@ -247,6 +274,10 @@ class Example
                     'method_private',
                 ])
                 ->getOption(),
+            (new FixerOptionBuilder('sortAlgorithm', 'How multiple occurrences of same type statements should be sorted'))
+                ->setAllowedValues($this->supportedSortAlgorithms)
+                ->setDefault(self::SORT_NONE)
+                ->getOption(),
         ]);
     }
 
@@ -302,6 +333,12 @@ class Example
                     $element['type'] = $type;
                 }
 
+                if ('property' === $element['type']) {
+                    $element['name'] = $tokens[$i]->getContent();
+                } elseif (in_array($element['type'], ['use_trait', 'constant', 'method', 'magic'], true)) {
+                    $element['name'] = $tokens[$tokens->getNextMeaningfulToken($i)]->getContent();
+                }
+
                 $element['end'] = $this->findElementEnd($tokens, $i);
 
                 break;
@@ -424,10 +461,9 @@ class Example
         }
         unset($element);
 
-        usort($elements, static function (array $a, array $b) {
+        usort($elements, function (array $a, array $b) {
             if ($a['position'] === $b['position']) {
-                // same group, preserve current order
-                return $a['start'] > $b['start'] ? 1 : -1;
+                return $this->sortGroupElements($a, $b);
             }
 
             return $a['position'] > $b['position'] ? 1 : -1;
@@ -436,6 +472,17 @@ class Example
         return $elements;
     }
 
+    private function sortGroupElements(array $a, array $b)
+    {
+        $selectedSortAlgorithm = $this->configuration['sortAlgorithm'];
+
+        if (self::SORT_ALPHA === $selectedSortAlgorithm) {
+            return strcasecmp($a['name'], $b['name']);
+        }
+
+        return $a['start'] > $b['start'] ? 1 : -1;
+    }
+
     /**
      * @param Tokens  $tokens
      * @param int     $startIndex

+ 197 - 2
tests/Fixer/ClassNotation/OrderedClassElementsFixerTest.php

@@ -332,12 +332,14 @@ EOT
     /**
      * @param string      $expected
      * @param null|string $input
+     * @param array       $configuration
      *
      * @dataProvider provideFix71Cases
      * @requires PHP 7.1
      */
-    public function testFix71($expected, $input = null)
+    public function testFix71(array $configuration, $expected, $input = null)
     {
+        $this->fixer->configure($configuration);
         $this->doTest($expected, $input);
     }
 
@@ -345,13 +347,14 @@ EOT
     {
         return [
             [
+                [],
                 <<<'EOT'
 <?php
 
 class Foo
 {
-    public const C1 = 1;
     const C2 = 2;
+    public const C1 = 1;
     public const C3 = 3;
     protected const C4 = 4;
     private const C5 = 5;
@@ -363,10 +366,37 @@ EOT
 class Foo
 {
     private const C5 = 5;
+    const C2 = 2;
     public const C1 = 1;
     protected const C4 = 4;
+    public const C3 = 3;
+}
+EOT
+            ],
+            [
+                ['sortAlgorithm' => 'alpha'],
+                <<<'EOT'
+<?php
+
+class Foo
+{
+    public const C1 = 1;
     const C2 = 2;
     public const C3 = 3;
+    protected const C4 = 4;
+    private const C5 = 5;
+}
+EOT
+                , <<<'EOT'
+<?php
+
+class Foo
+{
+    private const C5 = 5;
+    const C2 = 2;
+    public const C1 = 1;
+    protected const C4 = 4;
+    public const C3 = 3;
 }
 EOT
             ],
@@ -596,6 +626,171 @@ EOT
         ];
     }
 
+    /**
+     * @param array  $configuration
+     * @param string $input
+     * @param string $expected
+     *
+     * @dataProvider provideSortingConfigurationCases
+     */
+    public function testFixWithSortingAlhorithm(array $configuration, $input, $expected)
+    {
+        $this->fixer->configure($configuration);
+        $this->doTest($expected, $input);
+    }
+
+    public function provideSortingConfigurationCases()
+    {
+        return [
+            [
+                [
+                    'order' => [
+                        'property_public_static',
+                        'method_public',
+                        'method_private',
+                    ],
+                    'sortAlgorithm' => 'alpha',
+                ],
+                <<<'EOT'
+<?php
+class Example
+{
+    public function D(){}
+    public static $pubStatProp2;
+    public function B1(){}
+    public function B2(){}
+    private function E(){}
+    public static $pubStatProp1;
+    public function A(){}
+    public function C(){}
+    public function C1(){}
+}
+EOT
+                ,
+                <<<'EOT'
+<?php
+class Example
+{
+    public static $pubStatProp1;
+    public static $pubStatProp2;
+    public function A(){}
+    public function B1(){}
+    public function B2(){}
+    public function C(){}
+    public function C1(){}
+    public function D(){}
+    private function E(){}
+}
+EOT
+            ],
+            [
+                [
+                    'order' => [
+                        'use_trait',
+                        'constant',
+                        'property_public_static',
+                        'property_protected_static',
+                        'property_private_static',
+                        'property_public',
+                        'property_protected',
+                        'property_private',
+                        'construct',
+                        'destruct',
+                        'magic',
+                        'method_public_static',
+                        'method_protected_static',
+                        'method_private_static',
+                        'method_public',
+                        'method_protected',
+                        'method_private',
+                    ],
+                    'sortAlgorithm' => 'alpha',
+                ],
+                <<<'EOT'
+<?php
+class Foo
+{
+    private static function privStatFunc() {}
+    protected static $protStatProp;
+    use BazTrait;
+    public static $pubStatProp2;
+    public $pubProp3;
+    use BarTrait;
+    public function __toString() {}
+    protected function protFunc() {}
+    protected $protProp;
+    function pubFunc2() {}
+    public $pubProp1;
+    public function __destruct() {}
+    var $pubProp2;
+    public function __magicB() {}
+    const C2 = 2;
+    public static $pubStatProp1;
+    public function __magicA() {}
+    private static $privStatProp;
+    static function pubStatFunc2() {}
+    public function pubFunc3(int $b, int $c) {
+        $a = $b*$c;
+
+        return $a % 4;
+    }
+    private $privProp;
+    const C1 = 1;
+    public static function pubStatFunc3() {
+        return $this->privFunc();
+    }
+    public function pubFunc1() {}
+    public static function pubStatFunc1() {}
+    private function privFunc() {}
+    protected function __construct() {}
+    protected static function protStatFunc() {}
+}
+EOT
+                ,
+                <<<'EOT'
+<?php
+class Foo
+{
+    use BarTrait;
+    use BazTrait;
+    const C1 = 1;
+    const C2 = 2;
+    public static $pubStatProp1;
+    public static $pubStatProp2;
+    protected static $protStatProp;
+    private static $privStatProp;
+    public $pubProp1;
+    var $pubProp2;
+    public $pubProp3;
+    protected $protProp;
+    private $privProp;
+    protected function __construct() {}
+    public function __destruct() {}
+    public function __magicA() {}
+    public function __magicB() {}
+    public function __toString() {}
+    public static function pubStatFunc1() {}
+    static function pubStatFunc2() {}
+    public static function pubStatFunc3() {
+        return $this->privFunc();
+    }
+    protected static function protStatFunc() {}
+    private static function privStatFunc() {}
+    public function pubFunc1() {}
+    function pubFunc2() {}
+    public function pubFunc3(int $b, int $c) {
+        $a = $b*$c;
+
+        return $a % 4;
+    }
+    protected function protFunc() {}
+    private function privFunc() {}
+}
+EOT
+            ],
+        ];
+    }
+
     public function testWrongConfig()
     {
         $this->expectException(\PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException::class);