Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Malte Schlüter [email protected] @malteschlueter

Slide 3

Slide 3 text

Looking for a Job? https://sensiolabs.de/unternehmen/stellenanzeigen/96454

Slide 4

Slide 4 text

Agenda Deprecations New Features PHP 8 New Components

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

https://packagist.org/packages/symfony/symfony/advisories ^5.1. 5 ^4.4.1 3 ^3.4.35

Slide 7

Slide 7 text

Deprecations from 5.1 to 5.2 Code will be removed in Symfony 6.0 Symfony 5.2 https://github.com/symfony/symfony/blob/5.x/UPGRADE-5.2.md

Slide 8

Slide 8 text

Deprecated setPrivate() use Symfony\Component\DependencyInjection\Alias ; use Symfony\Component\DependencyInjection\Definition ; /** @var Definition $definition * / $definition->setPublic(true) ; $definition->setPrivate(false) ; /** @var Alias $alias * / $alias->setPublic(true) ; $alias->setPrivate(false) ; DependencyInjection

Slide 9

Slide 9 text

Deprecated public services to private FrameworkBundle validator form.factory form.type.file translator security.csrf.token_manager serializer cache_clearer filesystem use Symfony\Component\DependencyInjection\ContainerInterface ; class SomeServic e { private ContainerInterface $container ; public function doSomething(): voi d { $translator = $this->container->get('translator') ; } } // Accessing the "%alias_id%" service directly from the container is deprecated, use dependency injection instead .

Slide 10

Slide 10 text

Deprecated public services to private TwigBundle twig use Symfony\Component\DependencyInjection\ContainerInterface ; class SomeServic e { private Environment $container ; public function doSomething(): voi d { $twig = $this->container->get('twig') ; } } // Accessing the "%alias_id%" service directly from the container is deprecated, use dependency injection instead .

Slide 11

Slide 11 text

Deprecated the allowEmptyString option Validator use Symfony\Component\Validator\Constraints as Assert ; // Befor e /* * * @Assert\Length(min=5, allowEmptyString=true ) * / // Afte r /* * * @Assert\AtLeastOneOf( { * @Assert\Blank() , * @Assert\Length(min=5 ) * } ) * /

Slide 12

Slide 12 text

BC BREAKS from 5.1 to 5.2 Symfony 5.2 only in experimental features

Slide 13

Slide 13 text

Changed return type Noti f i er namespace Symfony\Component\Notifier\Transport ; use Symfony\Component\Notifier\Message\MessageInterface ; use Symfony\Component\Notifier\Message\SentMessage ; /* * * @experimental in 5. 1 * / interface TransportInterfac e { // Befor e public function send(MessageInterface $message): void ; // Afte r public function send(MessageInterface $message): ?SentMessage; }

Slide 14

Slide 14 text

Changed return type Noti f i er namespace Symfony\Component\Notifier\Transport ; use Symfony\Component\Notifier\Message\MessageInterface ; use Symfony\Component\Notifier\Message\SentMessage ; /* * * @experimental in 5. 1 * / abstract class AbstractTransport implements TransportInterfac e { // Befor e abstract protected function doSend(MessageInterface $message): void ; // Afte r abstract protected function doSend(MessageInterface $message): SentMessage ; // ... }

Slide 15

Slide 15 text

Changed type-hint Noti f i er namespace Symfony\Component\Notifier\Notification ; use Symfony\Component\Notifier\Message\EmailMessage ; use Symfony\Component\Notifier\Recipient\EmailRecipientInterface ; use Symfony\Component\Notifier\Recipient\Recipient ; /* * * @experimental in 5. 1 * / interface EmailNotificationInterfac e { // Befor e public function asEmailMessage(Recipient $recipient, string $transport = null): ?EmailMessage ; // Afte r public function asEmailMessage(EmailRecipientInterface $recipient, string $transport = null): ?EmailMessage ; }

Slide 16

Slide 16 text

Changed type-hint Noti f i er namespace Symfony\Component\Notifier\Notification ; use Symfony\Component\Notifier\Message\SmsMessage ; use Symfony\Component\Notifier\Recipient\Recipient ; use Symfony\Component\Notifier\Recipient\SmsRecipientInterface ; /* * * @experimental in 5. 1 * / interface SmsNotificationInterfac e { // Befor e public function asSmsMessage(Recipient $recipient, string $transport = null): ?SmsMessage ; // Afte r public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage ; }

Slide 17

Slide 17 text

Changed type-hint Noti f i er namespace Symfony\Component\Notifier ; use Symfony\Component\Notifier\Notification\Notification ; use Symfony\Component\Notifier\Recipient\Recipient ; use Symfony\Component\Notifier\Recipient\RecipientInterface ; /* * * @experimental in 5. 1 * / interface NotifierInterfac e { // Befor e public function send(Notification $notification, Recipient ...$recipients): void ; // Afte r public function send(Notification $notification, RecipientInterface ...$recipients): void ; }

Slide 18

Slide 18 text

Changed type-hint Noti f i er namespace Symfony\Component\Notifier\Notification ; use Symfony\Component\Notifier\Recipient\Recipient ; use Symfony\Component\Notifier\Recipient\RecipientInterface ; /* * * @experimental in 5. 1 * / class Notificatio n { // Befor e public function getChannels(Recipient $recipient): array ; // Afte r public function getChannels(RecipientInterface $recipient): array ; }

Slide 19

Slide 19 text

Changed type-hint Noti f i er namespace Symfony\Component\Notifier\Channel ; use Symfony\Component\Notifier\Notification\Notification ; use Symfony\Component\Notifier\Recipient\Recipient ; use Symfony\Component\Notifier\Recipient\RecipientInterface ; /* * * @experimental in 5. 1 * / interface ChannelInterfac e { // Before public function notify(Notification $notification, Recipient $recipient, string $transportName = null): void ; public function supports(Notification $notification, Recipient $recipient): bool ; // After public function notify(Notification $notification, RecipientInterface $recipient, string $transportName = null): void ; public function supports(Notification $notification, RecipientInterface $recipient): bool ; }

Slide 20

Slide 20 text

Updated Security System Security // Remove d \Symfony\Component\Security\Http\Firewall\AccessListener::PUBLIC_ACCES S // Use instea d \Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter::PUBLIC_ACCES S https://symfony.com/blog/new-in-symfony-5-1-updated-security-system In the experimental authenticator-based system, * TokenInterface::getUser( ) returns null in case of unauthenticated session.

Slide 21

Slide 21 text

New features Symfony 5.2

Slide 22

Slide 22 text

True colors in the console Contributed by fabpot in #36802 // using a predefined styl e $output->writeln('... contents ...>') ; // custom style using basic color s $output->writeln('... contents …>') ; // custom style using true color s $output->writeln('... contents ...>') ; // the third optional argument defines the style s $color = new Color('#000', '#fff', ['underscore', 'reverse']) ; echo $color->apply('... contents ...') ; Console

Slide 23

Slide 23 text

Doctrine types for UUID and ULID Contributed by gennadigennadigennadi in #37678 /* * * @ORM\Entit y * / class Produc t { /* * * @ORM\Column(type="uuid" ) * / private $someProperty ; /* * * @ORM\Column(type="uuid_binary" ) * / private $anotherProperty ; } Doctrine /* * * @ORM\Entit y * / class Produc t { /* * * @ORM\Column(type="ulid" ) * / private $someProperty ; /* * * @ORM\Column(type="ulid_binary" ) * / private $anotherProperty ; }

Slide 24

Slide 24 text

Doctrine types for UUID and ULID Contributed by gennadigennadigennadi in #37678 // there are generators for UUID V1 and V6 to o use Symfony\Bridge\Doctrine\IdGenerator\UuidV4Generator; /* * * @ORM\Entit y * / class Produc t { /* * * @ORM\I d * @ORM\Column(type="uuid", unique=true ) * @ORM\GeneratedValue(strategy="CUSTOM" ) * @ORM\CustomIdGenerator(class=UuidV4Generator::class ) * / private $id ; } Doctrine

Slide 25

Slide 25 text

Doctrine types for UUID and ULID Contributed by gennadigennadigennadi in #37678 use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator; /* * * @ORM\Entit y * / class Produc t { /* * * @ORM\I d * @ORM\Column(type="ulid", unique=true ) * @ORM\GeneratedValue(strategy="CUSTOM" ) * @ORM\CustomIdGenerator(class=UlidGenerator::class ) * / private $id ; } Doctrine

Slide 26

Slide 26 text

Uid normalizer Contributed by guillbdx and norkunas in #36406 and #38151 /* * * @ORM\Entit y * / class Produc t { /* * * @ORM\Column(type="uuid" ) * / private $id ; } Serializer $product = new Product() ; $jsonContent = $serializer->serialize($product, 'json') ; // $jsonContent contains {"id":"9b7541de-6f87-11ea-ab3c-9da9a81562fc","...":"..." }

Slide 27

Slide 27 text

Ulid validation Contributed by laurent35240 in #38322 /* * * @ORM\Entit y * / class Produc t { /* * * @ORM\Column(type="ulid" ) * @Assert\Uli d * / private $id ; } Validator

Slide 28

Slide 28 text

Translatable objects Contributed by natewiebe13 in #37670 // Befor e return $this->render('...', [ 'order_status' => [ 'message' => 'order.status_message' , 'params' => ['%status%' => $order->getStatus(), '%order_id%' => $order->getId()] , 'domain' => 'admin' , ] , ]); Translation {# Before # } {{ order_status.message|trans(order_status.params, order_status.domain) }}

Slide 29

Slide 29 text

Translatable objects Contributed by natewiebe13 in #37670 // Afte r use Symfony\Component\Translation\TranslatableMessage; return $this->render('...', [ 'order_status' => new TranslatableMessage ( 'order.status_message' , ['%status%' => $order->getStatus(), '%order_id%' => $order->getId()] , 'admin ' ) , ]); Translation {# After # } {{ order_status|trans }}

Slide 30

Slide 30 text

Translatable objects Contributed by natewiebe13 in #37670 {# Before #} {{ message is defined ? message|trans : fallback|trans({'%param%': value}) }} {# After #} {{ message|default(t(fallback, {'%param%': value}))|trans }} Translation

Slide 31

Slide 31 text

Session pro f i ling Contributed by mtarld in #36364 WebPro f i lerBundle

Slide 32

Slide 32 text

Noti f i er improvements Contributed by jschaedl in #36479 WebPro f i lerBundle

Slide 33

Slide 33 text

Simpler DataCollectors Contributed by lvo in #37332 # config/services.yam l services : App\DataCollector\MyCustomDataCollector : tags : - name: data_collecto r template: 'data_collector/template.html.twig ' id: 'app.my_custom_collector ' use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface ; class MyCustomDataCollector implements DataCollectorInterfac e { // ... } WebPro f i lerBundle

Slide 34

Slide 34 text

Simpler DataCollectors Contributed by lvo in #37332 use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector ; class MyCustomDataCollector extends AbstractDataCollecto r { public function collect(Request $request, Response $response, \Throwable $exception = null): voi d { $this->data = '...' ; } public static function getTemplate(): ?strin g { return 'data_collector/template.html.twig' ; } public function getName(): strin g { return 'app.my_custom_collector' ; } } WebPro f i lerBundle

Slide 35

Slide 35 text

New integrations Noti f i er • Infobip SMS in PR #36480 provided by Jérémy Romey • Google Chat in PR #36488 provided by Jérôme Tamarelle • Esendex SMS in PR #36573 provided by Olivier Dolbeau • Zulip Chat in PR #36616 provided by Mohammad Emran Hasan • Mobyt SMS in PR #36648 provided by Bastien Durand • SMSAPI SMS in PR #36940 provided by Marcin Szepczynski • LinkedIn in PR #37830 provided by Smaine Milianni • Sendinblue SMS in PR #38298 provided by Pierre Tondereau • Discord Chat in PR #38522 provided by Karoly Gossler and Mathieu Piot • Improved Telegram Chat to allow de f i ne some options in PR #36496 provided by Mihail Krasilnikov

Slide 36

Slide 36 text

DKIM email authentication Contributed by fabpot in #37165 use Symfony\Component\Mime\Crypto\DkimSigner ; use Symfony\Component\Mime\Email ; $email = (new Email() ) ->from('[email protected]' ) // .. . ->html('...') ; $signer = new DkimSigner('file:///path/to/private-key.key', 'example.com', 'sf') ; $signedEmail = $signer->sign($email) ; /** @var /Symfony\Component\Mailer\MailerInterface $mailer * / $mailer->send($signedEmail); Mailer

Slide 37

Slide 37 text

Retryable HTTP client Contributed by fabpot in #37165 framework : http_client : retry_failed : # only retry errors with these HTTP code s http_codes: [429, 500 ] max_retries: 2 # waiting time between retries (in milliseconds ) delay: 100 0 # if set, the waiting time of each retry increases by this facto r # (e.g. first retry: 1000ms; second retry: 3 * 1000ms; etc. ) multiplier: 3 HttpClient framework : http_client : retry_failed: true # 423, 425, 429, 500, 502, 503, 504, 507, 510

Slide 38

Slide 38 text

Form mapping callbacks Contributed by yonelceruto in #37968 class PersonType extends AbstractTyp e { public function buildForm(FormBuilderInterface $builder, array $options ) { $builde r ->add('name', TextType::class, [ 'getter' => function (Person $person, FormInterface $form): string { return $person->getUserData()->getFirstName() ; } , 'setter' => function (Person &$person, ?string $name, FormInterface $form): void { $person->rename($name) ; } , ] ) ; } } Form

Slide 39

Slide 39 text

Form testing asserts Contributed by mnapoli in #38287 and #38288 // Befor e $view = $this->factory->create(TestedType::class, $formData)->createView() ; $this->assertArrayHasKey('custom_var', $view->vars) ; $this->assertSame('expected value', $view->vars[‚custom_var‘]) ; // Afte r class SomeTest extends WebTestCas e { public function testIndex(): voi d { $client = static::createClient() ; $client->request('GET', '/some-page') ; $client->submitForm('Save', [ 'activateMembership' => 'on' , 'trialPeriod' => '7' , ]) ; self::assertFormValue('#form', 'trialPeriod', '7') ; self::assertCheckboxChecked('activateMembership') ; } } PHPUnit Bridge

Slide 40

Slide 40 text

Shared locks Contributed by jderusse in #37752 use Symfony\Component\Lock\LockFactory ; use Symfony\Component\Lock\Store\InMemoryStore ; $factory = new LockFactory(new InMemoryStore()) ; $lock1 = $factory->createLock('test') ; $lock2 = $factory->createLock('test') ; $lock1->acquire(); // tru e $lock2->acquire(); // fals e $lock1->release() ; $lock2->acquire(); // true Lock

Slide 41

Slide 41 text

Shared locks Contributed by jderusse in #37752 use Symfony\Component\Lock\LockFactory ; use Symfony\Component\Lock\Store\InMemoryStore ; $factory = new LockFactory(new InMemoryStore()) ; $lock1 = $factory->createLock('test') ; $lock2 = $factory->createLock('test') ; $lock3 = $factory->createLock('test') ; $lock1->acquireRead(); // tru e $lock2->acquireRead(); // tru e $lock3->acquire(); // false Lock

Slide 42

Slide 42 text

Shared locks Contributed by jderusse in #37752 use Symfony\Component\Lock\LockFactory ; use Symfony\Component\Lock\Store\InMemoryStore ; $factory = new LockFactory(new InMemoryStore()) ; $lock1 = $factory->createLock('test') ; $lock2 = $factory->createLock('test') ; $lock3 = $factory->createLock('test') ; $lock1->acquire(); // tru e $lock2->acquire(); // fals e $lock3->acquireRead(); // false Lock

Slide 43

Slide 43 text

Shared locks Contributed by jderusse in #37752 Lock use Symfony\Component\Lock\LockFactory ; use Symfony\Component\Lock\Store\SemaphoreStore ; $factory = new LockFactory(new SemaphoreStore()) ; $lock1 = $factory->createLock('test') ; $lock2 = $factory->createLock('test') ; $lock3 = $factory->createLock('test') ; $lock1->acquireRead(); // tru e $lock2->acquireRead(); // fals e $lock3->acquire(); // false

Slide 44

Slide 44 text

Front controller con f i guration Contributed by nicolas-grekas in #37351 FrameworkBundle // public/index.ph p use App\CacheKernel ; use App\Kernel ; // .. . $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']) ; if ('prod' === $kernel->getEnvironment()) { $kernel = new CacheKernel($kernel) ; } $request = Request::createFromGlobals() ; // ...

Slide 45

Slide 45 text

Front controller con f i guration Contributed by nicolas-grekas in #37351 FrameworkBundle # config/packages/framework.yam l framework : # use the HTTP Cache default s http_cache: tru e # configure every HTTP Cache optio n http_cache : private_headers: ['Authorization', 'Cookie', 'MyCustomHeader' ] default_ttl: 360 0 allow_revalidate: tru e stale_if_error: 600

Slide 46

Slide 46 text

Front controller con f i guration Contributed by nicolas-grekas in #37357 FrameworkBundle // public/index.ph p // .. . if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) { Request::setTrustedProxies ( explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOS T ) ; } if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) { Request::setTrustedHosts([$trustedHosts]) ; } $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']) ; // ...

Slide 47

Slide 47 text

Front controller con f i guration Contributed by nicolas-grekas in #37357 FrameworkBundle # config/packages/framework.yam l framework: # configure proxies to trust directly in the config file : trusted_proxies: '127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 ' # or use an env var if this value is dynami c trusted_proxies: '%env(TRUSTED_PROXIES)% ' # you can also define the trusted header s trusted_headers: ['x-forwarded-all', '!x-forwarded-host', '!x-forwarded-prefix']

Slide 48

Slide 48 text

Login links Contributed by weaverryan in #38177 Security

Slide 49

Slide 49 text

Login links Contributed by weaverryan in #38177 Security security : enable_authenticator_manager: true firewalls: main: login_link : check_route: 'magic_link_verify ' signature_properties: [id, password, email ] lifetime: 600 # optional (default=600 ) max_uses: 3 # optional (default=null ) used_link_cache: cache.app # Cache service id used if max_uses is set # Generated URL looks something like this : # https://127.0.0.1:9033/login/verify? [email protected]&expires=1601342578&hash=YzE1ZDJlYjM3YTMyMjgwZDdkYzg2ZjFlMjZhN2E5ZWRmM zk3NjAxNjRjYThiMjMzNmIxYzAzYzQ4NmQ2Zjk4NA%3D%3D

Slide 50

Slide 50 text

class MagicLinkLoginController extends AbstractControlle r { /* * * @Route("/login", name="magic_link_login" ) * / public function requestMagicLink(Request $request, LoginLinkHandlerInterface $loginLinkHandler, UserRepository $userRepository, AuthenticationUtils $authenticationUtils ) { if ($request->isMethod('POST')) { $email = $request->request->get('email') ; $user = $userRepository->findOneBy(['email' => $email]) ; if ($user) { $magicLink = $loginLinkHandler->createLoginLink($user) ; // TODO: in a real app, use notifier to email thi s dump($magicLink->getUrl()) ; } return $this->redirectToRoute('magic_link_check_email') ; } return $this->render('magic_link/login.html.twig', [ 'error' => $authenticationUtils->getLastAuthenticationError( ) ]) ; } /* * * @Route("/login/check-email", name="magic_link_check_email" ) * / public function magicLinkCheckEmail( ) { return $this->render('magic_link/check_email.html.twig') ; } /* * * @Route("/login/verify", name="magic_link_verify" ) * / public function checkMagicLink( ) { throw new \Exception('will be handled by authenticator') ; Security

Slide 51

Slide 51 text

Login Throttling Contributed by wouterj in #38204 Security security : firewalls : default : # by default, the feature allows 5 login attempts per minut e login_throttling: ~ # configuring the maximum login attempts (per minute ) login_throttling : max_attempts: 1 # you can even use a custom rate limiter via its service I D login_throttling : limiter: app.my_login_rate_limite r

Slide 52

Slide 52 text

PHP 8

Slide 53

Slide 53 text

PHP 8 attributes Contributed by derrabus in #37474 and #37545 PHP 8 // Befor e use Symfony\Component\Routing\Annotation\Route ; class SomeControlle r { /* * * @Route("/path", name="action" ) * / public function someAction( ) { // .. . } } // Afte r use Symfony\Component\Routing\Annotation\Route ; class SomeControlle r { #[Route(‚/path', name: 'action')] public function someAction( ) { // .. . } }

Slide 54

Slide 54 text

PHP 8 attributes Contributed by derrabus in #37474 and #37545 PHP 8 use Symfony\Contracts\Service\Attribute\Required ; class SomeServic e { /** @Required */ #[Required ] public Bar $bar ; /** @Required */ #[Required ] public function setFoo(Foo $foo): voi d { // .. . } }

