Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Autowiring Deprecations

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $6.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

On the web debug toolbar, I've got a little yellow icon that says 10 deprecation warnings. Rude! Let's click that!

These are all the ways that my code is using old, deprecated, uncool functionality. It's basically a list of stuff we need to update before upgrading to Symfony 4. And there are a few deprecations related to autowiring:

Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should rename or alias security.user_password_encoder.generic to ... long class name... UserPasswordEncoder instead.

Tip

The text in the deprecation may look slightly different for you: we updated it to be a bit more clear in Symfony 3.3.1.

Um... what?????

This is saying that somewhere, we are type-hinting an argument with Symfony\Component\Security\Core\Encoder\UserPasswordEncoder... but there is no service in the container with that exact id. So, autowiring got busy: it looked at every service and found exactly one - security.user_password_encoder.generic - that has this class. It passed this service to that argument.

And that is the part of autowiring that is deprecated. Looking across every service for a matching class or interface was a little more magic than we wanted in Symfony.

How do we fix this? Actually, there are two solutions!

Solution 1) Fixing the Type-Hint

Here's the first question: is there a different type-hint that we should be using instead for this service? Let's find out!

Head to your terminal. We already know about the debug:container command:

php bin/console debug:container

This gives us a big list of every public service in the container. The blue text is the id of each service. But guess what? Service id's are much less important in Symfony 3.3... because we almost always rely on type-hints and autowiring.

Re-run the command again with a new --types option:

php bin/console debug:container --types

Voilà! This is a list of all valid type-hints that you can use for autowiring. This is awesome. If you search for "encoder", you'll find one called UserPasswordEncoderInterface. This is the type-hint we should use! Symfony ships with this alias to enable autowiring.

Cool! Let's find out where we're using this:

git grep UserPasswordEncoder

Two places: HashPasswordListener and LoginFormAuthenticator. Open up HashPasswordListener. Then, add Interface to the end of the use statement, and also the type-hint:

... lines 1 - 7
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class HashPasswordListener implements EventSubscriber
{
... lines 12 - 13
public function __construct(UserPasswordEncoderInterface $passwordEncoder)
{
... line 16
}
... lines 18 - 63
}

That's it.

Open up LoginFormAuthenticator and do the exact same thing: update the use... and the argument:

... lines 1 - 10
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
... lines 12 - 15
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 18 - 22
public function __construct(FormFactoryInterface $formFactory, EntityManager $em, RouterInterface $router, UserPasswordEncoderInterface $passwordEncoder)
{
... lines 25 - 28
}
... lines 30 - 78
}

Ok, go back to the browser! Refresh, and watch those 10 deprecations. Bam! 8 deprecations!

If you check the list now, we still have one more autowiring deprecation. This time, apparently, it's unhappy about an EntityManager type-hint.

Same question as before: is there a better type-hint to use? Let's find out:

php bin/console debug:container --types

Search for EntityManager and... boom! There is an EntityManagerInterface alias. This is the officially supported type-hint.

Solution 2: Adding an Alias

Ok, we know the fix: update our EntityManager type hints to EntityManagerInterface! But... there's another solution! If you want, it is totally ok to type-hint EntityManager. To make this work with autowiring, we can create an alias.

Copy the Doctrine\ORM\EntityManager class name. Then, find your editor and open up services.yml. Add the alias: Doctrine\ORM\EntityManager aliased to @, and then copy the target service id: @doctrine.orm.default_entity_manager:

... lines 1 - 5
services:
... lines 7 - 26
# alias to allow this type to be autowired
... line 28
Doctrine\ORM\EntityManager: '@doctrine.orm.default_entity_manager'
... lines 30 - 45

We have full control over autowiring. With aliases, we can configure exactly which service we want to use for each type-hint. No magic.

Ok, refresh the page one more time! Got it! 8 deprecations now down to 7. The rest of the deprecations are related to other parts of our code. I'll leave those as homework.

Leave a comment!

4
Login or Register to join the conversation
Trafficmanagertech Avatar
Trafficmanagertech Avatar Trafficmanagertech | posted 5 years ago

How do I know which one to use in this case? (it was the Symfony\Bundle\FrameworkBundle\Routing\Router )
Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for argument "$router" of method "AppBundle\Service\Postback::__construct()" to one of its parents: interface "Symfony\Component\Routing\Matcher\UrlMatcherInterface", interface "Symfony\Component\Routing\Generator\UrlGeneratorInterface", or interface "Symfony\Component\Routing\RouterInterface".

Reply

Hey Trafficmanagertech!

Ah, great question! Can I give you 2 different answers? :)

1) They are ALL correct. All 3 interfaces will cause the correct service to be injected. So, choose your favorite!
2) (the slightly more complete answer) Whatever you choose, you just need to make sure that the method you want to call on the route (e.g. generate) is on that interface. For example, UrlMatcherInterface does *not* have a generate() method on it. Actually, your code will still work just fine (because Symfony will pass your the router service, which *does* have this method), but it will look weird to call a method on an interface that does not have that method :). So, assuming you are calling generate(), that method exists on both UrlGeneratorInterface and RouterInterface. Actually, RouterInterface simply extends both UrlGeneratorInterface and UrlMatcherInterface :p. So, you can use either RouterInterface or UrlGeneratorInterface - the second one is more "hipster" because it's a smaller, more-specific interface, but I often use RouterInterface because it's more clear (we understand what a "router" is, but a "url generator" sounds more cryptic.

Phew! Sorry for the long explanation, but I hope it helps!

Cheers!

2 Reply
Trafficmanagertech Avatar
Trafficmanagertech Avatar Trafficmanagertech | weaverryan | posted 5 years ago

Thank you very much :)

Reply
Mike P. Avatar
Mike P. Avatar Mike P. | posted 5 years ago

"I leave those as homework", such a great way of explaining things! Keep on going Ryan! :)

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.3.0-RC1", // v3.3.0-RC1
        "doctrine/orm": "^2.5", // 2.7.5
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.7
        "symfony/monolog-bundle": "^3.1", // v3.2.0
        "symfony/polyfill-apcu": "^1.0", // v1.23.0
        "sensio/distribution-bundle": "^5.0", // v5.0.25
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.29
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.4
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "knplabs/knp-markdown-bundle": "^1.4", // 1.7.1
        "doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
        "stof/doctrine-extensions-bundle": "^1.2" // v1.3.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.7
        "symfony/phpunit-bridge": "^3.0", // v3.4.47
        "nelmio/alice": "^2.1", // v2.3.6
        "doctrine/doctrine-fixtures-bundle": "^2.3" // v2.4.1
    }
}
userVoice