Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What's new in Symfony 4.4/5.0

What's new in Symfony 4.4/5.0

Jan Schädlich

January 06, 2020
Tweet

More Decks by Jan Schädlich

Other Decks in Programming

Transcript

  1. Jan Schädlich
    [email protected]
    @jschaedl
    @janschaedlich

    View full-size slide

  2. Looking for a Job?
    https://sensiolabs.de/jobs/96454

    View full-size slide

  3. Agenda
    Deprecations

    Improvements

    New Components

    View full-size slide

  4. 4.4
    Deprecations
    Bugfixes Bugfixes
    Features Features
    Deprecations
    5.0
    What are the Differences?

    View full-size slide

  5. Deprecations
    Symfony 4.4
    from 4.3 to 4.4
    Code will be removed in Symfony 5.0

    View full-size slide

  6. Non-int return value in Command::execute()
    Contributed by jschaedl in #33775
    namespace App\Command;
    use Symfony\Component\Console\Command\Command;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    class CreateUserCommand extends Command
    {
    protected static $defaultName = 'app:create-user';
    // ...
    protected function execute(InputInterface $input, OutputInterface $output)
    {
    // ...
    }
    }
    Console

    View full-size slide

  7. Non-int return value in Command::execute()
    Contributed by jschaedl in #33775
    protected function execute(InputInterface $input, OutputInterface $output)
    {
    // ...
    return 0;
    }
    Console

    View full-size slide

  8. Deprecated tag !tagged in favor of !tagged_iterator
    Contributed by jschaedl in #31321
    # Before
    services:
    App\Handler:
    tags: ['app.handler']
    App\HandlerCollection:
    arguments: [!tagged app.handler]
    # After
    services:
    App\Handler:
    tags: ['app.handler']
    App\HandlerCollection:
    arguments: [!tagged_iterator app.handler]
    DependencyInjection

    View full-size slide

  9. Deprecated short factories/configurators YAML syntax
    Contributed by nicolas-grekas in #31543
    # Before
    services:
    my_service:
    factory: factory_service:method
    # After
    services:
    my_service:
    factory: ['@factory_service', method]
    DependencyInjection

    View full-size slide

  10. Deprecate int/float for string input in NumberType
    Contributed by xabbuh in #32130
    use Symfony\Component\Form\Extension\Core\Type\NumberType;
    $form = $this->factory->create(NumberType::class, 2.99, [
    'input' => 'string',
    'scale' => 2,
    ]);
    Form

    View full-size slide

  11. Deprecated HeaderBag::get() returning an array
    Contributed by Simperfit in #32122
    // Symfony 4.3
    namespace Symfony\Component\HttpFoundation;
    class HeaderBag implements \IteratorAggregate, \Countable
    {
    /**
    * Returns a header value by name.
    *
    * @param string $key The header name
    * @param string|null $default The default value
    * @param bool $first Whether to return the first value or all header values
    *
    * @return string|string[]|null The first header value or default value if $first is true,
    * an array of values otherwise
    */
    public function get($key, $default = null, $first = true) {...}
    }
    HttpFoundation

    View full-size slide

  12. Deprecated HeaderBag::get() returning an array
    Contributed by Simperfit in #32122
    namespace Symfony\Component\HttpFoundation;
    class HeaderBag implements \IteratorAggregate, \Countable
    {
    // Symfony 4.4, using argument $first is deprecated
    /**
    * @return string|string[]|null The first header value or default value if $first is
    * true, an array of values otherwise
    */
    public function get($key, $default = null) {…}
    // Symfony 5.0
    /**
    * @return string|null The first header value or default value
    */
    public function get(string $key, string $default = null) {...}
    }
    HttpFoundation

    View full-size slide

  13. Deprecated HeaderBag::get() returning an array
    Contributed by Simperfit in #32122
    class HeaderBag implements \IteratorAggregate, \Countable
    {
    // Symfony 4.4
    public function all(/*string $key = null*/) {…}
    // Symfony 5.0
    public function all(string $key = null) {...}
    }
    HttpFoundation

    View full-size slide

  14. Deprecated passing arguments to Request::isMethodSafe()
    Contributed by dFayet in #31658
    // Symfony 4.3
    namespace Symfony\Component\HttpFoundation;
    class Request
    /**
    * Checks whether or not the method is safe.
    *
    * @param bool $andCacheable Adds the additional condition that the
    * method should be cacheable. True by default.
    *
    * @return bool
    */
    public function isMethodSafe(/* $andCacheable = true */) {...}
    }
    HttpFoundation

    View full-size slide

  15. Deprecated passing arguments to Request::isMethodSafe()
    Contributed by dFayet in #31658
    // Symfony 4.3
    $request->isMethodSafe(true); // throws BadMethodCallException
    $request->isMethodSafe(); // throws BadMethodCallException
    $request->isMethodSafe(false);
    // Symfony 4.4
    $request->isMethodSafe(false); // deprecated
    // use
    $request->isMethodSafe();
    $request->isMethodCachable();
    HttpFoundation

    View full-size slide

  16. Deprecated returning non-boolean
    values from checkCredentials()
    Contributed by derrabus in #33308
    # Symfony 4.3
    # Guard\AuthenticatorInterface.php
    /**
    * Returns true if the credentials are valid.
    *
    * If any value other than true is returned, authentication will
    * fail. You may also throw an AuthenticationException if you wish
    * to cause authentication to fail.
    *
    * The *credentials* are the return value from getCredentials()
    *
    * @param mixed $credentials
    * @return bool
    * @throws AuthenticationException
    */
    public function checkCredentials($credentials, UserInterface $user);
    Security

    View full-size slide

  17. Deprecated returning non-boolean
    values from checkCredentials()
    Contributed by derrabus in #33308
    # Symfony 4.4
    # Guard\AuthenticatorInterface.php
    /**
    * Returns true if the credentials are valid.
    *
    * If false is returned, authentication will fail. You may also throw
    * an AuthenticationException if you wish to cause authentication to fail.
    *
    * The *credentials* are the return value from getCredentials()
    *
    * @param mixed $credentials
    * @return bool
    * @throws AuthenticationException
    */
    public function checkCredentials($credentials, UserInterface $user);
    Security

    View full-size slide

  18. Deprecated isGranted() on more than one attribute
    Contributed by wouterj in #33584
    // Before
    if ($this->authorizationChecker->isGranted(['ROLE_USER', 'ROLE_ADMIN'])) {
    // ...
    }
    // After
    if ($this->authorizationChecker->isGranted(
    new Expression("is_granted('ROLE_USER') or is_granted(‘ROLE_ADMIN')"))
    ) {}
    // or
    if ($this->authorizationChecker->isGranted('ROLE_USER')
    || $this->authorizationChecker->isGranted('ROLE_ADMIN')) {}
    Security

    View full-size slide

  19. Debug Component
    deprecated
    Use the new ErrorHandler Component instead!

    View full-size slide

  20. WebServer Bundle
    $ curl -sS https://get.symfony.com/cli/installer | bash
    deprecated
    Use the Symfony CLI instead!

    View full-size slide

  21. └── MyBundle/
    ├── config/
    ├── public/
    ├── src/
    │ └── MyBundle.php
    ├── templates/
    └── translations
    class MyBundle extends Bundle
    {
    public function getPath(): string
    {
    return \dirname(__DIR__);
    }
    }
    Added new Bundle directory convention
    consistent with standard skeleton
    HttpKernel

    View full-size slide

  22. Type Declarations and Return Types
    https://symfony.com/blog/symfony-type-declarations-return-types
    https://github.com/symfony/symfony/issues/32179

    View full-size slide

  23. BC-Breaks
    Symfony 4.4
    from 4.3 to 4.4

    View full-size slide

  24. - [HttpClient] Added method cancel() to ResponseInterface.

    - [Mailer] Changed the DSN to use for disabling delivery (using the NullTransport) from smtp://null to null://null (host doesn't matter).

    - [Mailer] Renamed class SmtpEnvelope to Envelope and DelayedSmtpEnvelope to DelayedEnvelope.

    - [Mailer] Added a required string $transport argument to MessageEvent::__construct.

    - [Messenger] Removed SendersLocatorInterface::getSenderByAlias added in 4.3.

    - [Messenger] Removed $retryStrategies argument from Worker::__construct.

    - [Messenger] Changed arguments of ConsumeMessagesCommand::__construct.

    - [Messenger] Removed $senderClassOrAlias argument from RedeliveryStamp::__construct.

    - [Messenger] Removed UnknownSenderException.

    - [Messenger] Removed WorkerInterface.

    - [Messenger] Removed $onHandledCallback of Worker::run(array $options = [], callable $onHandledCallback = null).

    - [Messenger] Removed StopWhenMemoryUsageIsExceededWorker in favor of StopWorkerOnMemoryLimitListener.

    - [Messenger] Removed StopWhenMessageCountIsExceededWorker in favor of StopWorkerOnMessageLimitListener.

    - [Messenger] Removed StopWhenTimeLimitIsReachedWorker in favor of StopWorkerOnTimeLimitListener.

    - [Messenger] Removed StopWhenRestartSignalIsReceived in favor of StopWorkerOnRestartSignalListener.

    - [Mime] Removed NamedAddress, use Address instead (which supports a name now)

    View full-size slide

  25. There is even more
    https://github.com/symfony/symfony/blob/master/UPGRADE-4.4.md
    https://github.com/symfony/symfony/blob/master/UPGRADE-5.0.md

    View full-size slide

  26. Improvements

    View full-size slide

  27. Improved Type Constraint
    Contributed by jschaedl in #31351
    // src/Entity/Author.php
    use Symfony\Component\Validator\Constraints as Assert;
    class Author
    {
    /**
    * @Assert\Type("Ramsey\Uuid\UuidInterface")
    */
    protected $id;
    /**
    * @Assert\Type("string")
    */
    protected $firstName;
    // ...
    }
    Validator

    View full-size slide

  28. Improved Type Constraint
    Contributed by jschaedl in #31351
    // src/Entity/Author.php
    use Symfony\Component\Validator\Constraints as Assert;
    class Author
    {
    // ...
    /**
    * @Assert\Type(type={"alpha", "digit"})
    */
    protected $accessCode;
    // ...
    }
    Validator

    View full-size slide

  29. Week Form Type
    Contributed by dFayet in #32061
    $builder->add('startDateTime', WeekType::class, [
    // use this if you store week numbers as strings ('2011-W17')
    'input' => 'string',
    // use this if you store week numbers as arrays (e.g. [2011, 17])
    'input' => 'array',
    // renders two to select the year and week number
    'widget' => 'choice',
    // renders two to write the year and week number
    'widget' => 'text',
    // renders a which is properly rendered by most browsers
    'widget' => 'single_text',
    ]);
    Form

    View full-size slide

  30. PHP Assertions for Email Messages
    Contributed by fabpot in #32930
    // tests/Controller/DefaultControllerTest.php
    use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
    class DefaultControllerTest extends WebTestCase
    {
    public function testSomething()
    {
    $client = static::createClient();
    $client->request('GET', '/newsletter-signup');
    // ...
    $this->assertEmailCount(2);
    $this->assertEmailIsQueued($this->getMailerEvent(0));
    $email = $this->getMailerMessage(0);
    $this->assertEmailHeaderSame($email, 'To', '[email protected]');
    $this->assertEmailTextBodyContains($email, 'Welcome to Symfony!');
    $this->assertEmailAttachementCount($email, 1);
    }
    }
    FrameworkBundle

    View full-size slide

  31. PHP Assertions for Email Messages
    Contributed by fabpot in #32930
    FrameworkBundle
    $this->assertEmailCount()
    $this->assertQueuedEmailCount()
    $this->assertEmailIsQueued()
    $this->assertEmailIsNotQueued()
    $this->assertEmailAttachementCount()
    $this->assertEmailTextBodyContains()
    $this->assertEmailTextBodyNotContains()
    $this->assertEmailHtmlBodyContains()
    $this->assertEmailHtmlBodyNotContains()
    $this->assertEmailHasHeader()
    $this->assertEmailNotHasHeader()
    $this->assertEmailHeaderSame()
    $this->assertEmailHeaderNotSame()
    $this->assertEmailAddressContains()

    View full-size slide

  32. Simpler Event Listeners
    Contributed by derrabus in #33851
    EventDispatcher
    use Symfony\Component\HttpKernel\Event\RequestEvent;
    final class MyRequestListener
    {
    public function __invoke(RequestEvent $event): void
    {
    // ...
    }
    }
    services:
    App\EventListener\MyRequestListener:
    tags:
    - - { name: kernel.event_listener, event: kernel.request }
    + - { name: kernel.event_listener }
    # config/services.yaml
    services:
    App\EventListener\:
    resource: ../src/EventListener/*
    tags: ['kernel.event_listener']

    View full-size slide

  33. Allow Binding Tagged Services
    Contributed by lyrixx in #33623
    services:
    _instanceof:
    App\Foo\Rule\RuleInterface:
    tags: ['app.foo.rule']
    _defaults:
    bind:
    iterable $rules: !tagged_iterator app.foo.rule
    # ...
    DependencyInjection

    View full-size slide

  34. Improved YAML Syntax for Method Calls
    Contributed by lyrixx in #33623
    services:
    App\Service\MessageGenerator:
    # ...
    calls:
    - method: setLogger
    arguments:
    - '@logger'
    DependencyInjection
    Contributed by nicolas-grekas in #33779.
    services:
    App\Service\MessageGenerator:
    # ...
    calls:
    - setLogger: ['@logger']

    View full-size slide

  35. Priorities for Tagged Services
    Contributed by lyrixx in #33623
    services:
    _instanceof:
    App\Handler:
    tags:
    - { name: 'app.handler', priority: 20 }
    App\HandlerCollection:
    arguments: [!tagged_iterator app.handler]
    DependencyInjection
    services:
    # ...
    App\HandlerCollection:
    arguments: [!tagged_iterator app.handler, default_priority_method: 'calculateServicePriority']
    final class MyService
    {
    public static function getDefaultPriority(): int
    {
    return 0; }
    }
    }

    View full-size slide

  36. Mailer Integration
    Contributed by fabpot in #32912
    WebProfilerBundle

    View full-size slide

  37. HttpClient Integration
    Contributed by tyx and jeremyFreeAgent in #33015
    WebProfilerBundle

    View full-size slide

  38. Clear Ajax Requests
    Contributed by Matts in #31876
    WebProfilerBundle

    View full-size slide

  39. Service Container Linter
    DependencyInjection
    Contributed by alcalyn, GuilhemN and nicolas-grekas in #33015
    $ bin/console lint:container

    View full-size slide

  40. Service Container Linter
    DependencyInjection
    Contributed by alcalyn, GuilhemN and nicolas-grekas in #33015
    namespace App\SomeNamespace;
    class SomeService
    {
    public function __construct(int $someProperty = 7)
    {
    // ...
    }
    }
    services:
    App\SomeNamespace\SomeService: ~
    Invalid definition for service "App\SomeNamespace\SomeService": argument 1 of
    "App\SomeNamespace\SomeService::__construct" accepts "int", "NULL" passed.

    View full-size slide

  41. Service Container Linter
    DependencyInjection
    Contributed by alcalyn, GuilhemN and nicolas-grekas in #33015
    namespace App\SomeNamespace;
    class SomeService
    {
    public function setSomeItems(
    SomeClass $item,
    SomeClass …$items
    ) {
    // ...
    }
    }
    Invalid definition for service "App\SomeNamespace\SomeService": argument 2 of
    "App\SomeNamespace\SomeService::setSomeItems" accepts "App\SomeNamespace\SomeClass",
    "App\AnotherNamespace\SomeDifferentClass" passed.
    services:
    foo:
    class: App\SomeNamespace\SomeClass
    bar:
    class: App\AnotherNamespace\SomeDifferentClass
    App\SomeNamespace\SomeService:
    calls:
    - method: setSomeItems
    arguments:
    - '@foo'
    - '@bar'

    View full-size slide

  42. Notification Emails
    Contributed by fabpot in #33605
    use Symfony\Bridge\Twig\Mime\NotificationEmail;
    $email = (new NotificationEmail())
    ->from('[email protected]')
    ->to('[email protected]')
    ->subject('My first notification email via Symfony')
    ->markdown(<<There is a **problem** on your website, you should investigate it
    right now. Or just wait, the problem might solves itself automatically,
    we never know.
    EOF
    )
    ->action('More info?', 'https://example.com/')
    ->importance(NotificationEmail::IMPORTANCE_HIGH)
    ;
    Mime

    View full-size slide

  43. Notification Emails
    Contributed by fabpot in #33605
    Mime

    View full-size slide

  44. Lazy Firewalls
    Contributed by nicolas-grekas in #33676
    # config/packages/security.yaml
    security:
    # ...
    firewalls:
    main:
    pattern: ^/
    anonymous: ~
    # ...
    Security
    # config/packages/security.yaml
    security:
    # ...
    firewalls:
    main:
    pattern: ^/
    anonymous: lazy
    # ...

    View full-size slide

  45. Added Support for Alpha-3 Codes
    Contributed by creiner in #33791
    use Symfony\Component\Form\Extension\Core\Type\CountryType;
    // ...
    $builder->add('country', CountryType::class, [
    'alpha3' => true, // ISO 3166-1, e.g deu
    ]);
    Form

    View full-size slide

  46. Password Migrations
    Contributed by nicolas-grekas in #31594, #31597 and #31843
    # config/packages/security.yaml
    security:
    # ...
    encoders:
    App\Entity\User:
    algorithm: auto
    cost: 14
    Security
    use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
    class UserRepository extends EntityRepository implements PasswordUpgraderInterface
    {
    public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
    {
    // this code is only an example; the exact code will depend on
    $user->setPassword($newEncodedPassword);
    $this->getEntityManager()->flush($user);
    }
    }

    View full-size slide

  47. Password Hashing
    Contributed by chalas_r in #34020 and #34139
    security:
    # ...
    encoders:
    App\Entity\User:
    algorithm: 'argon2i'
    algorithm: 'argon2id'
    algorithm: 'auto'
    algorithm: 'bcrypt'
    algorithm: 'sodium'
    Security
    # config/packages/security.yaml
    security:
    # ...
    encoders:
    App\Entity\User:
    algorithm: 'argon2i'
    migrate_from: 'bcrypt'

    View full-size slide

  48. Preloading Symfony
    Applications in PHP 7.4

    View full-size slide

  49. OPCache Preloading in Practice
    ; php.ini
    opcache.preload=/path/to/the/preload.php

    View full-size slide

  50. OPCache Preloading in Symfony
    ; php.ini
    opcache.preload=/path/to/project/var/cache/prod/App_KernelProdContainer.preload.php

    View full-size slide

  51. OPCache Preloading in Symfony
    namespace Symfony\Component\HttpKernel;
    abstract class Kernel implements KernelInterface, RebootableInterface, TerminableInterface
    {
    /**
    * Gets the classes to list in the preloading script.
    *
    * When a class is listed, all its parent classes or interfaces are automatically listed too.
    * Service classes are also automatically preloaded and don't need to be listed explicitly.
    */
    public function getClassesToPreload(): array
    {
    //...
    }
    }

    View full-size slide

  52. OPCache Preloading in Symfony
    namespace Symfony\Component\HttpKernel\DependencyInjection;
    abstract class Extension extends BaseExtension
    {
    /**
    * Adds classes to list in the preloading script.
    *
    * When a class is listed, all its parent classes or interfaces are automatically listed too.
    * Service classes are also automatically preloaded and don't need to be listed explicitly.
    */
    public function addClassesToPreload(array $preloadedClasses): void
    {
    //...
    }
    }

    View full-size slide

  53. There is even more
    https://symfony.com/blog/category/living-on-the-edge/5.0-4.4
    https://symfony.com/blog/symfony-4-4-curated-new-features
    https://symfony.com/blog/symfony-5-0-curated-new-features

    View full-size slide

  54. New Components

    View full-size slide

  55. ErrorHandler Component
    Symfony 4.4
    The ErrorHandler component provides tools to
    manage errors and ease debugging PHP code.
    $ composer require symfony/error-handler

    View full-size slide

  56. # public/index.php
    use App\Kernel;
    // Before
    use Symfony\Component\Debug\Debug;
    require dirname(__DIR__).'/config/bootstrap.php';
    if ($_SERVER['APP_DEBUG']) {
    umask(0000);
    Debug::enable();
    }
    // ...
    $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
    $request = Request::createFromGlobals();
    $response = $kernel->handle($request);
    $response->send();
    $kernel->terminate($request, $response);

    View full-size slide

  57. # public/index.php
    use App\Kernel;
    // After
    use Symfony\Component\ErrorHandler\Debug;
    require dirname(__DIR__).'/config/bootstrap.php';
    if ($_SERVER['APP_DEBUG']) {
    umask(0000);
    Debug::enable();
    }
    // ...
    $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
    $request = Request::createFromGlobals();
    $response = $kernel->handle($request);
    $response->send();
    $kernel->terminate($request, $response);

    View full-size slide

  58. Class Loading Debugger
    use Symfony\Component\ErrorHandler\DebugClassLoader;
    DebugClassLoader::enable();

    View full-size slide

  59. Turning PHP Errors into Exeptions
    use Symfony\Component\ErrorHandler\ErrorHandler;
    ErrorHandler::register();

    View full-size slide

  60. Catching PHP Function Errors and
    turning them into Exceptions
    data = json_decode(file_get_contents($filename), true);
    $data['read_at'] = date($datetimeFormat);
    file_put_contents($filename, json_encode($data));

    View full-size slide

  61. Catching PHP Function Errors and
    turning them into Exceptions
    $content = @file_get_contents($filename);
    if (false === $content) {
    throw new \RuntimeException('Could not load file.');
    }
    $data = @json_decode($content, true);
    if (null === $data) {
    throw new \RuntimeException('File does not contain valid JSON.');
    }
    $datetime = @date($datetimeFormat);
    if (false === $datetime) {
    throw new \RuntimeException('Invalid datetime format.');
    }

    View full-size slide

  62. Catching PHP Function Errors and
    turning them into Exceptions
    $content = ErrorHandler::call('file_get_contents', $filename);

    View full-size slide

  63. Catching PHP Function Errors and
    turning them into Exceptions
    $data = ErrorHandler::call(static function () use ($filename,
    $datetimeFormat) {
    // if any code executed inside this anonymous function fails,
    // a PHP exception will be thrown, even if the code
    // uses the '@' PHP silence operator
    $data = json_decode(file_get_contents($filename), true);
    $data['read_at'] = date($datetimeFormat);
    file_put_contents($filename, json_encode($data));
    return $data;
    });

    View full-size slide

  64. {
    "title": "Not Found",
    "status": 404,
    "detail": "Sorry, the page you are looking for could not be found"
    }
    Error pages for non-HTML formats
    templates/bundles/TwigBundle/Exception/error403.json.twig
    Request Format
    JSON XML ATOM TXT RFC 7807
    deprecated

    View full-size slide

  65. Deprecated error templates for non-html formats
    Contributed by yceruto in #31398
    # If you were not using this option previously, set it to `null`
    twig:
    exception_controller: null
    # If you were using this option previously, set it to `null`
    # and use `framework.error_controller` instead
    # Before
    twig:
    exception_controller: 'App\Controller\MyExceptionController'
    # After
    twig:
    exception_controller: null
    framework:
    error_controller: 'App\Controller\MyExceptionController'

    View full-size slide

  66. How to customize error pages for non-HTML formats?
    // templates/bundles/TwigBundle/Exception/error.json.twig
    {
    "type": "https://example.com/error",
    "title": "{{ status_text }}",
    "status": {{ status_code }}
    }

    View full-size slide

  67. How to customize error pages for non-HTML formats?
    class ProblemJsonNormalizer implements NormalizerInterface
    {
    public function normalize($exception, $format = null, array $context = [])
    {
    return [
    'type' => 'https://example.com/error',
    'title' => $exception->getStatusText(),
    'status' => $exception->getStatusCode(),
    ];
    }
    public function supportsNormalization($data, $format = null)
    {
    return 'json' === $format && $data instanceof FlattenException;
    }
    }

    View full-size slide

  68. Custom HTML error pages based on Twig
    keep working as before.
    templates/bundles/TwigBundle/Exception/error500.html.twig

    View full-size slide

  69. Error preview pages
    - # config/routes/dev/twig.yaml
    + # config/routes/dev/framework.yaml
    _errors:
    - resource: '@TwigBundle/Resources/config/routing/errors.xml'
    + resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
    prefix: /_error
    The error page preview feature keeps working as before,
    but some files have changed their location.

    View full-size slide

  70. Notifier Component
    Symfony 5.0
    experimental
    https://www.meetup.com/de-DE/sfughh/events/xqdjjrybcdbgb/

    View full-size slide

  71. String Component
    Symfony 5.0
    experimental
    https://www.meetup.com/de-DE/sfughh/events/xqdjjrybcdbgb/

    View full-size slide

  72. Thank you!
    https://speakerdeck.com/jschaedl

    View full-size slide