SlideShare une entreprise Scribd logo
1  sur  43
SOLID : LES PRINCIPES
À L’ORIGINE DU SUCCÈS DE SYMFONY
ET DE VOS APPLICATIONS
https://vria.eu
contact@vria.eu
https://twitter.com/RiaVlad
RIABCHENKO Vladyslav
5+ ans full stack web-développeur
Certifié Symfony
Architecte technique à Webnet
Symfony
SOLID
Responsabilité unique
Ouvert / fermé
Substitution de Liskov
Ségrégation des interfaces
Inversion de dépendances
Plan
3
1.
2.
S.
O.
L.
I.
D.
Symfony framework
Symfony
2. Framework PHP pour projets web
1. Ensemble de composants PHP
MVC Pattern
Model View
Requête HTTP Routage Contrôleur Réponse HTTP
5
Symfony
HttpKernel
DependencyInjection
HttpFoundation
Routing
EventDispatcher
Cœur du framework qui gère le cycle de requête-réponse HTTP
Couche orientée objet aux spécifications HTTP
Mapper les requêtes aux variables (contrôleur, etc.)
Médiateur permettant aux composants découplés de
communiquer à l’aide d’événements
Conteneur de service qui instancie des services en se basant sur
la configuration et injecte leurs dépendances
et 45 autres, y compris Security, Form, Console, Cache, Asset, Validator, Serializer, etc.
6
Symfony
FrameworkBundle
Configure les composants Symfony et les colle ensemble.
Il enregistre les services et les listeners d'événements.
Les bundles sont des plugins réutilisables qui fournissent des services, des routes, des
contrôleurs, des templates, etc.
Your code
Fournit des routes, des contrôleurs, des services, une logique
métier et des templates adaptés à vos besoins.
doctrine/orm DBAL et ORM pour communiquer avec des bases de données.
twig/twig Moteur de template.
7
Principes SOLID
SOLID
– Responsabilité unique
– Ouvert / fermé
– Substitution de Liskov
– Ségrégation des interfaces
– Inversion des dépendances
Réutilisable
Flexible
Évolutif
Compréhensible
Fragile
Dupliqué
Visqueux
Complexe sans raison
SOLID aide à créer le code qui est … et à éviter le code qui est
S
O
L
I
D
9
Principe de responsabilité unique
class ReportService
{
public function createReport($data)
{
// working with report manager at domain layer
}
public function getLastThreeReportsByAuthor($author)
{
// working with database at ORM or DBAL layer
}
public function searchBy($criteria)
{
// working with database at ORM or DBAL layer
}
public function export(Report $report)
{
// working with filesystem at by means of PHP functions
}
}
Une classe ne devrait avoir qu'une seule responsabilité.
La responsabilité est une raison de modifier la classe.
Single responsibility principle
11
// working with database at ORM and/or DBAL layer
class ReportRepository
{
public function getLastThreeReportsByAuthor($author)
{ ... }
public function searchBy($criteria)
{ ... }
}
// working with report manager at domain layer (business logic)
class ReportManager
{
public function createReport($data)
{ ... }
}
// working with filesystem and pdf, excel, csv, etc.
class ReportExporter
{
public function export(Report $report)
{ ... }
}
Manager
Repository
Exporter
Single responsibility principle
12
Feature : Stocker les utilisateurs dans la base de données
Feature : Stocker les commandes dans la base de données
Feature : Authentifier des utilisateurs (formulaire de connexion, HTTP, peu importe …),
stocker les informations d'identification dans la session et authentifier chaque
requête ultérieure
Feature : Lors de la création de la commande,
affecter automatiquement cette commande à un utilisateur connecté
Single responsibility principle
13
SecurityContext
Stocke le token d’utilisateur setToken($token), getToken()
Vérifie l’autorisation isGranted('attr', $sub)
AuthenticationProvider Authentifie le token
UserProvider Récupère ou rafraîchit l'utilisateur
EntityManager Toutes les interactions avec la base de données, ORM
OrderListener Notre listener qui modifie la commande avant qu'il ne soit inséré
L’injection du service SecurityContext dans n’importe quel listener
d’EntityManager crée une dépendance circulaire.
Single responsibility principle
14
Single responsibility principle
La solution est diviser SecurityContext en AuthorizationChecker et TokenStorage.
AuthorizationChecker
AuthenticationProvider
UserProvider
EntityManager
OrderListener
TokenStorage
getToken()
setToken($token)
isGranted('attr', $sub)
15
Principe Ouvert / fermé
Les entités doivent être ouvertes à l’extension, mais fermées à la modification.
Ouvert à l’extension:
• Altérer ou ajouter un comportement
• Étendre une entité avec de nouvelles propriétés
Fermé à la modification:
• Adapter une entité sans modifier son code source
• Doter une entité d’une interface claire et stable
Open/closed principle
17
Techniques pour rester en conformité avec le principe ouvert / fermé :
• Abstraction / Héritage / Polymorphisme
• Injection de dépendance
• Patrons de conception. Par exemple, ceux du GoF :
• Abstract Factory, Factory Method, Builder, Prototype,
• Bridge, Decorator, Proxy,
• Command, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor.
Open/closed principle
18
• …
SymfonyComponentHttpKernel
App
// public/index.php
use AppKernel;
use SymfonyComponentHttpFoundationRequest;
$kernel = new Kernel($env, $debug);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
HttpKernel
- dispatcher
+ handle($request): Response
- container
- httpKernel
Kernel
+ handle($request)
+ registerBundles()
+ configureRoutes()
Kernel
+ registerBundles()
+ configureRoutes()
Toutes les requêtes HTTP arrivent sur le
front controller.
Open/closed principle
19
Open/closed principle
Le traitement « requête-réponse » est ouvert à l’extension grâce aux événements levés à
chaque étape.
Request
Response
resolve
controller
REQUEST
resolve
arguments
call
controller
CONTROLLER CONTROLLER_ARGUMENTS
Response?
RESPONSE FINISH_REQUEST
Response
exists
TERMINATE
VIEW
Exception EXCEPTION
20
Principe de substitution de Liskov
Les objets peuvent être remplacés par des instances de leurs sous-types
sans altérer l'exactitude du programme.
LSP décrit une héritage comportemental –
sémantique plutôt que purement syntaxique.
Liskov substitution principle
22
Rectangle
Carré est un rectangle à quatre côtés égaux.
- width
- height
+ setWidth($width)
+ setHeight($height)
+ getArea()
+ setWidth($width)
+ setHeight($height)
Square
function client(Rectangle $rect)
{
$rect->setHeight(8);
$rect->setWidth(3);
assert($rect->area() == 24);
}
Le code client va échouer si on passe un
objet de type Square
class Square extends Rectangle
{
public function setWidth($width)
{
$this->width = $width;
$this->height = $width;
}
public function setHeight($height)
{
$this->height = $height;
$this->width = $height;
}
}
Liskov substitution principle
23
• Préconditions ne peuvent pas être renforcées dans une sous-classe
• Postconditions ne peuvent pas être affaiblies dans une sous-classe
• Invariants doivent être préservée dans un sous-type
LSP suggère des conditions comportementales similaires à celles de
programmation par contrat :
Rectangle Précondition: width > 0
Postcondition: $this->width == $width
Invariant: $this->height reste inchangé
Précondition: width > 0
Postcondition: $this->width == $width
Invariant: $this->height reste inchangé
Invariant: $this->width == $this->height
- width
- height
+ setWidth($width)
+ setHeight($height)
+ getArea()
+ setWidth($width)
+ setHeight($height)
Square
Liskov substitution principle
24
Autorisation – vérification des droits d'accès/privilèges de l'utilisateur authentifié.
Dans Symfony, l'autorisation est basée sur :
• Token : les informations d'authentification de l'utilisateur (rôles, …),
• Attributs : droits/privilèges à vérifier (ROLE_ADMIN, EDIT, …),
• Objet optionnel : tout objet pour lequel il faut contrôler les droits.
$authorizationChecker->isGranted('ROLE_ADMIN');
$authorizationChecker->isGranted('EDIT’, $comment);
$authorizationChecker->isGranted(['VIEW', 'MANAGE'], $order);
Liskov substitution principle
25
Liskov substitution principle
- voters : VoterInterface[]
+ decide($token, $attributes, $object)
AuthorizationChecker
- tokenStorage
- accessDecisionManager
+ isGranted($attributes, $object)
AccessDecisionManager
+ vote($token, $subject, $attributes)
VoterInterface
isGranted renvoie vrai/faux. Il délègue la
décision à AccessDecisionManager::decide.
decide demande à tous les voters de voter
et prend la décision finale.
vote doit renvoyer une de ces valeurs :
ACCESS_GRANTED, ACCESS_ABSTAIN or ACCESS_DENIED.
26
DoctrineORM
Doctrine est une bibliothèque PHP axée sur le stockage données et « object mapping ».
Instance d’Article
id: 17
author: Author(id=3, name=Marie)
createdAt: DateTime(2018-10-01)
text: "Salut PHP Forum! Ajour…"
Instance d’Author
id: 3
name: Marie
articles: [Article(id=17, …)]
article
id author_id created_at text
16 2 2018-05-21 In thi…
17 17 2018-10-01 Hi sec…
…
author
id name
2 Albert
3 Marie
4 Isaac
…
27
Liskov substitution principle
Doctrine déclenche une série d'événements au cours de la vie des entités stockées :
prePersist et postPersist, preUpdate et postUpdate, preRemove et postRemove, etc.
use DoctrineORMEventLifecycleEventArgs;
use DoctrineORMMapping as ORM;
class Article
{
private $createdAt;
private $updatedAt;
private $createdBy;
private $updatedBy;
/** @ORMPrePersist() */
public function prePersist(LifecycleEventArgs $event)
{
$this->createdAt = new DateTime();
}
/** @ORMPreUpdate() */
public function preUpdate(LifecycleEventArgs $event)
{
$article = $event->getEntity();
$article->setUpdatedAt(new DateTime());
}
}
Précondition: $event->getEntity()
est une instance d’Article
28
Liskov substitution principle
use AppEntityArticle;
use DoctrineORMEventLifecycleEventArgs;
class ArticleLifecycleListener
{
/** @var AppEntity|Author */
private $user;
public function prePersist(LifecycleEventArgs $event)
{
$article = $event->getEntity();
if ($article instanceof Article) {
$article->setCreatedBy($this->user);
}
}
public function preUpdate(LifecycleEventArgs $event)
{
$article = $event->getEntity();
if ($article instanceof Article) {
$article->setUpdatedBy($this->user);
}
}
}
Précondition: $event->getEntity()
est n’importe quelle entité
Les listeners-services ont accès à d'autres services via l'injection de dépendances :
29
Liskov substitution principle
Principe de ségrégation des interfaces
De nombreuses interfaces spécifiques au client sont préférables à une grande
interface général.
Interface segregation principle
31
Aucun client ne devrait être obligé de dépendre de méthodes qu'il n'utilise pas.
Conteneur de
services
Configuration:
yaml, xml, php
Pour chaque service :
- id
- FQCN
- dépendances
- …
- Instancie l'objet de service à la demande
- Instancie ses dépendances
- Injecte des dépendances en service
- Renvoie et stocke le service
- Renvoie la même instance pour des
demandes consécutives
“Compilation”
Container gère les services qui sont des objets utiles.
Interface segregation principle
32
PsrContainer
Récupérer des services ContainerInterface
+ get($id): mixed
+ has($id): bool
SymfonyComponentDependencyInjection
ContainerInterface
+ set($id, $service)
+ initialized($id): bool
+ getParameter($name): mixed
Container
- services
- parameterBag
- aliases
+ compile()
Configurer les services et les
paramètres
Compiler le conteneur (init, cache)
(Code client)
(Extensions)
(Kernel)
Interface segregation principle
33
Routes configuration
Url matcher
'_route’ => string 'blog_list'
'_controller' => string 'AppControllerBlog::list'
'page’ => string '2'
'category’ => string 'php-forum'
Url generator
'/blog/php-forum'
'/blog/php7/4'
$matcher->match('/blog/php-forum/2');
$generator->generate('blog_list', ['category' => 'php-forum']);
$generator->generate('blog_list', ['category' => 'php7', 'page' => 4]);
# config/routes.yaml
blog_list: # nom de route
path: /blog/{category}/{page} # path pattern
controller: AppControllerBlog::list # contrôleur à exécuter
requirements: # contraintes
category: '[a-z0-9-_]+'
page: 'd+'
defaults: # valeurs par défaut
page: 1
Interface segregation principle
34
SymfonyComponentRouting
Interface segregation principle
UrlMatcherInterface
+ match($pathinfo): array
UrlGeneratorInterface
+ generate
($name, $parameters, $referenceType): string
RouterInterface
+ getRouteCollection(): RouteCollection
Router
- collection: RouteCollection
- options: array
- context
Contient la configuration
de toutes les routes.
35
Principe d'inversion des dépendances
Les modules de haut niveau ne doivent pas dépendre de modules de bas niveau.
Les deux devraient dépendre d’abstractions.
Les abstractions ne doivent pas dépendre des détails.
Les détails devraient dépendre des abstractions.
Dependency inversion principle
37
Framework
namespace Monolog;
class Logger
{
public function log($message)
{
echo $message;
}
}
namespace Framework;
use MonologLogger;
class Kernel
{
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function handle()
{
$this->logger->log('...');
// ...
}
}
Monolog
Kernel Logger
Dependency inversion principle
38
namespace Framework;
class Kernel
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
namespace Monolog;
use FrameworkLoggerInterface;
class Logger implements LoggerInterface
{
public function log($message)
{
echo $message;
}
}
namespace Framework;
interface LoggerInterface
{
public function log($message);
}
Framework Monolog
Kernel LoggerLoggerInterface
Dependency inversion principle
39
AbstractLogger
Solution + flexible
Framework
Monolog
Kernel
Logger
LoggerInterface
Dependency inversion principle
40
Dependency inversion principle
PsrLog
SymfonyHTTPKernel
Monolog
Kernel
Logger
LoggerInterface
PSR – PHP Standards Recommendations
FIG – Framework Interoperability Group
SymfonyRouting
Router
SymfonySecurity
Handlers ...
...
...
41
Dependency inversion principle
Symfony utilise l’injection de dépendances et les interfaces quasiment partout
SymfonyForm
SymfonyValidator
ACMEValidator
FormInterface
Form
ExtensionValidator
ValidatorExtension
ValidatorInterface
Validator
Validator
- validator: ValidatorInterface
FormExtensionInterface
42
Merci de votre
attention
https://vria.eu
contact@vria.eu
https://twitter.com/RiaVlad
https://webnet.fr

Contenu connexe

Tendances

버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
민태 김
 
Clean Code I - Best Practices
Clean Code I - Best PracticesClean Code I - Best Practices
Clean Code I - Best Practices
Theo Jungeblut
 

Tendances (20)

Bad Code Smells
Bad Code SmellsBad Code Smells
Bad Code Smells
 
레가시 프로젝트의 빌드 자동화
레가시 프로젝트의 빌드 자동화레가시 프로젝트의 빌드 자동화
레가시 프로젝트의 빌드 자동화
 
Clean code
Clean codeClean code
Clean code
 
[FR] Présentatation d'Ansible
[FR] Présentatation d'Ansible [FR] Présentatation d'Ansible
[FR] Présentatation d'Ansible
 
Clean code and Code Smells
Clean code and Code SmellsClean code and Code Smells
Clean code and Code Smells
 
svn 능력자를 위한 git 개념 가이드
svn 능력자를 위한 git 개념 가이드svn 능력자를 위한 git 개념 가이드
svn 능력자를 위한 git 개념 가이드
 
Git tutorial
Git tutorialGit tutorial
Git tutorial
 
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
 
Git One Day Training Notes
Git One Day Training NotesGit One Day Training Notes
Git One Day Training Notes
 
Tutoriel GIT
Tutoriel GITTutoriel GIT
Tutoriel GIT
 
C++20 Key Features Summary
C++20 Key Features SummaryC++20 Key Features Summary
C++20 Key Features Summary
 
Clean Code I - Best Practices
Clean Code I - Best PracticesClean Code I - Best Practices
Clean Code I - Best Practices
 
Clean code
Clean codeClean code
Clean code
 
Introduction to ansible
Introduction to ansibleIntroduction to ansible
Introduction to ansible
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
 
Intro to Linux Shell Scripting
Intro to Linux Shell ScriptingIntro to Linux Shell Scripting
Intro to Linux Shell Scripting
 
Présentation de git
Présentation de gitPrésentation de git
Présentation de git
 
SOLID Design Principles applied in Java
SOLID Design Principles applied in JavaSOLID Design Principles applied in Java
SOLID Design Principles applied in Java
 
Clean Code
Clean CodeClean Code
Clean Code
 
Git l'essentiel
Git l'essentielGit l'essentiel
Git l'essentiel
 

Similaire à SOLID : les principes à l’origine du succès de Symfony et de vos applications

Soutenance Zend Framework vs Symfony
Soutenance Zend Framework vs SymfonySoutenance Zend Framework vs Symfony
Soutenance Zend Framework vs Symfony
Vincent Composieux
 
BordeauxJUG : Portails & Portlets Java
BordeauxJUG : Portails & Portlets JavaBordeauxJUG : Portails & Portlets Java
BordeauxJUG : Portails & Portlets Java
Camblor Frédéric
 
Drupal 8, symfony
Drupal 8, symfonyDrupal 8, symfony
Drupal 8, symfony
jeUXdiCode
 
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
Atsé François-Xavier KOBON
 
Rich Desktop Applications
Rich Desktop ApplicationsRich Desktop Applications
Rich Desktop Applications
goldoraf
 

Similaire à SOLID : les principes à l’origine du succès de Symfony et de vos applications (20)

Mieux Développer en PHP avec Symfony
Mieux Développer en PHP avec SymfonyMieux Développer en PHP avec Symfony
Mieux Développer en PHP avec Symfony
 
Soutenance Zend Framework vs Symfony
Soutenance Zend Framework vs SymfonySoutenance Zend Framework vs Symfony
Soutenance Zend Framework vs Symfony
 
php2 : formulaire-session-PDO
php2 : formulaire-session-PDOphp2 : formulaire-session-PDO
php2 : formulaire-session-PDO
 
Introduction à Symfony2
Introduction à Symfony2Introduction à Symfony2
Introduction à Symfony2
 
Introduction à Symfony
Introduction à SymfonyIntroduction à Symfony
Introduction à Symfony
 
Présentation symfony drupal
Présentation symfony drupalPrésentation symfony drupal
Présentation symfony drupal
 
Rapport tp3 j2ee
Rapport tp3 j2eeRapport tp3 j2ee
Rapport tp3 j2ee
 
Symfony 2 : chapitre 1 - Présentation Générale
Symfony 2 : chapitre 1 - Présentation GénéraleSymfony 2 : chapitre 1 - Présentation Générale
Symfony 2 : chapitre 1 - Présentation Générale
 
Laravel yet another framework
Laravel  yet another frameworkLaravel  yet another framework
Laravel yet another framework
 
BordeauxJUG : Portails & Portlets Java
BordeauxJUG : Portails & Portlets JavaBordeauxJUG : Portails & Portlets Java
BordeauxJUG : Portails & Portlets Java
 
Playing With PHP 5.3
Playing With PHP 5.3Playing With PHP 5.3
Playing With PHP 5.3
 
Drupal 8, symfony
Drupal 8, symfonyDrupal 8, symfony
Drupal 8, symfony
 
Open close principle, on a dit étendre, pas extends !
Open close principle, on a dit étendre, pas extends !Open close principle, on a dit étendre, pas extends !
Open close principle, on a dit étendre, pas extends !
 
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
 
Symfony2 - Un Framework PHP 5 Performant
Symfony2 - Un Framework PHP 5 PerformantSymfony2 - Un Framework PHP 5 Performant
Symfony2 - Un Framework PHP 5 Performant
 
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
 
Rich Desktop Applications
Rich Desktop ApplicationsRich Desktop Applications
Rich Desktop Applications
 
Introduction à Laravel
Introduction à LaravelIntroduction à Laravel
Introduction à Laravel
 
Cours php -partie 1.pdf
Cours php -partie 1.pdfCours php -partie 1.pdf
Cours php -partie 1.pdf
 
Authentification sociale en angular 1.pptx
Authentification sociale en angular 1.pptxAuthentification sociale en angular 1.pptx
Authentification sociale en angular 1.pptx
 

Plus de Vladyslav Riabchenko

Plus de Vladyslav Riabchenko (8)

Modèle de domaine riche dans une application métier complexe un exemple pratique
Modèle de domaine riche dans une application métier complexe un exemple pratiqueModèle de domaine riche dans une application métier complexe un exemple pratique
Modèle de domaine riche dans une application métier complexe un exemple pratique
 
SOLID: the core principles of success of the Symfony web framework and of you...
SOLID: the core principles of success of the Symfony web framework and of you...SOLID: the core principles of success of the Symfony web framework and of you...
SOLID: the core principles of success of the Symfony web framework and of you...
 
Sécurisation de vos applications web à l’aide du composant Security de Symfony
Sécurisation de vos applications web  à l’aide du composant Security de SymfonySécurisation de vos applications web  à l’aide du composant Security de Symfony
Sécurisation de vos applications web à l’aide du composant Security de Symfony
 
Sécurisation de vos applications web à l’aide du composant Security de Symfony
Sécurisation de vos applications web à l’aide du composant Security de SymfonySécurisation de vos applications web à l’aide du composant Security de Symfony
Sécurisation de vos applications web à l’aide du composant Security de Symfony
 
Git
GitGit
Git
 
Versionning sémantique et Composer
Versionning sémantique et ComposerVersionning sémantique et Composer
Versionning sémantique et Composer
 
Injection de dépendances dans Symfony >= 3.3
Injection de dépendances dans Symfony >= 3.3Injection de dépendances dans Symfony >= 3.3
Injection de dépendances dans Symfony >= 3.3
 
Les patrons de conception du composant Form
Les patrons de conception du composant FormLes patrons de conception du composant Form
Les patrons de conception du composant Form
 

SOLID : les principes à l’origine du succès de Symfony et de vos applications

  • 1. SOLID : LES PRINCIPES À L’ORIGINE DU SUCCÈS DE SYMFONY ET DE VOS APPLICATIONS
  • 2. https://vria.eu contact@vria.eu https://twitter.com/RiaVlad RIABCHENKO Vladyslav 5+ ans full stack web-développeur Certifié Symfony Architecte technique à Webnet
  • 3. Symfony SOLID Responsabilité unique Ouvert / fermé Substitution de Liskov Ségrégation des interfaces Inversion de dépendances Plan 3 1. 2. S. O. L. I. D.
  • 5. Symfony 2. Framework PHP pour projets web 1. Ensemble de composants PHP MVC Pattern Model View Requête HTTP Routage Contrôleur Réponse HTTP 5
  • 6. Symfony HttpKernel DependencyInjection HttpFoundation Routing EventDispatcher Cœur du framework qui gère le cycle de requête-réponse HTTP Couche orientée objet aux spécifications HTTP Mapper les requêtes aux variables (contrôleur, etc.) Médiateur permettant aux composants découplés de communiquer à l’aide d’événements Conteneur de service qui instancie des services en se basant sur la configuration et injecte leurs dépendances et 45 autres, y compris Security, Form, Console, Cache, Asset, Validator, Serializer, etc. 6
  • 7. Symfony FrameworkBundle Configure les composants Symfony et les colle ensemble. Il enregistre les services et les listeners d'événements. Les bundles sont des plugins réutilisables qui fournissent des services, des routes, des contrôleurs, des templates, etc. Your code Fournit des routes, des contrôleurs, des services, une logique métier et des templates adaptés à vos besoins. doctrine/orm DBAL et ORM pour communiquer avec des bases de données. twig/twig Moteur de template. 7
  • 9. SOLID – Responsabilité unique – Ouvert / fermé – Substitution de Liskov – Ségrégation des interfaces – Inversion des dépendances Réutilisable Flexible Évolutif Compréhensible Fragile Dupliqué Visqueux Complexe sans raison SOLID aide à créer le code qui est … et à éviter le code qui est S O L I D 9
  • 11. class ReportService { public function createReport($data) { // working with report manager at domain layer } public function getLastThreeReportsByAuthor($author) { // working with database at ORM or DBAL layer } public function searchBy($criteria) { // working with database at ORM or DBAL layer } public function export(Report $report) { // working with filesystem at by means of PHP functions } } Une classe ne devrait avoir qu'une seule responsabilité. La responsabilité est une raison de modifier la classe. Single responsibility principle 11
  • 12. // working with database at ORM and/or DBAL layer class ReportRepository { public function getLastThreeReportsByAuthor($author) { ... } public function searchBy($criteria) { ... } } // working with report manager at domain layer (business logic) class ReportManager { public function createReport($data) { ... } } // working with filesystem and pdf, excel, csv, etc. class ReportExporter { public function export(Report $report) { ... } } Manager Repository Exporter Single responsibility principle 12
  • 13. Feature : Stocker les utilisateurs dans la base de données Feature : Stocker les commandes dans la base de données Feature : Authentifier des utilisateurs (formulaire de connexion, HTTP, peu importe …), stocker les informations d'identification dans la session et authentifier chaque requête ultérieure Feature : Lors de la création de la commande, affecter automatiquement cette commande à un utilisateur connecté Single responsibility principle 13
  • 14. SecurityContext Stocke le token d’utilisateur setToken($token), getToken() Vérifie l’autorisation isGranted('attr', $sub) AuthenticationProvider Authentifie le token UserProvider Récupère ou rafraîchit l'utilisateur EntityManager Toutes les interactions avec la base de données, ORM OrderListener Notre listener qui modifie la commande avant qu'il ne soit inséré L’injection du service SecurityContext dans n’importe quel listener d’EntityManager crée une dépendance circulaire. Single responsibility principle 14
  • 15. Single responsibility principle La solution est diviser SecurityContext en AuthorizationChecker et TokenStorage. AuthorizationChecker AuthenticationProvider UserProvider EntityManager OrderListener TokenStorage getToken() setToken($token) isGranted('attr', $sub) 15
  • 17. Les entités doivent être ouvertes à l’extension, mais fermées à la modification. Ouvert à l’extension: • Altérer ou ajouter un comportement • Étendre une entité avec de nouvelles propriétés Fermé à la modification: • Adapter une entité sans modifier son code source • Doter une entité d’une interface claire et stable Open/closed principle 17
  • 18. Techniques pour rester en conformité avec le principe ouvert / fermé : • Abstraction / Héritage / Polymorphisme • Injection de dépendance • Patrons de conception. Par exemple, ceux du GoF : • Abstract Factory, Factory Method, Builder, Prototype, • Bridge, Decorator, Proxy, • Command, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor. Open/closed principle 18 • …
  • 19. SymfonyComponentHttpKernel App // public/index.php use AppKernel; use SymfonyComponentHttpFoundationRequest; $kernel = new Kernel($env, $debug); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send(); HttpKernel - dispatcher + handle($request): Response - container - httpKernel Kernel + handle($request) + registerBundles() + configureRoutes() Kernel + registerBundles() + configureRoutes() Toutes les requêtes HTTP arrivent sur le front controller. Open/closed principle 19
  • 20. Open/closed principle Le traitement « requête-réponse » est ouvert à l’extension grâce aux événements levés à chaque étape. Request Response resolve controller REQUEST resolve arguments call controller CONTROLLER CONTROLLER_ARGUMENTS Response? RESPONSE FINISH_REQUEST Response exists TERMINATE VIEW Exception EXCEPTION 20
  • 22. Les objets peuvent être remplacés par des instances de leurs sous-types sans altérer l'exactitude du programme. LSP décrit une héritage comportemental – sémantique plutôt que purement syntaxique. Liskov substitution principle 22
  • 23. Rectangle Carré est un rectangle à quatre côtés égaux. - width - height + setWidth($width) + setHeight($height) + getArea() + setWidth($width) + setHeight($height) Square function client(Rectangle $rect) { $rect->setHeight(8); $rect->setWidth(3); assert($rect->area() == 24); } Le code client va échouer si on passe un objet de type Square class Square extends Rectangle { public function setWidth($width) { $this->width = $width; $this->height = $width; } public function setHeight($height) { $this->height = $height; $this->width = $height; } } Liskov substitution principle 23
  • 24. • Préconditions ne peuvent pas être renforcées dans une sous-classe • Postconditions ne peuvent pas être affaiblies dans une sous-classe • Invariants doivent être préservée dans un sous-type LSP suggère des conditions comportementales similaires à celles de programmation par contrat : Rectangle Précondition: width > 0 Postcondition: $this->width == $width Invariant: $this->height reste inchangé Précondition: width > 0 Postcondition: $this->width == $width Invariant: $this->height reste inchangé Invariant: $this->width == $this->height - width - height + setWidth($width) + setHeight($height) + getArea() + setWidth($width) + setHeight($height) Square Liskov substitution principle 24
  • 25. Autorisation – vérification des droits d'accès/privilèges de l'utilisateur authentifié. Dans Symfony, l'autorisation est basée sur : • Token : les informations d'authentification de l'utilisateur (rôles, …), • Attributs : droits/privilèges à vérifier (ROLE_ADMIN, EDIT, …), • Objet optionnel : tout objet pour lequel il faut contrôler les droits. $authorizationChecker->isGranted('ROLE_ADMIN'); $authorizationChecker->isGranted('EDIT’, $comment); $authorizationChecker->isGranted(['VIEW', 'MANAGE'], $order); Liskov substitution principle 25
  • 26. Liskov substitution principle - voters : VoterInterface[] + decide($token, $attributes, $object) AuthorizationChecker - tokenStorage - accessDecisionManager + isGranted($attributes, $object) AccessDecisionManager + vote($token, $subject, $attributes) VoterInterface isGranted renvoie vrai/faux. Il délègue la décision à AccessDecisionManager::decide. decide demande à tous les voters de voter et prend la décision finale. vote doit renvoyer une de ces valeurs : ACCESS_GRANTED, ACCESS_ABSTAIN or ACCESS_DENIED. 26
  • 27. DoctrineORM Doctrine est une bibliothèque PHP axée sur le stockage données et « object mapping ». Instance d’Article id: 17 author: Author(id=3, name=Marie) createdAt: DateTime(2018-10-01) text: "Salut PHP Forum! Ajour…" Instance d’Author id: 3 name: Marie articles: [Article(id=17, …)] article id author_id created_at text 16 2 2018-05-21 In thi… 17 17 2018-10-01 Hi sec… … author id name 2 Albert 3 Marie 4 Isaac … 27 Liskov substitution principle
  • 28. Doctrine déclenche une série d'événements au cours de la vie des entités stockées : prePersist et postPersist, preUpdate et postUpdate, preRemove et postRemove, etc. use DoctrineORMEventLifecycleEventArgs; use DoctrineORMMapping as ORM; class Article { private $createdAt; private $updatedAt; private $createdBy; private $updatedBy; /** @ORMPrePersist() */ public function prePersist(LifecycleEventArgs $event) { $this->createdAt = new DateTime(); } /** @ORMPreUpdate() */ public function preUpdate(LifecycleEventArgs $event) { $article = $event->getEntity(); $article->setUpdatedAt(new DateTime()); } } Précondition: $event->getEntity() est une instance d’Article 28 Liskov substitution principle
  • 29. use AppEntityArticle; use DoctrineORMEventLifecycleEventArgs; class ArticleLifecycleListener { /** @var AppEntity|Author */ private $user; public function prePersist(LifecycleEventArgs $event) { $article = $event->getEntity(); if ($article instanceof Article) { $article->setCreatedBy($this->user); } } public function preUpdate(LifecycleEventArgs $event) { $article = $event->getEntity(); if ($article instanceof Article) { $article->setUpdatedBy($this->user); } } } Précondition: $event->getEntity() est n’importe quelle entité Les listeners-services ont accès à d'autres services via l'injection de dépendances : 29 Liskov substitution principle
  • 30. Principe de ségrégation des interfaces
  • 31. De nombreuses interfaces spécifiques au client sont préférables à une grande interface général. Interface segregation principle 31 Aucun client ne devrait être obligé de dépendre de méthodes qu'il n'utilise pas.
  • 32. Conteneur de services Configuration: yaml, xml, php Pour chaque service : - id - FQCN - dépendances - … - Instancie l'objet de service à la demande - Instancie ses dépendances - Injecte des dépendances en service - Renvoie et stocke le service - Renvoie la même instance pour des demandes consécutives “Compilation” Container gère les services qui sont des objets utiles. Interface segregation principle 32
  • 33. PsrContainer Récupérer des services ContainerInterface + get($id): mixed + has($id): bool SymfonyComponentDependencyInjection ContainerInterface + set($id, $service) + initialized($id): bool + getParameter($name): mixed Container - services - parameterBag - aliases + compile() Configurer les services et les paramètres Compiler le conteneur (init, cache) (Code client) (Extensions) (Kernel) Interface segregation principle 33
  • 34. Routes configuration Url matcher '_route’ => string 'blog_list' '_controller' => string 'AppControllerBlog::list' 'page’ => string '2' 'category’ => string 'php-forum' Url generator '/blog/php-forum' '/blog/php7/4' $matcher->match('/blog/php-forum/2'); $generator->generate('blog_list', ['category' => 'php-forum']); $generator->generate('blog_list', ['category' => 'php7', 'page' => 4]); # config/routes.yaml blog_list: # nom de route path: /blog/{category}/{page} # path pattern controller: AppControllerBlog::list # contrôleur à exécuter requirements: # contraintes category: '[a-z0-9-_]+' page: 'd+' defaults: # valeurs par défaut page: 1 Interface segregation principle 34
  • 35. SymfonyComponentRouting Interface segregation principle UrlMatcherInterface + match($pathinfo): array UrlGeneratorInterface + generate ($name, $parameters, $referenceType): string RouterInterface + getRouteCollection(): RouteCollection Router - collection: RouteCollection - options: array - context Contient la configuration de toutes les routes. 35
  • 36. Principe d'inversion des dépendances
  • 37. Les modules de haut niveau ne doivent pas dépendre de modules de bas niveau. Les deux devraient dépendre d’abstractions. Les abstractions ne doivent pas dépendre des détails. Les détails devraient dépendre des abstractions. Dependency inversion principle 37
  • 38. Framework namespace Monolog; class Logger { public function log($message) { echo $message; } } namespace Framework; use MonologLogger; class Kernel { private $logger; public function __construct(Logger $logger) { $this->logger = $logger; } public function handle() { $this->logger->log('...'); // ... } } Monolog Kernel Logger Dependency inversion principle 38
  • 39. namespace Framework; class Kernel { private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } } namespace Monolog; use FrameworkLoggerInterface; class Logger implements LoggerInterface { public function log($message) { echo $message; } } namespace Framework; interface LoggerInterface { public function log($message); } Framework Monolog Kernel LoggerLoggerInterface Dependency inversion principle 39
  • 41. Dependency inversion principle PsrLog SymfonyHTTPKernel Monolog Kernel Logger LoggerInterface PSR – PHP Standards Recommendations FIG – Framework Interoperability Group SymfonyRouting Router SymfonySecurity Handlers ... ... ... 41
  • 42. Dependency inversion principle Symfony utilise l’injection de dépendances et les interfaces quasiment partout SymfonyForm SymfonyValidator ACMEValidator FormInterface Form ExtensionValidator ValidatorExtension ValidatorInterface Validator Validator - validator: ValidatorInterface FormExtensionInterface 42