Fixing our Deprecations: Form, Controller & Mailer

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

We are now super close to fixing all the deprecation warnings that block us from going to Symfony 5. Let's check out the current list for the homepage. There are technically 12 deprecations. But remember, we can ignore all the ones from doctrine/persistence because they're not related to Symfony.

Form getExtendedTypes() Deprecation

With that in mind... if you look closely, there are really only two real deprecations left... and they look like the same thing: something about TextareaSizeExtension should implement a static getExtendedTypes() method.

This TextareaSizeExtension class is a "form type extension" that we built in an earlier tutorial. Let's go check it out: src/Form/TypeExtension/TextareaSizeExtension.php:

... lines 1 - 7
use Symfony\Component\Form\FormTypeExtensionInterface;
... lines 9 - 11
class TextareaSizeExtension implements FormTypeExtensionInterface
{
... lines 14 - 37
}

And... PhpStorm is immediately mad at us:

Class must be declared abstract or implement method getExtendedTypes().

This is the error you see when you have a class that implements an interface but is missing one of the methods that the interface requires. But in this case, that's not technically true. Hold command or control and click the interface to jump to that file.

In reality, there is no getExtendedTypes() method on this interface! It has getExtendedType() - that's the old, deprecated method - but no getExtendedTypes(). It's not actually on the interface, it's just described on top of the class in comments.

You're seeing Symfony's deprecation system in action. If Symfony suddenly added this new getExtendedTypes() method to the interface in Symfony 4.4, it would have broken our app when we upgraded. That would violate Symfony's backwards-compatibility promise... which basically says: we will not break your app on a minor upgrade.

Instead Symfony describes that you need this method and warns you to add it via the deprecation system. It will be added to the interface in Symfony 5.0. Our job is to add this new static getExtendedTypes() method that returns iterable.

We got this! At the bottom of our class, add public static function getExtendedTypes() with an iterable return type. Inside, return an array with the same class as the old method:

... lines 1 - 4
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
... lines 6 - 11
class TextareaSizeExtension implements FormTypeExtensionInterface
{
... lines 14 - 33
public static function getExtendedTypes(): iterable
{
return [TextareaType::class];
}
... lines 38 - 42
}

As soon as we do this, the old, getExtendedType() method won't be called anymore:

... lines 1 - 11
class TextareaSizeExtension implements FormTypeExtensionInterface
{
... lines 14 - 33
public function getExtendedType()
{
return TextareaType::class;
}
}

And it will be gone from the interface in Symfony 5.0. But we do need to keep it temporarily... because, again, for backwards compatibility, it does still exist on the interface in Symfony 4.4. If we removed it from our class, PHP would be super angry. I'll add a comment:

not used anymore - remove in 5.0

... lines 1 - 11
class TextareaSizeExtension implements FormTypeExtensionInterface
{
... lines 14 - 38
public function getExtendedType()
{
// not used anymore - remove in 5.0
}
}

Cool! Let's go close the profiler, refresh and open the new deprecations list. And... hey! Ignoring the doctrine/persistence stuff, our homepage is now free of deprecations!

Does that mean our app is ready for Symfony 5? Ah... not so fast: we still need to do a few more things to be sure that no deprecated code is hiding.

Clearing the Cache to Trigger Deprecations

For example, sometimes deprecations hide in the cache-building process. Find your terminal and run:

php bin/console cache:clear

This will force Symfony to rebuild its container, a process which itself can sometimes contain deprecation warnings. Refresh the homepage now: still 10 deprecation warnings but... oh! One of these is different!

CommentAdminController extends Controller that is deprecated, use AbstractController instead.

Controller to AbstractController

Let's go find this: src/Controller/CommentAdminController.php:

... lines 1 - 9
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
... lines 11 - 14
class CommentAdminController extends Controller
{
... lines 17 - 35
}

Very simply: change extends Controller to extends AbstractController:

... lines 1 - 7
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
... lines 9 - 15
class CommentAdminController extends AbstractController
{
... lines 18 - 36
}

I'll also remove the old use statement:

... lines 1 - 10
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
... lines 12 - 38

These two base-classes work almost the same. The only difference is that, once you use AbstractController, you can't use $this->get() or $this->container->get() to fetch services by their id.

Mailer: NamedAddress to Address

Ok! Another deprecation down and the homepage is once again not triggering any deprecated code. Let's surf around and see if we notice any other deprecations... how about the registration page: SymfonyNerd@example.com, any password, agree to the terms and... woh! That's not a deprecation... that's a huge error!

In theory, you should never get an error after a "minor" version upgrade - like Symfony 4.3 to 4.4. But this is coming from Symfony's Mime component, which is part of Mailer. And because Mailer was experimental until Symfony 4.4 there were some breaking changes from 4.3 to 4.4. We saw this one mentioned earlier when we looked at the Mailer CHANGELOG. Basically, NamedAddress is now called Address.

Where do we use NamedAddress? Let's find out! At your terminal, my favorite way to find out is to run:

git grep NamedAddress

