Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Hunting Down the Final 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 $10.00

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

Login Subscribe

All right team! Let's fix these last few deprecations. One of the trickiest things about these is that, sometimes, they come from third-party bundles. I don't have any examples here, but sometimes you'll get a deprecation and... if you look into it, you'll realize it's not your fault. It's coming from a library or a bundle you're using. When this happens, you need to upgrade that bundle, and hope there's a new version without any deprecations. We actually did have some examples of this way back at the beginning of the tutorial. But... we've already run composer update a few times, and have, apparently, upgraded all of our dependencies to versions without deprecations. Yay, efficiency!


Ok, let's take a look at this list. It says that, in Symfony 5.1, ROLE_PREVIOUS_ADMIN is deprecated and we should use IS_IMPERSONATOR instead. You can show the context or trace to try to get more info, like where this is coming from. It isn't always obvious... and that's one of the trickiest things about deprecations. But this one is coming from base.html.twig.

Great! Open templates/base.html.twig and search for "previous_admin". In an earlier tutorial, we used this to check if we are currently impersonating a user with Symfony's switch_user feature. If we are, we changed the background to red to make it really obvious.

To fix the deprecation, very simply, change this to IS_IMPERSONATOR. Copy that... because there's one other spot on this page where we need to do the same thing: IS_IMPERSONATOR. Done! One less deprecation!

... lines 1 - 21
... line 23
{{ is_granted('IS_IMPERSONATOR') ? 'style="background-color: red !important"' }}
... lines 26 - 60
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="user-dropdown">
{% if is_granted('IS_IMPERSONATOR') %}
... lines 63 - 67
{% endif %}
... lines 69 - 71
... lines 73 - 79
... lines 81 - 95


While we're talking security, open up config/packages/security.yaml and head down to access_control. I have a few entries - /logout, /admin/login - that I want to make absolutely sure are accessible by everyone, even users that are not logged in. To do, we added these rules on top and, previously used IS_AUTHENTICATED_ANONYMOUSLY. So if I go to /logout, only this access_control is matched... and since the role is IS_AUTHENTICATED_ANONYMOUSLY access is always granted.

In Symfony 6, IS_AUTHENTICATED_ANONYMOUSLY has changed to PUBLIC_ACCESS. So use that in both places.

... lines 2 - 38
... lines 40 - 41
- { path: ^/logout, role: PUBLIC_ACCESS }
- { path: ^/admin/login, roles: PUBLIC_ACCESS }
... lines 44 - 59

If you're wondering why we didn't have a deprecation for this... well... it's a rare case where Symfony is unable to catch that deprecated path and show it to us. This doesn't happen very often, but it's a situation where a tool like SymfonyInsight can help catch this.... even when Symfony itself can't.

The Deprecated Session Service

Okay, the last deprecation on the list says:

SessionInterface aliases are deprecated, use $requestStack->getSession() instead. It's being referenced by the LoginFormAuthenticator service.

Let's go check that out! Open src/Security/LoginFormAuthenticator.php. Ahh. I'm autowiring the SessionInterface service. In Symfony 6, that service no longer exists. There are some technical reasons for this... but long story short, the session wasn't ever, really a true service. What you're supposed to do now is get it from the Request.

So, no big deal. Remove the SessionInterface constructor argument... and we don't need this use statement anymore either.

... lines 1 - 21
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
... lines 24 - 27
public function __construct(private EntityManagerInterface $entityManager, private UrlGeneratorInterface $urlGenerator)
... lines 31 - 84

Now search for "session". We're using it down in onAuthenticationSuccess(). Fortunately, this already passes us the$request object! So we can just say $request->getSession().

... lines 1 - 60
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): Response
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
... line 64
... lines 66 - 67
... lines 69 - 86

Hunting Down the Final Deprecations

Done! So... did we do it? Have we achieved zero deprecations and spiritual enlightenment? Go back to the homepage, refresh and... we did! Well, at least that first part... no deprecations! And if we surf around our site a bit... I'm not seeing any deprecations on any of these pages!

Does this mean we're done? Well, we've manually tested all of the pages that we can click on. But what about POST requests... like submitting the login or registration forms? And what about API endpoints? We have one called /api/me... which doesn't work because I'm not logged in. Log back in as "abraca_admin@example.com" with password "tada" and then... yea, /api/me works.

We can't see the web debug toolbar for this request, but I bet you already know the trick. Go to /_profiler to see the last ten requests. Here's the POST request to /login. Go down to Logs. Great! That had no deprecations. Go back and also check the API endpoint. If we look at Logs again, it also had no deprecations. We're on a roll!

