Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Design Patterns avec PHP 5.3, Symfony et Pimple

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

  • Als Erste(r) kommentieren

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);

×