Design Patterns avec PHP 5.3, Symfony et Pimple

5,961 views

Published on

Cette conférence présente deux grands motifs de conception : l'observateur et l'injection de dépendance. Ce sujet allie à la fois théorie et pratique. Le composant autonome EventDispatcher de Symfony ainsi que le conteneur d'injection de dépendance Pimple sont mis à l'honneur avec des exemples pratiques d'usage. Ces cas pratiques combinent du code de l'ORM Propel ainsi que le composant autonome Zend\Search\Lucene du Zend Framework 2

Published in: Technology
3 Comments
4 Likes
Statistics
Notes
  • Les design patterns répondent à des problématiques bien précises. Il faut savoir les utiliser au bon moment et à bon escient.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Encore moi ;o)
    Je découvre cet article magnifique :
    http://www.tonymarston.net/php-mysql/dependency-injection-is-evil.html
    Tout est dit
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Ca fait 20 ans que je code ( Cobol, C, ADA, VB, C#, PHP5 ) et putain j'en chie avec ces design pattern à la noix.
    L'ambition est bonne, mais on ne m'enlèvera pas de la tête qu'un bon développeur fera toujours mieux avec 3 bouts de ficelle qu'un super pro des design pattern qui développe comme un cochon.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
5,961
On SlideShare
0
From Embeds
0
Number of Embeds
60
Actions
Shares
0
Downloads
81
Comments
3
Likes
4
Embeds 0
No embeds

No notes for slide

Design Patterns avec PHP 5.3, Symfony et Pimple

  1. 1. Simplifiez-vous les Design Patterns avec PHP 5.3 Hugo Hamon – 12/07/12
  2. 2. ObservateurDependency InjectionInversion de Contrôle
  3. 3. Observer
  4. 4. Un sujet, l’objetobservable, émet un signalà des modules qui jouentle rôle d’observateurs.
  5. 5. Event Dispatcher
  6. 6. Le Dispatcheur est un objetqui gère les connexionsentre le sujet observé et sesobservateurs (écouteurs).
  7. 7. # composer.json{ "require": { "php": ">=5.3.3", "symfony/event-dispatcher": "2.1.*" }}
  8. 8. use SymfonyComponentEventDispatcherEvent;use SymfonyComponentEventDispatcherEventDispatcher;use AFUPArticleListener;$dispatcher = new EventDispatcher();// Déclaration des écouteurs$listener1 = array(new ArticleListener(), onDelete);$listener2 = array(new ArticleListener(), onSave);// Enregistrement des écouteurs$dispatcher->addListener(article.delete, $listener1);$dispatcher->addListener(article.pre_save, $listener2);// Notification des écouteurs$dispatcher->dispatch(article.pre_save, new Event());
  9. 9. Mise en Pratique
  10. 10. use AFUPModelArticle;$article = new Article();$article->setTitle(AFUP Design Patterns);$article->setContent(Some **content**);$article->save();echo $article->getHtmlContent();
  11. 11. <p> Some <strong>content</strong></p>
  12. 12. namespace AFUPModel;use PropelRuntimeConnectionConnectionInterface;use dflydevmarkdownMarkdownParser;class Article extends AFUPModelBaseArticle{ public function save(ConnectionInterface $con = null) { $parser = new MarkdownParser(); $html = $parser->transformMarkdown($this->getContent()); $this->setHtmlContent($html); $ret = parent::save($con); $this->updateLuceneIndex(); return $ret; }}
  13. 13. Le Sujet Observé
  14. 14. namespace AFUPModel;use SymfonyComponentEventDispatcherEventDispatcher;use AFUPModelBaseArticle as BaseArticle;class Article extends BaseArticle{ private $dispatcher; public function setDispatcher(EventDispatcher $dispatcher) { $this->dispatcher = $dispatcher; }}
  15. 15. namespace AFUPModel;// ...use PropelRuntimeConnectionConnectionInterface;use AFUPEventArticleEvent;class Article extends BaseArticle{ // ... public function save(ConnectionInterface $con = null) { $event = new ArticleEvent($this); $this->dispatcher->dispatch(article.pre_save, $event); $ret = parent::save($con); $this->dispatcher->dispatch(article.post_save, $event); return $ret; }}
  16. 16. Propager un Evénement
  17. 17. namespace AFUPEvent;use SymfonyComponentEventDispatcherEvent;use AFUPModelArticle;class ArticleEvent extends Event{ private $article; public function __construct(Article $article) { $this->article = $article; } public function getArticle() { return $this->article; }}
  18. 18. Ajouter des écouteurs
  19. 19. namespace AFUPListener;use AFUPEventArticleEvent;use dflydevmarkdownMarkdownParser;class ArticleListener{ public function onPreSave(ArticleEvent $event) { $article = $event->getArticle(); $markdown = $article->getContent(); $parser = new MarkdownParser(); $html = $parser->transformMarkdown($markdown); $article->setHtmlContent($html); }}
  20. 20. namespace AFUPListener;use ZendSearchLuceneDocument;use ZendSearchLuceneDocumentField;use AFUPEventArticleEvent;use AFUPModelArticlePeer;class LuceneListener{ public function onPostSave(ArticleEvent $event) { $article = $event->getArticle(); // ... }}
  21. 21. namespace AFUPListener;// ...class LuceneListener{ public function onPostSave(ArticleEvent $event) { $article = $event->getArticle(); $index = ArticlePeer::getLuceneIndex(); // remove existing entries foreach ($index->find(pk:.$article->getId()) as $hit) { $index->delete($hit->id); } $doc = new Document(); $doc->addField(Field::Keyword(pk, $article->getId())); $doc->addField(Field::UnStored(title, $article->getTitle())); $doc->addField(Field::UnStored(content, $article->getContent())); $index->addDocument($doc); $index->commit(); }}
  22. 22. Enregistrer les écouteurs
  23. 23. use SymfonyComponentEventDispatcherEventDispatcher;use AFUPListenerArticleListener;use AFUPListenerLuceneListener;use AFUPModelArticle;// Déclaration des écouteurs$listener1 = array(new ArticleListener(), onPreSave);$listener2 = array(new LuceneListener(), onPostSave);// Enregistrement des écouteurs$dispatcher = new EventDispatcher();$dispatcher->addListener(article.pre_save, $listener1);$dispatcher->addListener(article.post_save, $listener2);
  24. 24. $article = new Article();$article->setDispatcher($dispatcher);$article->setTitle(AFUP Design Patterns);$article->setMarkdownContent( Some **markdown** content);$article->save();
  25. 25. Dependency Injection
  26. 26. MauvaiseConceptionInitiale
  27. 27. class Mailer{ public function send(Message $message) { try { $transport = new SMTPTransport( smtp.foo.com, 1234, mailer, p$wD^ ); return $transport->send($message); } catch (TransportException $e) { $logger = Logger::getInstance(); $logger->log(Unable to send message to...); $logger->logException($e); throw $e; } }}
  28. 28. $message = new Message();$message->setFrom(me@example.com);$message->setTo(you@example.com);$message->setSubject(Bonjour ...);$message->setBody(Hello ...);$mailer = new Mailer();$mailer->send($message);
  29. 29. Ca fonctionne !
  30. 30. Oui mais ?!!!
  31. 31. $transport = new SMTPTransport( smtp.foo.com, 1234, mailer, p$wD^);
  32. 32. Je veux utiliserun transportdifférent…
  33. 33. Je veux configurerle SMTP en dev eten prod…
  34. 34. $logger = Logger::getInstance();$logger->log(Unable to...);$logger->logException($e);
  35. 35. Si le loggern’existe pas ?
  36. 36. Si je veux changerla configurationdu logger?
  37. 37. Je veux tester moncode avec PHPUnitet je n’y arrivepas…
  38. 38. La Solution?
  39. 39. Injecter sesdépendances auMailer
  40. 40. What ???
  41. 41. Injection parles propriétés
  42. 42. class Mailer{ public $transport; public function send(Message $message) { try { $this->transport->send($message); } catch (TransportException $e) { // ... } }}
  43. 43. $message = Message();$message->setFrom(me@example.com);$message->setTo(you@example.com);$message->setSubject(Bonjour ...);$message->setBody(Hello ...);$transport = new SMTPTransport(...);$mailer = new Mailer();$mailer->transport = $transport;$mailer->send($message);
  44. 44. Injection parconstructeur
  45. 45. class Mailer{ private $transport; function __construct(Transport $t) { $this->transport = $t; }}
  46. 46. $message = Message();$message->setFrom(me@example.com);// ...$transport = new SMTPTransport(...);$mailer = new Mailer($transport);$mailer->send($message);
  47. 47. Injection parun mutateur
  48. 48. class Mailer{ private $logger; function setLogger(Logger $logger) { $this->logger = $logger; }}
  49. 49. class Mailer{ // ... public function send(Message $message) { try { $this->transport->send($message); } catch (TransportException $e) { if (null !== $this->logger) { $this->logger->log(...); $this->logger->logException($e); throw $e; } } }}
  50. 50. $message = Message();// ...$logger = new FileLogger(/to/dev.log);$transport = new SMTPTransport(...);$mailer = new Mailer($transport);$mailer->setLogger($logger);$mailer->send($message);
  51. 51. Découplageavec lesinterfaces
  52. 52. class Mailer{ function __construct(TransportInterface $t) { $this->transport = $t; } function setLogger(LoggerInterface $logger) { $this->logger = $logger; }}
  53. 53. class SMTPTransport implements TransportInterface{}class MailTransport implements TransportInterface{}class NullTransport implements TransportInterface{}
  54. 54. Bénéfices vsPertes ?!
  55. 55. Configurabilité Modularité Testabilité
  56. 56. Construction un peu plus Complexe
  57. 57. Inversion de Contrôle
  58. 58. # composer.json{ "require": { "pimple/pimple": "1.0.*" }}
  59. 59. Global Configuration + Lazy Services = Container
  60. 60. ParamètresGlobaux deConfiguration
  61. 61. $pimple = new Pimple();$pimple[logger.file] = /path/to/dev.log;$pimple[logger.severity] = 200;$pimple[transport.smtp.host] = smtp.foo.com;$pimple[transport.smtp.port] = 1234;$pimple[transport.smtp.user] = mailer;$pimple[transport.smtp.passwd] = ^p4$$W0rD*;
  62. 62. Enregistrerdes services
  63. 63. $pimple[logger] = $pimple->share(function ($c) { if (!is_writable($c[logger.file])) { throw new Exception(...); } $logger = new Logger($c[logger.file]); if (isset($c[logger.severity])) { $logger->setSeverity($c[logger.severity]); } return $logger;});
  64. 64. $pimple[mailer.transport] = $pimple->share(function ($c) { return new SMTPTransport( $c[transport.smtp.host], $c[transport.smtp.port], $c[transport.smtp.user], $c[transport.smtp.passwd] );});
  65. 65. $pimple[mailer] = $pimple->share(function ($c) { $mailer = new Mailer($c[mailer.transport]); if (isset($c[logger])) { $mailer->setLogger($c[logger]); } return $mailer;});
  66. 66. Initialisation desservices à lademande
  67. 67. $pimple = new Pimple();$pimple[logger.file] = /path/to/dev.log;$pimple[logger.severity] = 200;// ...$message = Message();$message->setFrom(me@example.com);// ...// Création à la demande du mailer$pimple[mailer]->send($message);

×