$30 off During Our Annual Pro Sale. View Details »

What's New in Symfony 5.3

What's New in Symfony 5.3

Symfony 5.3 is the last minor release before the LTS and the next major, which means it's the last chance to introduce new experimental features and deprecate things to be removed in Symfony 6. If you want to be prepared for the update at the end of the month, join our speakers Marco, Jan & Malte on a grand tour of what they consider to be the biggest changes.

Marco Petersen

May 18, 2021
Tweet

More Decks by Marco Petersen

Other Decks in Programming

Transcript

  1. What’s new in


    Symfony 5.3

    View Slide

  2. Malte Schlüter
    [email protected]
    @malteschlueter
    [email protected]
    @ocrampete16
    Marco Petersen
    [email protected]
    @jschaedl
    Jan Schädlich

    View Slide

  3. Agenda

    View Slide

  4. Agenda
    Deprecations

    View Slide

  5. Agenda
    Deprecations
    Improvements

    View Slide

  6. View Slide

  7. Deprecations
    Symfony 5.3
    from 5.2 to 5.3
    Code will be removed in Symfony 6.0

    View Slide

  8. Rename master request to main request
    HttpKernelInterface::MASTER_REQUEST -> HttpKernelInterface::MAIN_REQUEST


    KernelEvent::isMasterRequest() -> KernelEvent::isMainRequest()


    RequestStack::getMasterRequest() -> RequestStack::getMainRequest()


    HttpFoundation|HttpKernel

    View Slide

  9. Deprecated the SessionInterface alias
    use Symfony\Component\HttpFoundation\Session\SessionInterface;


    final class SessionAwareService


    {


    public function __construct(


    private SessionInterface $session


    ) {


    }


    public function someMethodUsingTheSession(): void


    {


    $foo = $this->session->get('foo');


    }


    }


    FrameworkBundle

    View Slide

  10. Deprecated the SessionInterface alias
    use Symfony\Component\HttpFoundation\RequestStack;


    final class SessionAwareService


    {


    public function __construct(


    private RequestStack $requestStack


    ) {


    }


    public function someMethodUsingTheSession(): void


    {


    $foo = $this->requestStack->getSession()->get('foo');


    }


    }


    final class TestController


    {


    public function __invoke(Request $request): Response


    {


    $foo = $request->getSession()->get('foo');


    }


    }


    FrameworkBundle

    View Slide

  11. Removed dependency on symfony/intl
    composer req symfony/intl


    Form
    LocaleType


    CountryType


    CurrencyType


    LanguageType


    TimezoneType

    View Slide

  12. Deprecated the use of TLS option


    for Redis Bridge
    # .env


    # before


    MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages?tls=true


    # after


    MESSENGER_TRANSPORT_DSN=rediss://localhost:6379/messages


    Messenger

    View Slide

  13. https://github.com/symfony/symfony/blob/master/UPGRADE-5.3.md

    View Slide

  14. Improvements

    View Slide

  15. More PHP 8 Attributes

    View Slide

  16. Autowire arguments in controller actions
    final class Symfony52Controller


    {


    #[Route('/symfony52', name: 'symfony52', methods: ['GET'])]


    public function __invoke(UserRepository $userRepository): Response


    {


    return new Response('Go and get PHP 8!');


    }


    }


    Symfony 5.2
    # services.yaml


    App\Controller\Symfony52Controller:


    tags: ['controller.service_arguments']


    Symfony 5.2

    View Slide

  17. #[AsController]
    #[AsController]


    final class Symfony53Controller


    {


    #[Route('/symfony52', name: 'symfony52', methods: ['GET'])]


    public function __invoke(UserRepository $userRepository): Response


    {


    return new Response('Symfony 💖 PHP 8!');


    }


    }


    Symfony 5.3
    # services.yaml


    App\Controller\Symfony52Controller:


    tags: ['controller.service_arguments']


    Symfony 5.3

    View Slide

  18. Event Listeners/Subscribers
    final class Symfony52Listener implements EventSubscriberInterface


    {


    public static function getSubscribedEvents()


    {


    return [


    CustomEvent::class => [


    ['onCustomEvent', 10],


    ],


    ];


    }


    public function onCustomEvent(CustomEvent $event): void


    {


    $event->messages[] = 'Greetings from Symfony52Listener 👋';


    }


    }


    Symfony 5.2

    View Slide

  19. #[AsEventListener]
    #[AsEventListener(


    event: CustomEvent::class,


    method: 'onCustomEvent',


    priority: 10


    )]


    final class Symfony53Listener


    {


    public function onCustomEvent(CustomEvent $event): void


    {


    $event->messages[] = 'Greetings from Symfony53Listener 👋';


    }


    }


    Symfony 5.3

    View Slide

  20. Commands
    final class Symfony52Command extends Command


    {


    protected static $defaultName = 'app:symfony52';


    protected static $defaultDescription = 'an awesome description';


    protected function configure()


    {


    $this


    ->setAliases(['symfony52'])


    ->setHidden(false);


    }


    protected function execute(InputInterface $input, OutputInterface $output)


    {


    (new SymfonyStyle($input, $output))->text('Go and get PHP 8!');




    return 0;


    }


    }


    Symfony 5.2

    View Slide

  21. #[AsCommand]
    #[AsCommand(


    name: 'app:symfony53',


    description: 'an awesome description',


    aliases: ['symfony53'],


    hidden: false


    )]


    final class Symfony53Command extends Command


    {


    protected function execute(InputInterface $input, OutputInterface $output)


    {


    (new SymfonyStyle($input, $output))->text('Symfony 💖 PHP 8!');


    return 0;


    }


    }


    Symfony 5.3

    View Slide

  22. #[When]
    #[When(env: 'dev')]


    #[AsCommand(name: 'app:when-dev')]


    final class WhenDevCommand extends Command


    {


    protected function execute(InputInterface $input, OutputInterface $output)


    {


    (new SymfonyStyle($input, $output))


    ->text(‘Symfony 💖 PHP 8, but for now only in dev environment!');


    return 0;


    }


    }


    Symfony 5.3

    View Slide

  23. Autocon
    fi
    gure services via Attributes
    services:


    _defaults:


    autoconfigure: true # Automatically registers your services


    # as commands, event subscribers, etc.


    Symfony 5.2

    View Slide

  24. Autocon
    fi
    gure services via Attributes
    services:


    _defaults:


    autoconfigure: true # Automatically registers your services


    # as commands, event subscribers, etc.


    Symfony 5.2
    services:


    _instanceof:


    App\Security\CustomInterface:


    tags: ['app.custom_tag']


    View Slide

  25. Autocon
    fi
    gure services via Attributes
    services:


    _defaults:


    autoconfigure: true # Automatically registers your services


    # as commands, event subscribers, etc.


    Symfony 5.2
    services:


    _instanceof:


    App\Security\CustomInterface:


    tags: ['app.custom_tag']


    // src/Kernel.php


    class Kernel extends BaseKernel


    {


    // ...


    protected function build(ContainerBuilder $container): void


    {


    $container->registerForAutoconfiguration(CustomInterface::class)


    ->addTag('app.custom_tag')


    ;


    }


    }


    View Slide

  26. Autocon
    fi
    gure services via Attributes
    services:


    _defaults:


    autoconfigure: true # Automatically registers your services


    # as commands, event subscribers, etc.


    Symfony 5.2
    services:


    _instanceof:


    App\Security\CustomInterface:


    tags: ['app.custom_tag']


    // src/Kernel.php


    class Kernel extends BaseKernel


    {


    // ...


    protected function build(ContainerBuilder $container): void


    {


    $container->registerForAutoconfiguration(CustomInterface::class)


    ->addTag('app.custom_tag')


    ;


    }


    }


    // src/DependencyInjection/MyBundleExtension.php


    class MyBundleExtension extends Extension


    {


    // ...


    public function load(array $configs, ContainerBuilder $container): void


    {


    $container->registerForAutoconfiguration(CustomInterface::class)


    ->addTag('app.custom_tag')


    ;


    }


    }


    View Slide

  27. #[Autocon
    fi
    gure]
    Symfony 5.3
    use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;


    #[Autoconfigure(tags: ['app.loader'])]


    interface LoaderInterface


    {


    public function supports(string $type): bool;


    public function load();


    }


    View Slide

  28. #[Autocon
    fi
    gure]
    Symfony 5.3
    use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;


    #[Autoconfigure(tags: ['app.loader'])]


    interface LoaderInterface


    {


    public function supports(string $type): bool;


    public function load();


    }


    namespace Symfony\Component\DependencyInjection\Attribute;


    #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]


    class Autoconfigure


    {


    public function __construct(


    public ?array $tags = null,


    public ?array $calls = null,


    public ?array $bind = null,


    public bool|string|null $lazy = null,


    public ?bool $public = null,


    public ?bool $shared = null,


    public ?bool $autowire = null,


    public ?array $properties = null,


    public array|string|null $configurator = null,


    ) {


    }


    }


    View Slide

  29. #[Autocon
    fi
    gureTag]
    Symfony 5.3
    namespace App\Loader;


    use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;


    #[AutoconfigureTag('app.loader')]


    #[AutoconfigureTag()] # tag name => App\Loader\LoaderInterface


    interface LoaderInterface


    {


    public function supports(string $type): bool;


    public function load();


    }


    View Slide

  30. Use tagged services
    use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;


    final class LoaderIterator


    {


    public function __construct(


    #[TaggedIterator('app.loader')]


    private iterable $loaders,


    ) {


    }


    }


    Symfony 5.3

    View Slide

  31. Use tagged services
    use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;


    final class LoaderIterator


    {


    public function __construct(


    #[TaggedIterator('app.loader')]


    private iterable $loaders,


    ) {


    }


    }


    use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;


    #[AsTaggedItem(index: 'yaml_loader', priority: 10)]


    final class YamlLoader implements LoaderInterface


    {


    public function supports(string $type): bool { ... }


    public function load() { ... }


    }


    Symfony 5.3

    View Slide

  32. Use tagged services
    use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;


    final class LoaderIterator


    {


    public function __construct(


    #[TaggedIterator('app.loader')]


    private iterable $loaders,


    ) {


    }


    }


    use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;


    #[AsTaggedItem(index: 'xml_loader', priority: 20)]


    final class XmlLoader implements LoaderInterface


    {


    public function supports(string $type): bool { ... }


    public function load() { ... }


    }


    use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;


    #[AsTaggedItem(index: 'yaml_loader', priority: 10)]


    final class YamlLoader implements LoaderInterface


    {


    public function supports(string $type): bool { ... }


    public function load() { ... }


    }


    Symfony 5.3

    View Slide

  33. Use tagged services
    use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;


    final class LoaderIterator


    {


    public function __construct(


    #[TaggedIterator('app.loader')]


    private iterable $loaders,


    ) {


    }


    }


    use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;


    #[AsTaggedItem(index: 'xml_loader', priority: 20)]


    final class XmlLoader implements LoaderInterface


    {


    public function supports(string $type): bool { ... }


    public function load() { ... }


    }


    use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;


    #[AsTaggedItem(index: 'yaml_loader', priority: 10)]


    final class YamlLoader implements LoaderInterface


    {


    public function supports(string $type): bool { ... }


    public function load() { ... }


    }


    use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;


    final class LoaderIterator


    {


    public function __construct(


    #[TaggedIterator('app.loader')]


    private iterable $loaders,


    ) {


    }


    }


    Symfony 5.3

    View Slide

  34. Use tagged services
    use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;


    final class LoaderIterator


    {


    public function __construct(


    #[TaggedIterator('app.loader')]


    private iterable $loaders,


    ) {


    }


    }


    use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;


    #[AsTaggedItem(index: 'xml_loader', priority: 20)]


    final class XmlLoader implements LoaderInterface


    {


    public function supports(string $type): bool { ... }


    public function load() { ... }


    }


    use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;


    #[AsTaggedItem(index: 'yaml_loader', priority: 10)]


    final class YamlLoader implements LoaderInterface


    {


    public function supports(string $type): bool { ... }


    public function load() { ... }


    }


    use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;


    final class LoaderIterator


    {


    public function __construct(


    #[TaggedIterator('app.loader')]


    private iterable $loaders,


    ) {


    }


    }


    use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;


    final class LoaderLocator


    {


    public function __construct(


    #[TaggedLocator('app.loader')]


    private ContainerInterface $container,


    ) {


    $yamlLoader = $container->get('yaml_loader');


    $xmlLoader = $container->get('xml_loader');


    }


    }


    Symfony 5.3

    View Slide

  35. Add support for sorting
    fi
    elds
    Form

    View Slide

  36. Add support for sorting
    fi
    elds
    Form
    class PersonRegistrationType extends AbstractType


    {


    public function buildForm(FormBuilderInterface $builder, array $options)


    {


    $builder


    ->add('firstName')


    ->add('lastName')


    ->add(‘register', SubmitType::class);


    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {


    $person = $event->getData();


    if ($person->isLegalType()) {


    $event->getForm()->add('company');


    }


    });


    }


    }


    View Slide

  37. Add support for sorting
    fi
    elds
    Form
    class PersonRegistrationType extends AbstractType


    {


    public function buildForm(FormBuilderInterface $builder, array $options)


    {


    $builder


    ->add('firstName')


    ->add('lastName')


    ->add(‘register', SubmitType::class);


    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {


    $person = $event->getData();


    if ($person->isLegalType()) {


    $event->getForm()->add('company');


    }


    });


    }


    }








    Register {# wrong place #}


    {# wrong place #}


    View Slide

  38. class PersonRegistrationType extends AbstractType


    {


    public function buildForm(FormBuilderInterface $builder, array $options)


    {


    $builder


    ->add('firstName')


    ->add('lastName')


    ->add(‘register', SubmitType::class, ['priority' => -1/*, ... */]);


    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {


    $person = $event->getData();


    if ($person->isLegalType()) {


    $event->getForm()->add('company', null, ['priority' => 1]);


    }


    });


    }


    }


    Add support for sorting
    fi
    elds
    Form

    View Slide

  39. Noti
    fi
    er Component
    Symfony 5.3

    View Slide

  40. Noti
    fi
    er Component
    Symfony 5.3
    not experimental anymore

    View Slide

  41. New Noti
    fi
    er integrations
    Noti
    fi
    er
    Texter
    Chatter

    View Slide

  42. FakeSms & FakeChat
    Noti
    fi
    er
    # .env


    FAKE_SMS_DSN=fakesms+email://default?to=TO&from=FROM


    FAKE_CHAT_DSN=fakechat+email://default?to=TO&from=FROM


    # config/packages/dev/notifier.yaml


    framework:


    notifier:


    chatter_transports:


    slack: '%env(FAKE_CHAT_DSN)%'


    telegram: '%env(FAKE_CHAT_DSN)%'


    texter_transports:


    twilio: '%env(FAKE_SMS_DSN)%'


    nexmo: '%env(FAKE_SMS_DSN)%'


    View Slide

  43. (de)normalization context in mapping
    Serializer
    use Symfony\Component\Serializer\Annotation as Serializer;


    use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;


    class Foo


    {


    /**


    * @Serializer\Context({ DateTimeNormalizer::FORMAT_KEY = 'Y-m-d' })


    */


    public \DateTime $date;


    // In PHP 8 applications you can use PHP attributes instead:


    #[Serializer\Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]


    public \DateTime $anotherDate;


    }


    View Slide

  44. (de)normalization context in mapping
    Serializer
    #[Serializer\Context(


    normalizationContext: [DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'],


    denormalizationContext: [DateTimeNormalizer::FORMAT_KEY => \DateTimeImmutable::COOKIE]


    )]


    public \DateTime $date;


    #[Serializer\Context(


    normalizationContext: [DateTimeNormalizer::FORMAT_KEY => \DateTimeImmutable::RFC3339],


    groups: ['extended']


    )]


    public \DateTime $date;


    View Slide

  45. New Tailwind CSS form theme
    TwigBridge
    # twig.yaml


    twig:


    form_themes:


    - tailwind_2_layout.html.twig


    View Slide

  46. Export concatenated translations
    {{ ('aa' ~ 'bb') | trans }}


    TwigBridge
    php bin/console translation:update --dump-messages de


    View Slide

  47. Twig serialize
    fi
    lter
    {{ object|serialize(format = 'json', context = []) }}


    TwigBridge

    View Slide

  48. Twig serialize
    fi
    lter
    {{ object|serialize(format = 'json', context = []) }}


    TwigBridge
    {{ stimulus_controller('product-show', {


    product: product|serialize('json', { groups: 'product:read'})


    }) }}


    View Slide

  49. PHP Con
    fi
    guration Builder
    Con
    fi
    g


    View Slide

  50. PHP Con
    fi
    guration Builder
    Con
    fi
    g


    View Slide

  51. Removing Directories without Race Conditions
    Filesystem


    View Slide

  52. No-Lock Rate Limiters
    RateLimiter


    View Slide

  53. A Quick Security Recap

    View Slide

  54. New Authentication System (since 5.1)
    • Remove everything but Guard.

    • Use an event-driven system (like HttpKernel).

    • Guards are now called Authenticators, and are a bit di
    ff
    erent.

    • Unauthenticated Tokens are now Passports, and are also a bit
    di
    ff
    erent.

    View Slide

  55. Mandatory Passport Badges
    Security


    View Slide

  56. bin/console debug:
    fi
    rewall
    Security


    View Slide

  57. bin/console debug:
    fi
    rewall
    Security


    View Slide

  58. Generate Login Link with User Locale
    Security


    View Slide

  59. Mask CSRF Token against BREACH
    Security


    View Slide

  60. UID Component (experimental)


    🎉

    View Slide

  61. New UID Factory Methods UID


    View Slide

  62. Generate UIDs via CLI UID


    View Slide

  63. Generate UIDs via CLI UID


    View Slide

  64. UUID & ULID Form Types
    Form / UID


    View Slide

  65. New UID Normalization Formats
    Serializer / UID


    View Slide

  66. Support binary / negatable options
    Contributed by jderusse in #39642
    Console
    class SomeCommand extends Comman
    }


    View Slide

  67. Support binary / negatable options
    Contributed by jderusse in #39642
    Console
    class SomeCommand extends Comman
    d

    {

    // ...
    protected function execute(InputInterface $input, OutputInterface $output): in
    t

    {

    // if command is run as `command-name`, $useAnsi = nul
    l

    // if command is run as `command-name --ansi`, $useAnsi = tru
    e

    // if command is run as `command-name --no-ansi`, $useAnsi = fals
    e

    $useAnsi = $input->getOption('ansi')
    ;

    // ..
    .

    }

    }

    View Slide

  68. Linter: add Github annotations format for errors
    Contributed by ogizanagi in #38982
    Console

    View Slide

  69. Linter: add Github annotations format for errors
    Contributed by ogizanagi in #38982
    Console

    View Slide

  70. Linter: add Github annotations format for errors
    Contributed by ogizanagi in #38982
    Console

    View Slide

  71. Add a remove() method to the PHP con
    fi
    gurator
    Contributed by dunglas in #39806
    DependencyInjection


    // config/services.ph
    p

    namespace Symfony\Component\DependencyInjection\Loader\Configurator
    ;

    use App\Service\FooService
    ;

    return function(ContainerConfigurator $configurator)
    {

    // ..
    .

    $services->remove(FooService::class)
    ;

    };

    View Slide

  72. Add env() and EnvCon
    fi
    gurator in the PHP-DSL
    Contributed by fancyweb in #40682
    DependencyInjection


    // config/services.ph
    p

    namespace Symfony\Component\DependencyInjection\Loader\Configurator
    ;

    use App\Service\FooService
    ;

    use App\Service\BarService
    ;

    return function(ContainerConfigurator $configurator)
    {

    // ..
    .

    $services->set(FooService::class
    )

    ->arg('$myArg', '%env(default:my_param:key:path:url:MY_ENV_VAR)%'
    )

    ;

    $services->set(BarService::class
    )

    ->arg
    (

    '$myArg'
    ,

    env('MY_ENV_VAR'
    )

    ->url(
    )

    ->key('path'
    )

    ->default('my_param'
    )

    )

    ;

    };

    View Slide

  73. Negated (not:) env var processor
    Contributed by bpolaszek in #40169
    DependencyInjection


    # .en
    v

    TRUE_ENV_VAR=tru
    e

    FALSE_ENV_VAR=false
    # config/services.yam
    l

    parameters
    :

    is_processed_as_false: ‘%env(not:TRUE_ENV_VAR)%


    is_processed_as_true: ‘%env(not:FALSE_ENV_VAR)%’

    View Slide

  74. New Form Handler Helper "renderForm"
    Contributed by lyrixx and nicolas-grekas in #41178 and #41190
    FrameworkBundle


    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController
    ;

    class TaskController extends AbstractControlle
    r

    {

    public function new(Request $request): Respons
    e

    {

    $form = $this->createForm(TaskType::class
    )

    ->handleRequest($request
    )

    ;

    $response = $this->render('task/new.html.twig',
    [

    'form' => $form->createView()
    ,

    'other_variable' => 'foo'
    ,

    ])
    ;

    if ($form->isSubmitted() && !$form->isValid())
    {

    $response->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY)
    ;

    }

    return $response
    ;

    }

    }

    View Slide

  75. New Form Handler Helper "renderForm"
    Contributed by lyrixx and nicolas-grekas in #41178 and #41190
    FrameworkBundle


    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController
    ;

    class TaskController extends AbstractControlle
    r

    {

    public function new(Request $request): Respons
    e

    {

    $form = $this->createForm(TaskType::class
    )

    ->handleRequest($request
    )

    ;

    return $this->renderForm('task/new.html.twig',
    [

    'form' => $form
    ,

    'other_variable' => 'foo'
    ,

    ])
    ;

    }

    }

    View Slide

  76. Command cache:pool:clear warns and fails when
    one of the pools fails to clear
    Contributed by jderusse in #39910
    FrameworkBundle


    Symfony <=5.2
    Symfony >=5.3
    Return code: 0
    Return code: 1

    View Slide

  77. Con
    fi
    gure Multiple Environments in a Single File
    Contributed by nicolas-grekas in #40214 and #40782
    FrameworkBundle


    View Slide

  78. Con
    fi
    gure Multiple Environments in a Single File
    Contributed by nicolas-grekas in #40214 and #40782
    FrameworkBundle


    // config/packages/framework.ph
    l

    framework:
    secret: '%env(APP_SECRET)%'
    when@dev:
    services:
    App\SomeServiceForDev: ~
    when@test:
    framework:
    test: true
    # ...

    View Slide

  79. Con
    fi
    gure Multiple Environments in a Single File
    Contributed by nicolas-grekas in #40214 and #40782
    FrameworkBundle


    // config/packages/framework.ph
    >

    >

    >

    >

    >

    >

    >

    >

    >

    >

    >

    >


    View Slide

  80. Con
    fi
    gure Multiple Environments in a Single File
    Contributed by nicolas-grekas in #40214 and #40782
    FrameworkBundle


    # config/packages/framework.yam
    p

    namespace Symfony\Component\DependencyInjection\Loader\Configurator
    ;

    use App\SomeServiceForDev
    ;

    use Symfony\Config\FrameworkConfig
    ;

    return static function (FrameworkConfig $framework, ContainerConfigurator $container)
    {

    $framework->secret(env('APP_SECRET'))
    ;

    if ($container->env() === 'dev')
    {

    $container->services(
    )

    ->set(SomeServiceForDev::class
    )

    ;

    }

    if ($container->env() === 'test')
    {

    $framework->test(true)
    ;

    }

    // ..
    .

    };

    View Slide

  81. Add SYMFONY_PHPUNIT_REQUIRE env variable
    Contributed by acasademont in #40059
    PhpUnitBridge


    >

    >

    View Slide

  82. Add SYMFONY_PHPUNIT_REQUIRE env variable
    Contributed by acasademont in #40059
    PhpUnitBridge


    >

    >

    View Slide

  83. Add SYMFONY_PHPUNIT_REQUIRE env variable
    Contributed by acasademont in #40059
    PhpUnitBridge


    >

    >

    View Slide

  84. class BooksTest extends WebTestCas
    e

    {

    public function testGetCollection(): voi
    d

    {

    static::createClient()->request('GET', '/books')
    ;

    $this->assertResponseFormatSame('jsonld')
    ;

    }

    }

    // Predefined formats see \Symfony\Component\HttpFoundation\Request::initializeFormats()
    add assertResponseFormatSame()
    Contributed by dunglas in #39666
    FrameworkBundle


    View Slide

  85. class FooTest extends WebTestCas
    e

    {

    public function testGetContainerA(): void
    {

    $kernel = self::bootKernel()
    ;

    $container = $kernel->getContainer()
    ;

    }

    public function testGetContainerB(): void
    {

    self::bootKernel()
    ;

    $container = self::$container; // @deprecated since Symfony 5.3, use static::getContainer() instea
    d

    }

    public function testGetContainerC(): void
    {

    $client = self::createClient()
    ;

    $container = $client->getContainer()
    ;

    }

    public function testGetContainerTheOnlyWayYouShouldUse(): void
    {

    $container = self::getContainer()
    ;

    }

    Add KernelTestCase::getContainer()
    Contributed by Nyholm in #40366
    FrameworkBundle


    View Slide

  86. There is even more 😱
    https://symfony.com/blog/category/living-on-the-edge/5.3
    https://github.com/symfony/symfony/blob/5.x/CHANGELOG-5.3.md
    https://github.com/symfony/symfony/blob/5.x/UPGRADE-5.3.md

    View Slide

  87. New Components

    View Slide

  88. Questions?

    View Slide

  89. Thank you!

    View Slide