If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeOn 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!
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.
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.
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!
// 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
}
}
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".