How to Upgrade to Symplify 8 - From Sniffs to PHPStan Rules

This post was updated at August 2020

Updated ECS YAML to PHP configuration since ECS 8.


Since Symplify 7.3, you might notice a few deprecation notices in your coding standards. As Symplify 8 release is synced with Symfony cycle, both will be released at the end of May.

What to do with these deprecations? Why were these sniffs dropped? How to handle upgrades in 1 hour?

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:  Sniff "..." is deprecated. Use "..." instead

Why were These Sniffs Dropped?

Symplify 7 used lots of sniffs based on coding standard tokens. In time they were made, they were as good. Tokens are best at spaces, abstract syntax tree is best at logical code structure.

E.g. TraitNameSniff check name of traits and makes sure the name ends with "Trait":

<?php

// hey, this should be "ProductTrait"
trait Product
{
}

Do you see any spaces or token positions? No, it's just:

So writing this rule in tokens is the wrong tool chosen. Why? With tokens, you need to make sure:

But why waste time on re-inventing the wheel, when we can use a tool that handles tedious work for us? It's better to use abstract syntax tree technology, in this case PHPStan.


That's why all the "AST" sniffs were migrated to PHPStan rules.

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 PHPStan rules. It's better working and more reliable since it works with context and not token positions. So at first, you might discover a few new reported errors here and there.

How to Handle Upgrade in 1 hour?

There are 14 deprecated sniffs in total. Don't worry, while it might seem like a considerable number, the replacement is a matter of an hour.

Have you used a full Symplify set?


// ecs.php

declare(strict_types=1);

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\EasyCodingStandard\ValueObject\Option;use Symplify\EasyCodingStandard\ValueObject\Set\SetList;

return static function (ContainerConfigurator $containerConfigurator): void {
    $parameters = $containerConfigurator->parameters();
    $parameters->set(Option::SETS, [SetList::SYMPLIFY]);
};

Then you won't see any deprecations, because rules were removed from the config for you. Still, I recommend adding these rules to phpstan.neon.

1. Abstract, Trait and Interface Naming Sniffs

These rules were removed:

and replaced by:

composer require --dev slam/phpstan-extensions
# phpstan.neon
rules:
    - SlamPhpStan\ClassNotationRule

2. Cognitive Complexity

The only sniff rule your coding standard should have... just got better.

The orifinal sniffs were removed:

and replaced by more avanced AST in form of PHPStan rules:

# phpstan.neon
includes:
    - vendor/symplify/coding-standard/packages/cognitive-complexity/config/cognitive-complexity-rules.neon

# if you use default value, you can skip this section
parameters:
    symplify:
        max_cognitive_complexity: 10
        max_class_cognitive_complexity: 60

3. Make sure Classes used in @param, @var and @return Exists

Just drop it. PHPStan level 0 handles this.

4. Forbidden Static → Explicit Static

Static functions are best way to slowly create technical dept. But sometimes it's tough to code without them.

So instead of forbidding them:

Just be honest about them:

# phpstan.neon
rules:
    - Symplify\CodingStandard\Rules\NoClassWithStaticMethodWithoutStaticNameRule

The same way you see a "trait" in a file name and know it's a trait, this rule makes sure that classes with static methods have "static" in its name.

I've been using it for 4 days, and oh, what a shame I feel when I want to use a "Static" "service". It makes me think twice both about the design and consequences.

5. Forbidden Parent Class

Even though it's repeated over, again and again, that composition beats inheritance, the PHP code I see is full of it.

Sometimes the only way to promote so-far-the-best practise, is to enforce it.

# phpstan.neon
rules:
    - Symplify\CodingStandard\Rules\ForbiddenParentClassRule

parameters:
    symplify:
        forbidden_parent_classes:
            - 'Doctrine\ORM\EntityRepository'

6. Use Custom Exceptions over Basic Ones

# phpstan.neon
rules:
    - Symplify\CodingStandard\Rules\NoDefaultExceptionRule

7. Prefer One Class over Another

# phpstan.neon
parameters:
    symplify:
        old_to_preffered_classes:
            DateTime: 'Nette\Utils\DateTime'

rules:
    - Symplify\CodingStandard\Rules\PreferredClassRule

8. No Duplicated Short Classes

# phpstan.neon
rules:
    - Symplify\CodingStandard\Rules\NoDuplicatedShortClassNameRule

9. Use explicit Return over & Reference

# phpstan.neon
rules:
    - Symplify\CodingStandard\Rules\NoReferenceRule

10. Don't leave dump() Function in the Code

# phpstan.neon
rules:
    - Symplify\CodingStandard\Rules\NoDebugFuncCallRule

11. Respect Naming of Your Parents

# phpstan.neon
rules:
    - Symplify\CodingStandard\Rules\ClassNameRespectsParentSuffixRule

parameters:
    symplify:
        parent_classes:
            - Rector

And that's it! You've just migrated all deprecated sniffs to PHPStan rules. Upgrade to Symplify 8 will be a piece of cake for you.


In the next post, we'll look on the 2nd half - how to migrate fixers into Rector rules.


Happy coding!