It's used in SetFromListener, Mailer and MailerTest. Let's do some updating. I'll start with src/Service/Mailer.php: change the use statement from NamedAddress to Address, then search for NamedAddress and remove the Named part here and in one other place:

... lines 1 - 8
use Symfony\Component\Mime\Address;
... lines 10 - 12
class Mailer
{
... lines 15 - 27
public function sendWelcomeMessage(User $user): TemplatedEmail
{
$email = (new TemplatedEmail())
->to(new Address($user->getEmail(), $user->getFirstName()))
... lines 32 - 41
}
public function sendAuthorWeeklyReportMessage(User $author, array $articles): TemplatedEmail
{
... lines 46 - 51
$email = (new TemplatedEmail())
->to(new Address($author->getEmail(), $author->getFirstName()))
... lines 54 - 64
}
}

Next is EventListener/SetFromListener. Make the same change on top... and below:

... lines 1 - 6
use Symfony\Component\Mime\Address;
... lines 8 - 9
class SetFromListener implements EventSubscriberInterface
{
... lines 12 - 18
public function onMessage(MessageEvent $event)
{
... lines 21 - 25
$email->from(new Address('alienmailcarrier@example.com', 'The Space Bar'));
}
}

The last place is inside of tests/: Service/MailerTest. Let's see: remove Named from the use statement... and then it's used below in 2 places:

... lines 1 - 11
use Symfony\Component\Mime\Address;
... lines 13 - 15
class MailerTest extends KernelTestCase
{
public function testSendWelcomeMessage()
{
... lines 20 - 36
/** @var Address[] $namedAddresses */
... line 38
$this->assertInstanceOf(Address::class, $namedAddresses[0]);
... lines 40 - 41
}
... lines 43 - 63
}

Got it! Let's try the registration page now: refresh and... validation error. Change to a new email, agree to the terms and... got it!

Ok, the deprecations are gone from the homepage and registration page at least. Are we done? How can we be sure?

Next, let's use a few more tricks to really be sure the deprecations are gone.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.3.0",
        "ext-iconv": "*",
        "antishov/doctrine-extensions-bundle": "^1.4", // v1.4.2
        "aws/aws-sdk-php": "^3.87", // 3.110.11
        "doctrine/doctrine-bundle": "^2.0", // 2.0.6
        "doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // 2.1.2
        "doctrine/orm": "^2.5.11", // v2.7.2
        "easycorp/easy-log-handler": "^1.0", // v1.0.9
        "http-interop/http-factory-guzzle": "^1.0", // 1.0.0
        "knplabs/knp-markdown-bundle": "^1.7", // 1.8.1
        "knplabs/knp-paginator-bundle": "^5.0", // v5.0.0
        "knplabs/knp-snappy-bundle": "^1.6", // v1.7.0
        "knplabs/knp-time-bundle": "^1.8", // v1.11.0
        "league/flysystem-aws-s3-v3": "^1.0", // 1.0.23
        "league/flysystem-cached-adapter": "^1.0", // 1.0.9
        "league/html-to-markdown": "^4.8", // 4.8.2
        "liip/imagine-bundle": "^2.1", // 2.3.0
        "nexylan/slack-bundle": "^2.1", // v2.2.1
        "oneup/flysystem-bundle": "^3.0", // 3.3.0
        "php-http/guzzle6-adapter": "^2.0", // v2.0.1
        "sensio/framework-extra-bundle": "^5.1", // v5.5.3
        "symfony/asset": "5.0.*", // v5.0.2
        "symfony/console": "5.0.*", // v5.0.2
        "symfony/dotenv": "5.0.*", // v5.0.2
        "symfony/flex": "^1.0", // v1.6.2
        "symfony/form": "5.0.*", // v5.0.2
        "symfony/framework-bundle": "5.0.*", // v5.0.2
        "symfony/mailer": "5.0.*", // v5.0.2
        "symfony/messenger": "5.0.*", // v5.0.2
        "symfony/monolog-bundle": "^3.5", // v3.5.0
        "symfony/security-bundle": "5.0.*", // v5.0.2
        "symfony/sendgrid-mailer": "5.0.*", // v5.0.2
        "symfony/serializer-pack": "^1.0", // v1.0.2
        "symfony/twig-bundle": "5.0.*", // v5.0.2
        "symfony/twig-pack": "^1.0", // v1.0.0
        "symfony/validator": "5.0.*", // v5.0.2
        "symfony/webpack-encore-bundle": "^1.4", // v1.7.2
        "symfony/yaml": "5.0.*", // v5.0.2
        "twig/cssinliner-extra": "^2.12", // v2.12.0
        "twig/extensions": "^1.5", // v1.5.4
        "twig/inky-extra": "^2.12" // v2.12.0
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.3.0
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/browser-kit": "5.0.*", // v5.0.2
        "symfony/debug-bundle": "5.0.*", // v5.0.2
        "symfony/maker-bundle": "^1.0", // v1.14.3
        "symfony/phpunit-bridge": "5.0.*", // v5.0.2
        "symfony/profiler-pack": "^1.0", // v1.0.4
        "symfony/var-dumper": "5.0.*" // v5.0.2
    }
}