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

Symfony 5

Symfony 5

Titouan Galopin

November 13, 2019
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. 71 /** * @throws TransportExceptionInterface * When a network error

    occurs * * @throws RedirectionExceptionInterface * On a 3xx when $throw is true and the * "max_redirects" option has been reached * * @throws ClientExceptionInterface * On a 4xx when $throw is true * * @throws ServerExceptionInterface * On a 5xx when $throw is true */ Comportement en cas d’erreur du transport défini dans l’interface
  26. 72 Options du transport défini dans l’interface : créer des

    décorateurs réellement découplés est possible
  27. 74 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
  28. 76 $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()); }
  29. 78 4 décorateurs natifs • ScopingHttpClient Configuration d’options par défaut

    par domaine • MockHttpClient Client qui n’effectue pas les requêtes • CachingHttpClient Ajoute la gestion du cache HTTP • HttplugClient / Psr18Client Pour la compatibilité avec les packages virtuels
  30. 81 framework: http_client: max_host_connections: 4 default_options: # ... scoped_clients: github_client:

    base_uri: https://api.github.com headers: Authorization: token abc123 # creates the HttpClientInterface $githubClient autowiring alias # and the HttpClientInterface $scopingHttpClient one
  31. 87 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); // ... } }
  32. 90 // 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 // …
  33. 91 // 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'
  34. 92 // 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
  35. 94 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', ]) ;
  36. 95 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">
  37. 96 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 %}
  38. 97 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 %}
  39. 105 class SmsController extends AbstractController { public function sendSms(TexterInterface $texter)

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

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

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

    { $message = new ChatMessage('New subscription started!'); $chatter->send($message); // ... } } DSN : Slack, Telegram, ...
  43. 111 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]') ); // ... } }
  44. 115 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('नमस्ते')) { // ... }
  45. 116 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('नमस्ते')) { // ... }
  46. 118 class SlugController extends AbstractController { public function index(SluggerInterface $slugger)

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

    { $slug = $slugger->slug('Стойността трябва да бъде лъжа'); // ... } } Transliteration Basé sur la locale de la requête
  48. 121 Avant # config/packages/security.yaml security: # ... encoders: App\Entity\User: algorithm:

    'bcrypt' Après # config/packages/security.yaml security: # ... encoders: App\Entity\User: algorithm: 'auto'