Another option, instead of checking the profiler all the time, is to go over to your terminal and tail the log file:

tail -f var/log/dev.log

This will constantly stream any new logs. Actually, hit "ctrl" + "C" and run that again, but grep for deprecation:

tail -f var/log/dev.log | grep deprecation

Perfect. Now, if any logs come through that contain the word "deprecation", we'll see them. And since deprecated code paths trigger a log in the dev environment, this is a powerful tool.

Deprecated $this->getDoctrine() Method

For example, let's go register as a new user. I'll log out, then "Sign up". It asks me for my name, email, and a password. Click to "Agree" to some made-up terms and submit. Oh, my password is too short: my own validation rules coming back to haunt me! Fix that, hit "Register" again and... it works!

But if we go back to our terminal... rut roo!

Since symfony/framework-bundle 5.4, method AbstractController::getDoctrine() is deprecated. Inject an instance of ManagerRegistry in your controller instead.

It's not easy to see where this is coming from in our code, but we did just register... so let's open up RegistrationController. Ah, it's complaining about this right here: the getDoctrine() method is deprecated.

Instead of using this, we can inject the $entityManager. At the end of the argument list, autowire EntityManagerInterface $entityManager. And... then down here, delete this line because $entityManager is now being injected. Another deprecation gone!

... lines 1 - 7
use Doctrine\ORM\EntityManagerInterface;
... lines 9 - 16
class RegistrationController extends AbstractController
... line 19
public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher, VerifyEmailHelperInterface $verifyEmailHelper, EntityManagerInterface $entityManager): Response
... lines 22 - 56
... lines 58 - 88

Logging Deprecations on Production

Are we done now? Probably. Our project is pretty small, so checking all the pages manually isn't that big of a deal. But for bigger projects, it might be... a huge deal to check everything manually! And you really want to be sure that you didn't miss anything before you upgrade.

One great option to make sure you didn't miss anything is to log your deprecations on production. Open config/packages/monolog.yaml and go down to when@prod. This has a number of handlers that will log all errors to php://stderr. There's also a deprecation section. With this config, Symfony will log any deprecation messages (that's what this channels: [deprecation] means) to php://stderr.

... lines 1 - 40
... lines 44 - 58
type: stream
channels: [deprecation]
path: php://stderr

This means that you can deploy, wait for an hour, day or week, then... just check the log! If you want to log to a file instead, change the path to something like %kernel.logs_dir%/deprecations.log.

So that's my favorite thing to do: deploy it, and then see - in the real world - whether or not anyone is still hitting deprecated code paths.

At this point, I'm not seeing any more deprecations on our web debug toolbar, so I think we're done! And that means we're ready for Symfony 6! Let's do the upgrade next!

Leave a comment!

Login or Register to join the conversation
Dan_M Avatar

Hey Guys!

I worked through the code to this point, and I have 14 deprecations coming from two sources. KNP Markdown Bundle has four methods missing return definitions. Given that KNP Markdown Bundle has been abandoned, are these deprecations the trigger to replace KNP Markdown Bundle with twig/markdown-extra (or can I upgrade to Symfony 6 without removing those deprecations)?

The second source is BabDev\PagerfantaBundle. That module is at ^2.0 in composer.json. When I upgraded to version 3, Symfony blew up with a failed to open stream error. Is there any direction on upgrading Pagerfanta?


Dan_M Avatar

Ok, I just moved on to Chapter 16, and my questions were answered. Life is good.

Dan_M Avatar

Hmmmm...I just restarted my development system, and the KNP Markdown Bundle deprecations are no longer showing up in the debug toolbar. I don't know what that means.


Hey Dan_M!

Haha, yup I'm glad you found the answer! About the deprecations no longer showing up, that can happen sometimes - I think it deals with whether or not some of the cache needed to be built during that request or not. In short, you will see some variations between the deprecations count from time to time, even on the same page (that's why, once you think all the deprecations are gone, deploying to production with deprecations logging enabled is a great way to make sure you haven't missed anything).


MattWelander Avatar
MattWelander Avatar MattWelander | posted 1 year ago | edited

Hi, I also wanted to ask - you mentioned in another video that one can create a base class to extend all other controllers from, rather than extending the AbstractController every time.

Would it be possible to set up my own "template" sort of, with all the use statements that are common to all of my controllers, and also initialize the variables in the construct, so I don't need to clutter my every controller with that?

So I'm thinking something like this

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Security;

class WebtoolsController extends AbstractController

    private $requestStack;
    private $security;

    public function __construct(RequestStack $requestStack, Security $security)
        $this->requestStack = $requestStack;
        $this->security = $security;


And then omit all of this in my every controller, like this:

namespace App\Controller\ExtraOpening;
use App\Controller\WebtoolsController;

 * Price controller.
class priceController extends WebtoolsController
    public function __construct()

I get an error message when I do the parent::__construct();, apparently I'm missing arguments. But if I need to repeat the arguments on every controller it isn't much point in trying to abstract it in this way anyways...

Just thought maybe there is a smart way? =)