Slide 55

Slide 55 text

Controller argument attributes Contributed by jvasseur in #37829 PHP 8 use App\Entity\MyUser ; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController ; use Symfony\Component\Security\Http\Attribute\CurrentUser ; class SomeController extends AbstractControlle r { public function index(#[CurrentUser] MyUser $user) { // .. . } }

Slide 56

Slide 56 text

Constraints as PHP attributes Contributed by derrabus in #38309 and #38499 PHP 8 // Befor e use Symfony\Component\Validator\Constraints as Assert ; class Autho r { /* * * @Assert\Choice ( * choices = { "fiction", "non-fiction" } , * message = "Choose a valid genre. " * ) * / private $genre ; } // Afte r use Symfony\Component\Validator\Constraints as Assert ; class Autho r { #[Assert\Choice ( choices: ['fiction', 'non-fiction'] , message: 'Choose a valid genre.' , )] private $genre ; } Join the discussion for an alternative of nested attributes in #38503.

Slide 57

Slide 57 text

New Components

Slide 58

Slide 58 text

Semaphore Component Symfony 5.2

Slide 59

Slide 59 text

RateLimiter Component Symfony 5.2 experimental

Slide 60

Slide 60 text

There is even more https://github.com/symfony/symfony/blob/5.x/UPGRADE-5.2.md

Slide 61

Slide 61 text

Questions?

Slide 62

Slide 62 text

Thank you! https://speakerdeck.com/malteschlueter