Design Patterns avec PHP 5.3, Symfony et Pimple

  • 4,732 views
Uploaded 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 …

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

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • Les design patterns répondent à des problématiques bien précises. Il faut savoir les utiliser au bon moment et à bon escient.
    Are you sure you want to
    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
    Are you sure you want to
    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.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
4,732
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
66
Comments
3
Likes
3

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Simplifiez-vous les Design Patterns avec PHP 5.3 Hugo Hamon – 12/07/12
  • 2. ObservateurDependency InjectionInversion de Contrôle
  • 3. Observer
  • 4. Un sujet, l’objetobservable, émet un signalà des modules qui jouentle rôle d’observateurs.
  • 5. Event Dispatcher
  • 6. Le Dispatcheur est un objetqui gère les connexionsentre le sujet observé et sesobservateurs (écouteurs).
  • 7. # composer.json{ "require": { "php": ">=5.3.3", "symfony/event-dispatcher": "2.1.*" }}
  • 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. Mise en Pratique
  • 10. use AFUPModelArticle;$article = new Article();$article->setTitle(AFUP Design Patterns);$article->setContent(Some **content**);$article->save();echo $article->getHtmlContent();
  • 11. <p> Some <strong>content</strong></p>
  • 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. Le Sujet Observé
  • 14. namespace AFUPModel;use SymfonyComponentEventDispatcherEventDispatcher;use AFUPModelBaseArticle as BaseArticle;class Article extends BaseArticle{ private $dispatcher; public function setDispatcher(EventDispatcher $dispatcher) { $this->dispatcher = $dispatcher; }}
  • 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. Propager un Evénement
  • 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. Ajouter des écouteurs
  • 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. namespace AFUPListener;use ZendSearchLuceneDocument;use ZendSearchLuceneDocumentField;use AFUPEventArticleEvent;use AFUPModelArticlePeer;class LuceneListener{ public function onPostSave(ArticleEvent $event) { $article = $event->getArticle(); // ... }}
  • 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. Enregistrer les écouteurs
  • 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. $article = new Article();$article->setDispatcher($dispatcher);$article->setTitle(AFUP Design Patterns);$article->setMarkdownContent( Some **markdown** content);$article->save();
  • 25. Dependency Injection
  • 26. MauvaiseConceptionInitiale
  • 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. $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. Ca fonctionne !
  • 30. Oui mais ?!!!
  • 31. $transport = new SMTPTransport( smtp.foo.com, 1234, mailer, p$wD^);
  • 32. Je veux utiliserun transportdifférent…
  • 33. Je veux configurerle SMTP en dev eten prod…
  • 34. $logger = Logger::getInstance();$logger->log(Unable to...);$logger->logException($e);
  • 35. Si le loggern’existe pas ?
  • 36. Si je veux changerla configurationdu logger?
  • 37. Je veux tester moncode avec PHPUnitet je n’y arrivepas…
  • 38. La Solution?
  • 39. Injecter sesdépendances auMailer
  • 40. What ???
  • 41. Injection parles propriétés
  • 42. class Mailer{ public $transport; public function send(Message $message) { try { $this->transport->send($message); } catch (TransportException $e) { // ... } }}
  • 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. Injection parconstructeur
  • 45. class Mailer{ private $transport; function __construct(Transport $t) { $this->transport = $t; }}
  • 46. $message = Message();$message->setFrom(me@example.com);// ...$transport = new SMTPTransport(...);$mailer = new Mailer($transport);$mailer->send($message);
  • 47. Injection parun mutateur
  • 48. class Mailer{ private $logger; function setLogger(Logger $logger) { $this->logger = $logger; }}
  • 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. $message = Message();// ...$logger = new FileLogger(/to/dev.log);$transport = new SMTPTransport(...);$mailer = new Mailer($transport);$mailer->setLogger($logger);$mailer->send($message);
  • 51. Découplageavec lesinterfaces
  • 52. class Mailer{ function __construct(TransportInterface $t) { $this->transport = $t; } function setLogger(LoggerInterface $logger) { $this->logger = $logger; }}
  • 53. class SMTPTransport implements TransportInterface{}class MailTransport implements TransportInterface{}class NullTransport implements TransportInterface{}
  • 54. Bénéfices vsPertes ?!
  • 55. Configurabilité Modularité Testabilité
  • 56. Construction un peu plus Complexe
  • 57. Inversion de Contrôle
  • 58. # composer.json{ "require": { "pimple/pimple": "1.0.*" }}
  • 59. Global Configuration + Lazy Services = Container
  • 60. ParamètresGlobaux deConfiguration
  • 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. Enregistrerdes services
  • 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. $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. $pimple[mailer] = $pimple->share(function ($c) { $mailer = new Mailer($c[mailer.transport]); if (isset($c[logger])) { $mailer->setLogger($c[logger]); } return $mailer;});
  • 66. Initialisation desservices à lademande
  • 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);