Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
SOLID: the core principles of success
of the Symfony web framework
and of your applications
Software Engineering Conferenc...
https://vria.eu
contact@vria.eu
https://twitter.com/RiaVlad
RIABCHENKO Vladyslav
5+ years full stack web-developer
Symfony...
Symfony 3
1. Symfony
2. SOLID
3. Single responsibility principle
4. Open/closed principle
5. Liskov substitution principle...
Symfony framework
Symfony 5
2. PHP framework for web projects
1. Set of reusable PHP components
MVC Pattern
Model View
HTTP Request Routing ...
Symfony 6
HttpKernel
DependencyInjection
HttpFoundation
Routing
EventDispatcher
Heart of framework that manages HTTP reque...
Symfony 7
FrameworkBundle
Configures Symfony components and glues them in web app. It
registers services and event listene...
SOLID principles
SOLID 9
– Single Responsibility Principle
– Open/Closed Principle
– Liskov Substitution Principle
– Interface Segregation ...
Single responsibility principle
Single responsibility principle 11
class ReportService
{
public function createReport($data)
{
// working with report mana...
Single responsibility principle
// working with report manager at domain layer (business logic)
class ReportManager
{
publ...
Single responsibility principle 13
Feature : Store users in database
Feature : Store orders in database
Feature : Authenti...
Single responsibility principle 14
SecurityContext
Stores a user token setToken($token), getToken()
Checks if a current us...
Single responsibility principle 15
Solution is splitting SecurityContext into AuthorizationChecker and TokenStorage
Author...
Open/closed principle
Open/closed principle
Entities should be open for extension, but closed for modification.
17
Open for extension:
• Alter o...
Open/closed principle 18
Techniques to stay in compliance with the open/closed principle:
• Abstraction / Inheritance / Po...
SymfonyComponentHttpKernel
App
Open/closed principle 19
// public/index.php
use AppKernel;
use SymfonyComponentHttpFoundat...
Open/closed principle 20
Request-Response cycle is open for extension thanks to events dispatched at every step.
Request
R...
Liskov substitution principle
Liskov substitution principle
Objects should be replaceable with instances of their subtypes
without altering the correctn...
Liskov substitution principle 23
Rectangle
Square is a regular rectangle with four equal sides.
- width
- height
+ setWidt...
Liskov substitution principle 24
• Preconditions cannot be strengthened in a subtype
• Postconditions cannot be weakened i...
Liskov substitution principle 25
Authorization – verifying access rights/privileges of currently authenticated user.
In Sy...
Liskov substitution principle 26
- voters : VoterInterface[]
+ decide($token, $attributes, $object)
AuthorizationChecker
-...
DoctrineORM
Liskov substitution principle 27
Doctrine is several PHP libraries focused on database storage and object mapp...
Liskov substitution principle 28
Doctrine triggers a bunch of events during the life-time of stored entities:
prePersist a...
use AppEntityArticle;
use DoctrineORMEventLifecycleEventArgs;
class ArticleLifecycleListener
{
/** @var AppEntity|Author *...
Interface segregation principle
Interface segregation principle 31
Many client-specific interfaces are better than one general-purpose interface.
No clien...
Interface segregation principle 32
Service container
Configuraion:
yaml, xml, php
For each service:
- id
- class name
- de...
PsrContainer
Interface segregation principle 33
Fetch services ContainerInterface
+ get($id): mixed
+ has($id): bool
Symfo...
Interface segregation principle 34
Routes configuration
Url matcher
'_route’ => string 'blog_list'
'_controller' => string...
SymfonyComponentRouting
Interface segregation principle 35
UrlMatcherInterface
+ match($pathinfo): array
UrlGeneratorInter...
Dependency inversion principle
Dependency inversion principle 37
High-level modules should not depend on low-level modules.
Both should depend on abstrac...
Framework
Dependency inversion principle 38
namespace Monolog;
class Logger
{
public function log($message)
{
echo $messag...
Dependency inversion principle 39
namespace Framework;
class Kernel
{
private $logger;
public function __construct(LoggerI...
AbstractLogger
Dependency inversion principle 40
More flexible solution
Framework
Monolog
Kernel
Logger
LoggerInterface
Dependency inversion principle 41
PsrLog
SymfonyHTTPKernel
Monolog
Kernel
Logger
LoggerInterface
PSR – PHP Standards Recom...
Dependency inversion principle 42
Symfony uses interfaces and dependency injection everywhere.
SymfonyForm
SymfonyValidato...
Thank you!
https://vria.eu
contact@vria.eu
https://twitter.com/RiaVlad
https://webnet.fr
Prochain SlideShare
Chargement dans…5
×

SOLID: the core principles of success of the Symfony web framework and of your applications

1 008 vues

Publié le

We'll look at each SOLID principle on the example of a Symfony component to see how the implementation of this principle was beneficial.

Publié dans : Logiciels
  • Soyez le premier à commenter

SOLID: the core principles of success of the Symfony web framework and of your applications

  1. 1. SOLID: the core principles of success of the Symfony web framework and of your applications Software Engineering Conference Russia 2018 October 12-13 Moscow RIABCHENKO Vladyslav
  2. 2. https://vria.eu contact@vria.eu https://twitter.com/RiaVlad RIABCHENKO Vladyslav 5+ years full stack web-developer Symfony certified developer and contributor Technical architect at Webnet (France)
  3. 3. Symfony 3 1. Symfony 2. SOLID 3. Single responsibility principle 4. Open/closed principle 5. Liskov substitution principle 6. Interface segregation principle 7. Dependency inversion principle Plan
  4. 4. Symfony framework
  5. 5. Symfony 5 2. PHP framework for web projects 1. Set of reusable PHP components MVC Pattern Model View HTTP Request Routing Controller HTTP Response
  6. 6. Symfony 6 HttpKernel DependencyInjection HttpFoundation Routing EventDispatcher Heart of framework that manages HTTP request-response cycle Object-oriented layer for request, response, session, etc. Maps requests to variables (controller, path parameters) Mediator that allows decoupled components to communicate by dispatching events Service container that instantiates service objects out of configuration and injects their dependencies and 45 others including Security, Form, Console, Cache, Asset, Validator, Serializer, etc.
  7. 7. Symfony 7 FrameworkBundle Configures Symfony components and glues them in web app. It registers services and event listeners. Bundles are reusable plugins that provide services, routes, controllers, templates, etc. Your code Provides routes, controllers, services, business logic, templates for your purpose. doctrine/orm DBAL and ORM libraries to communicate with databases. twig/twig Template engine.
  8. 8. SOLID principles
  9. 9. SOLID 9 – Single Responsibility Principle – Open/Closed Principle – Liskov Substitution Principle – Interface Segregation Principle – Dependency Inversion Principle Reusable Flexible Maintainable Understandable Fragile Duplicated Viscose Unreasonably complex SOLID helps to design a system that is … and avoid a system that is S O L I D
  10. 10. Single responsibility principle
  11. 11. Single responsibility principle 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 } } A class should have only a single responsibility. Responsibility is a reason to change.
  12. 12. Single responsibility principle // 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) { ... } } // working with database at ORM and/or DBAL layer class ReportRepository { public function getLastThreeReportsByAuthor($author) { ... } public function searchBy($criteria) { ... } } Manager Exporter Repository 12
  13. 13. Single responsibility principle 13 Feature : Store users in database Feature : Store orders in database Feature : Authenticate users against database (login form, HTTP auth, whatever…) Feature : On creation of order assign a currentrly connected user to it. Store user credentials in session and authenticate users at latter requests
  14. 14. Single responsibility principle 14 SecurityContext Stores a user token setToken($token), getToken() Checks if a current user authorization isGranted('attr', $sub) AuthenticationProvider Authenticates a token UserProvider Retrieves or refreshes user from database EntityManager All interaction with database at ORM and DBAL levels OrderListener Your listener that modifies order before it gets inserted Injecting SecurityContext service in any EntityManager’s listener created a circular dependency.
  15. 15. Single responsibility principle 15 Solution is splitting SecurityContext into AuthorizationChecker and TokenStorage AuthorizationChecker AuthenticationProvider UserProvider EntityManager OrderListener TokenStorage getToken() setToken($token) isGranted('attr', $sub)
  16. 16. Open/closed principle
  17. 17. Open/closed principle Entities should be open for extension, but closed for modification. 17 Open for extension: • Alter or add behavior • Extend an entity with new properties Closed for modification: • Adapt entity without modifying its source code • Provide an entity with clear and stable interface
  18. 18. Open/closed principle 18 Techniques to stay in compliance with the open/closed principle: • Abstraction / Inheritance / Polymorphism • Dependency injection • Single responsibility principle • Design patterns. For example, those of GoF: • Abstract Factory, Factory Method, Builder, Prototype, • Bridge, Decorator, Proxy, • Command, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor.
  19. 19. SymfonyComponentHttpKernel App Open/closed principle 19 // 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() All HTTP requests are treated by front controller.
  20. 20. Open/closed principle 20 Request-Response cycle is open for extension thanks to events dispatched at every step. Request Response resolve controller REQUEST resolve arguments call controller CONTROLLER CONTROLLER_ARGUMENTS Response RESPONSE FINISH_REQUEST Response exists TERMINATE VIEW Exception EXCEPTION
  21. 21. Liskov substitution principle
  22. 22. Liskov substitution principle Objects should be replaceable with instances of their subtypes without altering the correctness of the program. 22 LSP describes behavioral subtyping – semantic rather than merely syntactic relation.
  23. 23. Liskov substitution principle 23 Rectangle Square is a regular rectangle with four equal sides. - 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); } Client will fail when you pass Square object class Square extends Rectangle { public function setWidth($width) { $this->width = $width; $this->height = $width; } public function setHeight($height) { $this->height = $height; $this->width = $height; } }
  24. 24. Liskov substitution principle 24 • Preconditions cannot be strengthened in a subtype • Postconditions cannot be weakened in a subtype • Invariants of the supertype must be preserved in a subtype LSP suggests behavioral conditions resembling those of design by contract methodology: Rectangle Precondition: width is integer > 0 Postcondition: $this->width equals $width Invariant: $this->height remains unchanged Precondition: width is integer > 0 Postcondition: $this->width equals $width Invariant: $this->height remains unchanged Invariant: $this->width equals $this->height - width - height + setWidth($width) + setHeight($height) + getArea() + setWidth($width) + setHeight($height) Square
  25. 25. Liskov substitution principle 25 Authorization – verifying access rights/privileges of currently authenticated user. In Symfony authorization is based on: • Token: the user’s authentication information (roles, etc.), • Attributes: rights/privileges to verify the access to, • Object: Any object for which access control needs to be checked. $authorizationChecker->isGranted('ROLE_ADMIN'); $authorizationChecker->isGranted('EDIT’, $comment); $authorizationChecker->isGranted(['VIEW', 'MANAGE'], $order);
  26. 26. Liskov substitution principle 26 - voters : VoterInterface[] + decide($token, $attributes, $object) AuthorizationChecker - tokenStorage - accessDecisionManager + isGranted($attributes, $object) AccessDecisionManager + vote($token, $subject, $attributes) VoterInterface isGranted returns true/false. It delegates decision to AccessDecisionManager::decide. decide asks all voters to vote and returns final decision. vote must return one of these values : ACCESS_GRANTED, ACCESS_ABSTAIN or ACCESS_DENIED.
  27. 27. DoctrineORM Liskov substitution principle 27 Doctrine is several PHP libraries focused on database storage and object mapping. Instance of Article id: 17 author: Author(id=3, name=Marie) createdAt: DateTime(2018-10-01) text: "Hi Secrus! Today we…" Instance of 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 …
  28. 28. Liskov substitution principle 28 Doctrine triggers a bunch of events during the life-time of stored entities: prePersist and postPersist, preUpdate and postUpdate, preRemove and postRemove. 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()); } } Precondition: $event->getEntity() is an instance of Article
  29. 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); } } } Liskov substitution principle 29 Precondition: $event->getEntity() is an instance of any entity Doctrine listeners as services have access to other services via dependency injection :
  30. 30. Interface segregation principle
  31. 31. Interface segregation principle 31 Many client-specific interfaces are better than one general-purpose interface. No client should be forced to depend on methods it does not use.
  32. 32. Interface segregation principle 32 Service container Configuraion: yaml, xml, php For each service: - id - class name - dependencies - … - Instantiate service object on demand - Instantiate its dependencies - Inject dependencies into service - Store and return the service - Return the same instance on consecutive demands “Compilation” Container manages services which are any useful objects.
  33. 33. PsrContainer Interface segregation principle 33 Fetch services ContainerInterface + get($id): mixed + has($id): bool SymfonyComponentDependencyInjection ContainerInterface + set($id, $service) + initialized($id): bool + getParameter($name): mixed Container - services - parameterBag - aliases + compile() Configure services, parameters Compile container (init, cache) (Any client code) (Extensions) (Kernel)
  34. 34. Interface segregation principle 34 Routes configuration Url matcher '_route’ => string 'blog_list' '_controller' => string 'AppControllerBlog::list' 'page’ => string '2' 'category’ => string 'secr-conference' Url generator '/blog/secr-conference' '/blog/secr/2' $matcher->match('/blog/secr-conference/2'); $generator->generate('blog_list', ['category' => 'secr-conference']); $generator->generate('blog_list', ['category' => 'secr', 'page' => 2]); # config/routes.yaml blog_list: # route name path: /blog/{category}/{page} # path pattern controller: AppControllerBlog::list # controller to execute requirements: # param constraints category: '[a-z0-9-_]+' page: 'd+' defaults: # param default values page: 1
  35. 35. SymfonyComponentRouting Interface segregation principle 35 UrlMatcherInterface + match($pathinfo): array UrlGeneratorInterface + generate ($name, $parameters, $referenceType): string RouterInterface + getRouteCollection(): RouteCollection Router - collection: RouteCollection - options: array - context Contains the configuration of all known routes.
  36. 36. Dependency inversion principle
  37. 37. Dependency inversion principle 37 High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.
  38. 38. Framework Dependency inversion principle 38 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
  39. 39. Dependency inversion principle 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
  40. 40. AbstractLogger Dependency inversion principle 40 More flexible solution Framework Monolog Kernel Logger LoggerInterface
  41. 41. Dependency inversion principle 41 PsrLog SymfonyHTTPKernel Monolog Kernel Logger LoggerInterface PSR – PHP Standards Recommendations by FIG – Framework Interoperability Group SymfonyRouting Router SymfonySecurity Handlers ... ... ...
  42. 42. Dependency inversion principle 42 Symfony uses interfaces and dependency injection everywhere. SymfonyForm SymfonyValidator ACMEValidator FormInterface Form ExtensionValidator ValidatorExtension ValidatorInterface Validator Validator - validator: ValidatorInterface FormExtensionInterface
  43. 43. Thank you! https://vria.eu contact@vria.eu https://twitter.com/RiaVlad https://webnet.fr

×