9
Evolution, pas Révolution
Symfony 5 est proche
de Symfony 4
Slide 10
Slide 10 text
10
Mais Symfony 5, c’est
aussi de nouvelles
fonctionnalités
Slide 11
Slide 11 text
11
La démarche : rendre
Symfony plus simple,
agréable et productif
Slide 12
Slide 12 text
12
Comment ?
DX, DX, DX
Slide 13
Slide 13 text
13
Symfony 5 reprend les
bases de Symfony 4 et
y intègre de nouveaux
composants
Slide 14
Slide 14 text
14
L’objectif ?
Construire des abstractions
haut-niveau au dessus de
nos abstractions bas-niveau
Slide 15
Slide 15 text
15
2.
Messenger
Mercure
Slide 16
Slide 16 text
16
L’asynchrone,
c’est génial
Slide 17
Slide 17 text
17
Mais en fait,
pourquoi ?
Slide 18
Slide 18 text
18
Depuis l’invention
de PHP, deux modèles
s’opposent
Slide 19
Slide 19 text
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
Slide 20
Slide 20 text
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
Slide 21
Slide 21 text
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
Slide 22
Slide 22 text
22
Et si on faisait les deux ?
Slide 23
Slide 23 text
23
L’asynchrone n’est pas
utile partout
Slide 24
Slide 24 text
24
Les consumers
Slide 25
Slide 25 text
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
Slide 26
Slide 26 text
26
Envoyer des e-mails
Générer des miniatures
Synchroniser avec une API
...
Slide 27
Slide 27 text
27
Messenger
=
Une librairie pour créer et
utiliser des consumers
Slide 28
Slide 28 text
28
composer require symfony/messenger
Slide 29
Slide 29 text
29
Process
PHP
Requête 1
Consumer
PHP
RabbitMQ,
Redis,
PDO,
...
Consumer
PHP
Process
PHP
Requête 1
Process
PHP
Requête 1
...
Slide 30
Slide 30 text
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
Slide 31
Slide 31 text
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
Slide 32
Slide 32 text
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
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
Slide 37
Slide 37 text
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)
Slide 38
Slide 38 text
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
42
Un binaire en Go
Transmet les messages
de votre consumer à vos clients
Slide 43
Slide 43 text
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
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;
}
Slide 62
Slide 62 text
62
Asynchrone ?
HTTP 2/3 ? Push ?
Streams ?
Options et erreurs de
transport ?
Slide 63
Slide 63 text
63
Non-défini dans les
interfaces
=
Inutilisable
Slide 64
Slide 64 text
64
Peut-on améliorer la
situation ?
Slide 65
Slide 65 text
65
composer require symfony/http-client
Slide 66
Slide 66 text
66
Fournit des
décorateurs pour tous les
packages virtuels
Slide 67
Slide 67 text
67
Symfony HttpClient
=
Un contrat
=
Une interface
Slide 68
Slide 68 text
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;
}
Slide 69
Slide 69 text
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;
}
81
Grâce aux abstractions
bas-niveau, on peut faire
beaucoup plus de choses
en moins de code
Slide 82
Slide 82 text
82
composer require symfony/mailer
Slide 83
Slide 83 text
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('
See Twig integration for better HTML integration!
');
$mailer->send($email);
// ...
}
}
Slide 84
Slide 84 text
84
Dotenv
Configurer le Mailer
différemment selon
l’environnement
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'
Slide 88
Slide 88 text
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
Slide 89
Slide 89 text
89
Twig
Des e-mails riches,
responsives et compatibles
avec tous les lecteurs
Slide 90
Slide 90 text
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',
])
;
Slide 91
Slide 91 text
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 #}
100
Reprend les principes de
Mailer et les adapte à
d’autres méthodes d’envoi
Slide 101
Slide 101 text
101
class SmsController extends AbstractController
{
public function sendSms(TexterInterface $texter)
{
$sms = new SmsMessage('+33606060606', 'New subscription started!');
$texter->send($sms);
// ...
}
}
Slide 102
Slide 102 text
102
class SmsController extends AbstractController
{
public function sendSms(TexterInterface $texter)
{
$sms = new SmsMessage('+33606060606', 'New subscription started!');
$texter->send($sms);
// ...
}
}
DSN : Twilio,
nextmo, ...
Slide 103
Slide 103 text
103
class ChatController extends AbstractController
{
public function sendMessage(ChatterInterface $chatter)
{
$message = new ChatMessage('New subscription started!');
$chatter->send($message);
// ...
}
}
Slide 104
Slide 104 text
104
class ChatController extends AbstractController
{
public function sendMessage(ChatterInterface $chatter)
{
$message = new ChatMessage('New subscription started!');
$chatter->send($message);
// ...
}
}
DSN : Slack,
Telegram, ...
Slide 105
Slide 105 text
105
Une même infrastructure
Abstraction
haut-niveau
(MailerInterface,
TexterInterface,
ChatterInterface, …)
Votre
code
Messenger
Twig
Transport
(Dotenv,
HttpClient)
Slide 106
Slide 106 text
106
Une même infrastructure
Abstraction
haut-niveau
(MailerInterface,
TexterInterface,
ChatterInterface, …)
Votre
code
Messenger
Twig
Transport
(Dotenv,
HttpClient)
Notifier
Slide 107
Slide 107 text
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]')
);
// ...
}
}
Slide 108
Slide 108 text
108
5.
Autres
nouveautés
Slide 109
Slide 109 text
109
Composant String
Slide 110
Slide 110 text
110
composer require symfony/string
Slide 111
Slide 111 text
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('नमस्ते')) {
// ...
}
Slide 112
Slide 112 text
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('नमस्ते')) {
// ...
}
Slide 113
Slide 113 text
113
Locale-aware slugger
Slide 114
Slide 114 text
114
class SlugController extends AbstractController
{
public function index(SluggerInterface $slugger)
{
$slug = $slugger->slug('Стойността трябва да бъде
лъжа');
// ...
}
}
Slide 115
Slide 115 text
115
class SlugController extends AbstractController
{
public function index(SluggerInterface $slugger)
{
$slug = $slugger->slug('Стойността трябва да бъде
лъжа');
// ...
}
} Transliteration
Basé sur la locale de la requête
Slide 116
Slide 116 text
116
Et bien d’autres !
https://symfony.com/blog/category/living-on-the-edge
Slide 117
Slide 117 text
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 !
Slide 118
Slide 118 text
Merci !
118
Des questions ?
▪ @titouangalopin
sur Twitter
▪ titouan.galopin
@symfony.com