How to Upgrade to Symplify 8 - From Fixers to Rector Rules

Found a typo? Edit me
This post was updated at December 2021 with fresh know-how.
What is new?

Updated Rector/ECS YAML to PHP configuration, as current standard. The method order by interface Rector rule is removed now.


Symplify 8 is going to be released in the 2nd half of May. But as in Symfony, you can get ready for future version today.

In the previous post we upgraded Coding Standard from Sniffs to PHPStan. Today we finish with 2nd half - from Fixers to Rector rules.

When you run ECS with version 7.3+:

vendor/bin/ecs check

You might see such notices right before your code gets checked:

PHP Notice:  Fixer "..." is deprecated. Use "..." instead

Why were These Fixers Dropped?

You'll find answer to this question in previous post. To extend answer specifically for this post: Fixer and Rector do the same job - they change code based on specific recipe.

Would you like to learn more about Rector? Step by step, from zero to hero?

What is the Difference?

Now we know why. Let's look how to deal with that.

What to do With These Deprecations?

So what does it mean? Remove all the rules from ecs.php and let go?

No, all you need to do is switch to Rector rules. It's better working and more reliable since it works with context and not token positions. So at first, you might see new changes in your code.

How to Handle Upgrade in 30 minutes?

There are dozen deprecated fixers in total. Let's take it one by one.

First - if you don't have Rector, install it:

composer require rector/rector --dev

1. No Empty Doc Block

The RemoveEmptyDocBlockFixer rule basically copied behavior of native NoEmptyPhpdocFixer, so just it instead:

 <?php

 // ecs.php

 declare(strict_types=1);

 use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

 return function (ContainerConfigurator $containerConfigurator): void {
     $services = $containerConfigurator->services();
-    $services->set(Symplify\CodingStandard\Fixer\Commenting\RemoveEmptyDocBlockFixer::class);
+    $services->set(PhpCsFixer\Fixer\Phpdoc\NoEmptyPhpdocFixer::class);
 };

2. Preg Delimiter Character

The PregDelimiterFixer was checking consistent preg delimiter, in this case #:

 class SomeClass
 {
     public function run()
     {
-        preg_match('~value~', $value);
+        preg_match('#value#', $value);
     }
 }

Instead of PhpCsFixer\Fixer\ControlStructure\PregDelimiterFixer fixer:

use Rector rule:

use Rector\CodingStyle\Rector\FuncCall\ConsistentPregDelimiterRector;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->set(ConsistentPregDelimiterRector::class)
        // default
        ->arg('delimiter', '#');
};

3. Required Must be followed by Absolute Path

 class SomeClass
 {
     public function run()
     {
-        require 'autoload.php';
+        require __DIR__ . '/autoload.php';
     }
 }

Instead of PhpCsFixer\Fixer\ControlStructure\RequireFollowedByAbsolutePathFixer fixer,

use Rector rule:

use Rector\CodingStyle\Rector\Include_\FollowRequireByDirRector;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->rule(FollowRequireByDirRector::class);
};

4. Match Exception variable name to its Type

 class SomeClass
 {
     public function run()
     {
         try {
             // ...
-        } catch (SomeException $typoException) {
-            $typoException->getMessage();
+        } catch (SomeException $someException) {
+            $someException->getMessage();
         }
     }
 }

Instead of PhpCsFixer\Fixer\Naming\CatchExceptionNameMatchingTypeFixer Fixer:

use Rector rule:

use Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->rule(CatchExceptionNameMatchingTypeRector::class);
};

5. Match Property and Variable to its Type

 class SomeClass
 {
     /**
      * @var EntityManager
      */
-    private $eventManager;
+    private $entityManager;
-    public function __construct(EntityManager $eventManager)
+    public function __construct(EntityManager $entityManager)
     {
-        $this->eventManager = $eventManager;
+        $this->entityManager = $entityManager;
     }
 }

Instead of Symplify\CodingStandard\Fixer\Naming\PropertyNameMatchingTypeFixer Fixer

use Rector rule:

use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->set(RenamePropertyToMatchTypeRector::class);
};

6. Set Default Values to bool and array Type to Prevent Undefined Value

 class SomeClass
 {
     /**
      * @var bool
      */
-    private $isDisabled;
+    private $isDisabled = false;

     /**
      * @var int[]
      */
-    private $values;
+    private $values = [];

     public function isEmpty()
     {
         return $this->values === [];
     }
 }

Instead of:

use Rector rules:

use Rector\CodingStyle\Rector\Class_\AddArrayDefaultToArrayPropertyRector;
use Rector\SOLID\Rector\Property\AddFalseDefaultToBoolPropertyRector;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->rule(AddFalseDefaultToBoolPropertyRector::class);
    $rectorConfig->rule(AddArrayDefaultToArrayPropertyRector::class);
};

7. Use ::class over Strings Names

This feature is here since PHP 5.5, and it's a massive help for static analysis and instant migrations.

 class AnotherClass
 {
 }

 class SomeClass
 {
     public function run()
     {
-        return 'AnotherClass';
+        return \AnotherClass::class;
     }
 }

Instead of Symplify\CodingStandard\Fixer\Php\ClassStringToClassConstantFixer Fixer:

use Rector rule:

use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->rule(StringClassNameToClassConstantRector::class);
};

8. Order Property by Complexity, Private Methods by Use

How do you order your methods? Random?

Be sure to read How to Teach Your Team Private Method Sorting in 3 mins.

Instead of:

use Rector rules:

use Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector;
use Rector\Order\Rector\Class_\OrderPropertyByComplexityRector;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->set(OrderPrivateMethodsByUseRector::class);
    $rectorConfig->set(OrderPropertyByComplexityRector::class);
};

9. Specific Order By Parent Contract

*Update: this rule was removed since Rector 0.9+ as not very useful in everyday coding.

10. Make Classes final, if You Can

This will be the biggest added value, as tokens have no idea if your class is extended by another class.

Rector knows that, so be ready for more solid code after you run it.

Instead of Symplify\CodingStandard\Fixer\Solid\FinalInterfaceFixer Fixer:

use Rector rule:

use Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->rule(FinalizeClassesWithoutChildrenRector::class);
};


And that's it. Now you're ready!


Happy coding!


Have you find this post useful? Do you want more?

Follow me on Twitter, RSS or support me on GitHub Sponsors.