Hey Matt,

I think in your case it would be better to use the ServiceSubscriber. You can define a list of services that you always want to have access to. You can read more about it here https://symfony.com/doc/current/service_container/service_subscribers_locators.html

By the way, if a child's constructor does nothing, you don't need a constructor at all. PHP will call the parent's constructor automatically


MattWelander Avatar
MattWelander Avatar MattWelander | posted 1 year ago | edited

Hi, I still get this deprecation:

The "Symfony\Bridge\Doctrine\Logger\DbalLogger" class implements "Doctrine\DBAL\Logging\SQLLogger" that is deprecated Use {@see \Doctrine\DBAL\Logging\Middleware} or implement {@see \Doctrine\DBAL\Driver\Middleware} instead.

From this issue (https://github.com/doctrine/DoctrineBundle/issues/1429) it seems it was fixed in Doctrine 2.6 but I'm running 2.7 and it is still there.

Guess I shouldn't worry too much about it, by the time I'm ready to move up to 6.x the matching doctrine version is probably fixed, but it'd be a good feeling to have a completely deprecation free board =)


Yea, it would feel nicer to get rid of all deprecations, but in this case, I think it would be better to just wait until you upgrade Doctrine


Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.6", // v3.6.1
        "composer/package-versions-deprecated": "^1.11", //
        "doctrine/annotations": "^1.13", // 1.13.2
        "doctrine/dbal": "^3.3", // 3.3.5
        "doctrine/doctrine-bundle": "^2.0", // 2.6.2
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.0", // 2.11.2
        "knplabs/knp-markdown-bundle": "^1.8", // 1.10.0
        "knplabs/knp-time-bundle": "^1.18", // v1.18.0
        "pagerfanta/doctrine-orm-adapter": "^3.6", // v3.6.1
        "pagerfanta/twig": "^3.6", // v3.6.1
        "sensio/framework-extra-bundle": "^6.0", // v6.2.6
        "sentry/sentry-symfony": "^4.0", // 4.2.8
        "stof/doctrine-extensions-bundle": "^1.5", // v1.7.0
        "symfony/asset": "6.0.*", // v6.0.7
        "symfony/console": "6.0.*", // v6.0.7
        "symfony/dotenv": "6.0.*", // v6.0.5
        "symfony/flex": "^2.1", // v2.1.7
        "symfony/form": "6.0.*", // v6.0.7
        "symfony/framework-bundle": "6.0.*", // v6.0.7
        "symfony/mailer": "6.0.*", // v6.0.5
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/property-access": "6.0.*", // v6.0.7
        "symfony/property-info": "6.0.*", // v6.0.7
        "symfony/proxy-manager-bridge": "6.0.*", // v6.0.6
        "symfony/routing": "6.0.*", // v6.0.5
        "symfony/runtime": "6.0.*", // v6.0.7
        "symfony/security-bundle": "6.0.*", // v6.0.5
        "symfony/serializer": "6.0.*", // v6.0.7
        "symfony/stopwatch": "6.0.*", // v6.0.5
        "symfony/twig-bundle": "6.0.*", // v6.0.3
        "symfony/ux-chartjs": "^2.0", // v2.1.0
        "symfony/validator": "6.0.*", // v6.0.7
        "symfony/webpack-encore-bundle": "^1.7", // v1.14.0
        "symfony/yaml": "6.0.*", // v6.0.3
        "symfonycasts/verify-email-bundle": "^1.7", // v1.10.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.8
        "twig/string-extra": "^3.3", // v3.3.5
        "twig/twig": "^2.12|^3.0" // v3.3.10
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.1
        "phpunit/phpunit": "^9.5", // 9.5.20
        "rector/rector": "^0.12.17", // 0.12.20
        "symfony/debug-bundle": "6.0.*", // v6.0.3
        "symfony/maker-bundle": "^1.15", // v1.38.0
        "symfony/var-dumper": "6.0.*", // v6.0.6
        "symfony/web-profiler-bundle": "6.0.*", // v6.0.6
        "zenstruck/foundry": "^1.16" // v1.18.0