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

Symfony 5 - AFUP Day Lille 2020

Symfony 5 - AFUP Day Lille 2020

Comme tous les deux ans, novembre 2019 a été l'occasion d'une nouvelle version majeure de Symfony. Cette version 5 est particulièrement intéressante de part la démarche qui l'anime : mieux résoudre les problèmes des développeurs et développeuses, en créant des librairies haut-niveau basées sur les fondations solides de Symfony 4. Je vous propose de découvrir ou redécouvrir ensemble les nouveautés apparues dans Symfony depuis la version 4.0 (Messenger, HttpClient, Mailer, Notifier, String, ...) et en quoi ces nouveautés changent la façon dont nous avons de concevoir le futur de Symfony.

Titouan Galopin

June 26, 2020
Tweet

More Decks by Titouan Galopin

Other Decks in Programming

Transcript

  1. Agenda 1. Symfony 5 2. Messenger / Mercure 3. Mailer

    / Notifier 4. HttpClient 5. Autres nouveautés 3
  2. 5

  3. 13 Symfony 5 reprend les bases de Symfony 4 et

    y intègre de nouveaux composants
  4. 19 Java, node, … Un process réutilisé Process Java, node,

    ... Requête 1 PHP Un process par reqûete Évolution du temps Requête 2 Requête 3 Requête 4 ... Process PHP Requête 1 Requête 2 Requête 3 Requête 4 ... Process PHP Process PHP Process PHP
  5. 20 Java, node, … Un process réutilisé PHP Un process

    par reqûete État partagé = Plus performant Sans état = Plus facile à maintenir et à développer
  6. 21 Java, node, … Un process réutilisé PHP Un process

    par reqûete État partagé = Plus performant Sans état = Plus facile à maintenir et à développer Avantage technique Avantage humain
  7. 25 PHP Un process par reqûete ... + un traitement

    en arrière-plan quand c’est utile Process PHP Requête 1 Requête 2 Requête 3 Process PHP Process PHP Process Consumer PHP
  8. 29 Process PHP Requête 1 Consumer PHP RabbitMQ, Redis, PDO,

    ... Consumer PHP Process PHP Requête 1 Process PHP Requête 1 ...
  9. 30 Process PHP Requête 1 Consumer PHP RabbitMQ, Redis, PDO,

    ... Consumer PHP Process PHP Requête 1 Process PHP Requête 1 ... Messenger utilise un intermédiaire de traitement des messages
  10. 31 Process PHP Requête 1 Consumer PHP RabbitMQ, Redis, PDO,

    ... Consumer PHP Process PHP Requête 1 Process PHP Requête 1 ... Messenger vous aide à créer des Messages depuis vos process HTTP
  11. 32 Process PHP Requête 1 Consumer PHP RabbitMQ, Redis, PDO,

    ... Consumer PHP Process PHP Requête 1 Process PHP Requête 1 ... Et à traiter ces Messages grâce à des Handlers utilisés par les Consumers
  12. 33 framework: messenger: failure_transport: failed transports: async: dsn: '%env(MESSENGER_TRANSPORT_DSN)%' retry_strategy:

    max_retries: 3 delay: 1000 multiplier: 2 failed: 'doctrine://default?queue_name=failed' routing: 'Symfony\Component\Mailer\Messenger\SendEmailMessage': async 'App\Messenger\Mailchimp\MailchimpSynchronizeMessage': async
  13. 34 framework: messenger: failure_transport: failed transports: async: dsn: '%env(MESSENGER_TRANSPORT_DSN)%' retry_strategy:

    max_retries: 3 delay: 1000 multiplier: 2 failed: 'doctrine://default?queue_name=failed' routing: 'Symfony\Component\Mailer\Messenger\SendEmailMessage': async 'App\Messenger\Mailchimp\MailchimpSynchronizeMessage': async Utiliser de l’asynchrone pour ces messages (Messenger peut aussi fonctionner en synchrone)
  14. 35 framework: messenger: failure_transport: failed transports: async: dsn: '%env(MESSENGER_TRANSPORT_DSN)%' retry_strategy:

    max_retries: 3 delay: 1000 multiplier: 2 failed: 'doctrine://default?queue_name=failed' routing: 'Symfony\Component\Mailer\Messenger\SendEmailMessage': async 'App\Messenger\Mailchimp\MailchimpSynchronizeMessage': async Réessayer 3 fois (1 seconde, 2 secondes et 4 secondes plus tard)
  15. 36 framework: messenger: failure_transport: failed transports: async: dsn: '%env(MESSENGER_TRANSPORT_DSN)%' retry_strategy:

    max_retries: 3 delay: 1000 multiplier: 2 failed: 'doctrine://default?queue_name=failed' routing: 'Symfony\Component\Mailer\Messenger\SendEmailMessage': async 'App\Messenger\Mailchimp\MailchimpSynchronizeMessage': async Si un message ne fonctionne finalement pas, le stocker dans une table
  16. 37 public function index(MessageBusInterface $bus) { // ... $this->bus->dispatch(new MailchimpAddEmailMessage($email));

    } Votre contrôleur N’importe quel objet créé par vous (et contenant donc toutes les informations que vous voulez)
  17. 38 use Symfony\Component\Messenger\Handler\MessageHandlerInterface; class MailchimpSynchronizeHandler implements MessageHandlerInterface { public function

    __invoke(MailchimpAddEmailMessage $message) { // ... } } Votre handler Le handler à utiliser est déterminé grâce au typehint du message Le handler est enregistré grâce à l’auto-configuration
  18. 43 Process PHP Client Consumer PHP Envoi de l’e-mail RabbitMQ,

    Redis, PDO, ... Réponse simple Message d’e-mail Message d’e-mail Message Mercure Process Mercure Message Mercure Requête initiale Connexion W S à M ercure Message Mercure
  19. 51 $url = 'https://symfony.com/versions.json'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url);

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $versions = json_decode(curl_exec($ch), true); curl_close($ch);
  20. 60 PSR18 $url = 'https://symfony.com/versions.json'; $request = $client->createRequest('GET', $url); $response

    = $client->sendRequest($request); $versions = json_decode( $response->getBody()->getContents(), true );
  21. 61 interface ClientInterface { /** * Sends a PSR-7 request

    and returns a PSR-7 response. * * @param RequestInterface $request * * @return ResponseInterface * * @throws \Psr\Http\Client\ClientExceptionInterface If an error happens * while processing the request. */ public function sendRequest(RequestInterface $request): ResponseInterface; }
  22. 62 Asynchrone ? HTTP 2/3 ? Push ? Streams ?

    Options et erreurs de transport ?
  23. 68 interface HttpClientInterface { public const OPTIONS_DEFAULTS = [...]; public

    function request( string $method, string $url, array $options = [] ): ResponseInterface; public function stream( $responses, float $timeout = null ): ResponseStreamInterface; }
  24. 69 interface ResponseInterface { public function getStatusCode(): int; public function

    getHeaders($throw = true): array; public function getContent($throw = true): string; public function toArray($throw = true): array; public function cancel(): void; public function getInfo(): array; }
  25. 72 use Symfony\Component\HttpClient\HttpClient; $client = HttpClient::create(); $responses = []; for

    ($i = 1; $i <= 100; $i++) { $responses[] = $client->request('GET', 'https://example.com/?p='.$i); } foreach ($responses as $response) { // block until completion of $response // but monitor all other responses meanwhile echo $response->getContent(); } ResponseInterface lazy
  26. 74 $url = 'http://.../ubuntu-18.04.1-desktop-amd64.iso'; $response = $client->request('GET', $url); // Responses

    are lazy! $h = fopen('./ubuntu.iso', 'wb'); foreach ($client->stream($response) as $chunk) { fwrite($h, $chunk->getContent()); }
  27. 83 class MailerController extends AbstractController { public function sendEmail(MailerInterface $mailer)

    { $email = (new Email()) ->from('[email protected]') ->to('[email protected]') ->subject('Time for Symfony Mailer!') ->text('Sending emails is fun again!') ->html('<p>See Twig integration for better HTML integration!</p>'); $mailer->send($email); // ... } }
  28. 86 // SMTP MAILER_DSN=smtp://user:[email protected] // Sendgrid // composer require symfony/sendgrid-mailer

    MAILER_DSN=smtp://$SENDGRID_KEY@sendgrid // Gmail // composer require symfony/google-mailer MAILER_DSN=smtp://$USERNAME:$PASSWORD@gmail // …
  29. 87 // SMTP MAILER_DSN=smtp://user:[email protected] // Sendgrid // composer require symfony/sendgrid-mailer

    MAILER_DSN=smtp://$SENDGRID_KEY@sendgrid // Gmail // composer require symfony/google-mailer MAILER_DSN=smtp://$USERNAME:$PASSWORD@gmail // … // High Availability: si le premier a une erreur, utiliser le deuxième MAILER_DSN='api://id@postmark || smtp://key@sendgrid' // Load Balancing: répartir les mails sur chaque provider MAILER_DSN='api://id@postmark && smtp://key@sendgrid'
  30. 88 // SMTP MAILER_DSN=smtp://user:[email protected] // Sendgrid // composer require symfony/sendgrid-mailer

    MAILER_DSN=smtp://$SENDGRID_KEY@sendgrid // Gmail // composer require symfony/google-mailer MAILER_DSN=smtp://$USERNAME:$PASSWORD@gmail // … // High Availability: si le premier a une erreur, utiliser le deuxième MAILER_DSN='api://id@postmark || smtp://key@sendgrid' // Load Balancing: répartir les mails sur chaque provider MAILER_DSN='api://id@postmark && smtp://key@sendgrid' HttpClient
  31. 90 use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Component\Mime\Address; $email = (new TemplatedEmail()) ->from('[email protected]')

    ->to(new Address('[email protected]')) ->subject('Thanks for signing up!') // path of the Twig template to render ->htmlTemplate('emails/signup.html.twig') // pass variables (name => value) to the template ->context([ 'expiration_date' => new \DateTime('+7 days'), 'username' => 'foo', ]) ;
  32. 91 Embarquer les images # config/packages/twig.yaml twig: paths: # point

    this wherever your images live '%kernel.project_dir%/assets/images': images {# '@images/' refers to the Twig namespace #} <img src="{{ email.image('@images/logo.png') }}" alt="Logo">
  33. 92 Inliner le CSS composer require twig/extra-bundle twig/cssinliner-extra # config/packages/twig.yaml

    twig: paths: '%kernel.project_dir%/assets/css: css {% apply inline_css(source('@css/email.css')) %} <h1>Welcome {{ username }}!</h1> {# ... #} {% endapply %}
  34. 93 Inky : responsive et cross-platform composer require twig/extra-bundle twig/inky-extra

    {% apply inky_to_html %} <container> <row class="header"> <columns> <spacer size="16"></spacer> <h1 class="text-center">Welcome {{ email.toName }}!</h1> </columns> {# ... #} </row> </container> {% endapply %}
  35. 101 class SmsController extends AbstractController { public function sendSms(TexterInterface $texter)

    { $sms = new SmsMessage('+33606060606', 'New subscription started!'); $texter->send($sms); // ... } }
  36. 102 class SmsController extends AbstractController { public function sendSms(TexterInterface $texter)

    { $sms = new SmsMessage('+33606060606', 'New subscription started!'); $texter->send($sms); // ... } } DSN : Twilio, nextmo, ...
  37. 103 class ChatController extends AbstractController { public function sendMessage(ChatterInterface $chatter)

    { $message = new ChatMessage('New subscription started!'); $chatter->send($message); // ... } }
  38. 104 class ChatController extends AbstractController { public function sendMessage(ChatterInterface $chatter)

    { $message = new ChatMessage('New subscription started!'); $chatter->send($message); // ... } } DSN : Slack, Telegram, ...
  39. 107 class NotificationController extends AbstractController { public function send(NotifierInterface $notifier)

    { $notification = new Notification( 'New subscription started!', ['sms', 'chat/slack', 'email'] ); $notifier->send( $notification, new Receiver('[email protected]') ); // ... } }
  40. 111 use Symfony\Component\String\UnicodeString; $text = (new UnicodeString('This is a déjà-vu

    situation.')) ->trimEnd('.') ->replace('déjà-vu', 'jamais-vu') ->append('!'); // $text = 'This is a jamais-vu situation!' $content = new UnicodeString('नमस्ते दु नया'); if ($content->ignoreCase()->startsWith('नमस्ते')) { // ... }
  41. 112 use Symfony\Component\String\UnicodeString; $text = u('This is a déjà-vu situation.')

    ->trimEnd('.') ->replace('déjà-vu', 'jamais-vu') ->append('!'); // $text = 'This is a jamais-vu situation!' $content = u('नमस्ते दु नया'); if ($content->ignoreCase()->startsWith('नमस्ते')) { // ... }
  42. 114 class SlugController extends AbstractController { public function index(SluggerInterface $slugger)

    { $slug = $slugger->slug('Стойността трябва да бъде лъжа'); // ... } }
  43. 115 class SlugController extends AbstractController { public function index(SluggerInterface $slugger)

    { $slug = $slugger->slug('Стойността трябва да бъде лъжа'); // ... } } Transliteration Basé sur la locale de la requête
  44. insight.symfony.com // [email protected] Jeudi 9 juillet Mettre en place des

    process qualité avec Symfony Partons à la découverte des process qualité de Symfony en tant que framework pour les appliquer à nos propres applications et ainsi faciliter leur maintenance. Rendez-vous sur @symfonyinsight (Twitter) pour vous inscrire !