Symfony Components 2.0 on PHP 5.3

  • 22,196 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
22,196
On Slideshare
0
From Embeds
0
Number of Embeds
6

Actions

Shares
Downloads
206
Comments
0
Likes
13

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. Symfony Components What's in for you? Fabien Potencier
  • 2. Fabien Potencier •  Serial entrepreneur and developer by passion •  Founder of Sensio (in 1998) –  A services and consulting company specialized in Web technologies and Internet marketing (France and USA) –  70 people –  Open-Source specialists –  Big corporate customers –  Consulting, training, development, web design, … and more –  Sponsor of a lot of Open-Source projects like symfony and Doctrine
  • 3. Fabien Potencier •  Creator and lead developer of symfony… •  and creator and lead developer of some more OSS: –  symfony components –  Swift Mailer : Powerful component based mailing library for PHP –  Twig : Fexible, fast, and secure template language for PHP –  Pirum : Simple PEAR Channel Server Manager –  Sismo : PHP continuous integration server –  Lime : Easy to use unit testing library for PHP –  Twitto : A web framework in a tweet –  Twittee : A Dependency Injection Container in a tweet –  Pimple : A small PHP 5.3 dependency injection container
  • 4. Fabien Potencier •  Read my technical blog: http://fabien.potencier.org/ •  Follow me on Twitter: @fabpot •  Fork my code on Github: http://github.com/fabpot/
  • 5. How many of you use symfony?
  • 6. symfony •  Full-stack framework (MVC architecture) •  symfony provides the infrastructure/tools needed for 95% of the web projects •  Open-Source (MIT License) since 2005 •  Based on –  11 years of Sensio experience building websites for its customers –  Existing Open-Source projects
  • 7. But wait, symfony is a monolithic framework, right?
  • 8. A bit of history symfony 1.0 (January 2007) started as a glue between existing Open-Source libraries
  • 9. A bit of history symfony 1.1 (June 2008) was a big refactoring of the code base –  Decoupled the main component: like Forms, Routing, Cache, YAML, ORMs, … sfRequest sfRouting sfLogger sfI18N sfUser sfResponse sfYAML sfDatabase sfForm sfEventDispatcher sfStorage sfCache sfOutputEscaper sfValidator sfWidget sfCoreAutoload platform
  • 10. The symfony (1.2/1.3/1.4) MVC framework is based on a set of cohesive but decoupled classes, the symfony components
  • 11. Symfony Components •  Announced in May 2009 •  Standalone components •  Packaged individually •  No dependencies •  Release cycle independent of Symfony, the framework
  • 12. Symfony Components Dedicated website for each component (with code and documentation) http://components.symfony-project.org/ Dedicated Subversion and Git repository http://svn.symfony-project.com/components/ http://github.com/fabpot
  • 13. Symfony Components They have been migrated to PHP 5.3 http://svn.symfony-project.com/branches/2.0/lib/Symfony/Components
  • 14. Symfony Components Each “old” PHP 5.2 symfony component has been branched and a 1.0 version will be released soon http://svn.symfony-project.com/components/
  • 15. Symfony Components •  Extracted from symfony 1 –  Event Dispatcher –  YAML –  Output Escaper •  Written from scratch for Symfony 2 –  Dependency Injection Container –  Request Handler –  Templating •  We don’t want to duplicate effort done by Zend Framework
  • 16. Symfony Components •  Coming soon… –  Request Handler –  Form –  Command Line Tools –  Routing –  Security –  Cache –  Debug –  …
  • 17. Symfony Components Using them in your project
  • 18. Which version? •  This presentation is about the components based on PHP 5.3 •  The stable 1.0 branch based on PHP 5.2 has the same feature set and work exactly in the same way, except: –  The autoloader –  The class names
  • 19. Symfony 2.0 Namespaces SymfonyComponents SymfonyFoundation SymfonyFramework
  • 20. Autoloader $path = '/path/to/SymfonyComponents/lib'; require_once $path.'/Symfony/Foundation/ClassLoader.php'; $loader = new SymfonyFoundationClassLoader('Symfony', $path); $loader->register();
  • 21. PHP 5.3 technical interoperability standards « … describes the mandatory requirements that must be adhered to for autoloader interoperability » http://groups.google.com/group/php-standards/web/psr-0-final-proposal
  • 22. What does this mean? •  PHP libraries will finally be 100% technically interoperable and have optimum autoloading performance •  Symfony 2.0 + Doctrine 2.0 + Zend Framework 2.0 •  Load classes from 3 different libraries with one autoloader and one include path •  Also able to load classes implementing the PEAR naming convention •  Share SplClassLoader implementation: –  http://gist.github.com/221634 •  Implement SplClassLoader in C/PHP :)
  • 23. Symfony Components YAML
  • 24. SymfonyComponentsYAML
  • 25. YAML •  YAML is a human friendly data serialization standard for all programming languages (implementations exist in Perl, Ruby, Python, Java, …) •  YAML is a great format for your configuration files •  YAML files are as expressive as XML files and as readable as INI files
  • 26. YAML •  Used in symfony for all configuration files •  Used in Doctrine for schema and fixtures •  Used in PHPUnit for TAP output and for DataSet (DbUnit)
  • 27. config: key: value foo: [foo, bar] bar: { foo: bar } foobar: { foo: [foo, bar] }
  • 28. use SymfonyComponentsYAMLYAML; // Parse YAML $config = YAML::load(<<<'EOF' config: key: value foo: [foo, bar] bar: { foo: bar } foobar: { foo: [foo, bar] } EOF ); // Dump YAML echo YAML::dump($config);
  • 29. Symfony Components Event Dispatcher
  • 30. SymfonyComponentsEventDispatcher
  • 31. Event Dispatcher •  Symfony Event Dispatcher is a PHP library that provides a lightweight implementation of the Observer design pattern •  Based on the Cocoa Notification Center •  Used in symfony to provide hooks and allow customizations of default behaviors sfRequest sfRouting sfLogger sfI18N sfUser sfResponse –  Inject the Web Debug Toolbar sfYAML sfDatabase sfForm sfEventDispatcher sfStorage sfCache sfOutputEscaper –  Inject methods into core Objects sfValidator sfWidget sfCoreAutoload platform –  I18n management –  …
  • 32. An object (the subject) maintains a list of its dependents (observers) and notifies them automatically of any state changes, usually by calling one of their methods http://en.wikipedia.org/wiki/Observer_pattern
  • 33. An event dispatcher is a central object that manages connections between subjects and observers
  • 34. It is a powerful mechanism to extend applications without having to change the objects themselves Associated words: hook, listener, event, subject, …
  • 35. use SymfonyComponentsEventDispatcherEvent; use SymfonyComponentsEventDispatcherEventDispatcher; $dispatcher = new EventDispatcher(); // an anonymous listener $listener = function (Event $event) { echo "Hello {$event['name']}n"; }; // register the listener (any PHP callable) with the dispatcher $dispatcher->connect('foo', $listener); // notify the event somewhere $event = new Event(null, 'foo', array('name' => 'Fabien')); $dispatcher->notify($event);
  • 36. Symfony Event Dispatcher •  The implementation is simple –  No interface to implement –  No need to create an event class for each event –  An event is just a unique identifier –  Some conventions (parameter names)
  • 37. Symfony Event Dispatcher •  Advantages –  Very simple to use –  Easy to add new arguments to the listener –  Very fast –  Very easy to add a new event, just notify it with a unique name •  Disadvantages –  The contract between the listener and the notifier is quite loose
  • 38. Notifications •  Notify : all listeners are called in turn, no feedback to the notifier –  Logging, … •  Notify Until : all listeners are called until one « processes » the event. The listener that has processed the event can return something to the caller –  Exceptions, Method not found in __call(), … •  Filter : Each listener filters a given value and can change it. The filtered value is returned to the caller –  HTML content, parameters, …
  • 39. class Response { protected $content, $dispatcher; public function __construct(EventDispatcher $dispatcher, $content) { $this->dispatcher = $dispatcher; $this->content = $content; } public function send() { $event = new Event($this, 'response.filter_content'); $this->dispatcher->filter($event, $this->content); echo $event->getReturnValue(); } }
  • 40. $dispatcher = new EventDispatcher(); // an anonymous listener $listener = function (Event $event, $content) { return '*'.$content.'*'; }; // register the listener $dispatcher->connect('response.filter_content', $listener); $response = new Response($dispatcher, 'Hello'); $response->send();
  • 41. Notifiers Dispatcher Listeners listener connects 1 to ‘response.filter_content’ Response notifies Calls listener 2 ‘response.filter_content’ all listeners is called
  • 42. The Response object knows nothing about the listeners The listener objects know nothing about the Response one They communicate through the Dispatcher object Anybody can listen to the ‘response.filter_content’ event and acts accordingly
  • 43. Notifiers Dispatcher Listeners listener connects 1 to response.filter_content Your class connects to response.filter_content Response notifies Calls listener 2 response.filter_content all listeners is called Your class callback is called
  • 44. Notifiers Dispatcher Response notifies Calls response.filter_content nothing very small overhead
  • 45. Symfony Components Templating
  • 46. SymfonyComponentsTemplating
  • 47. Symfony Templating •  Thin layer on top of PHP that adds some template-oriented features •  Can work with Template Engines (Smarty, Twig, …)
  • 48. New Templating Framework •  4 sub-components –  Template Engine –  Template Renderers –  Template Loaders –  Template Storages
  • 49. use SymfonyComponentsTemplatingEngine; use SymfonyComponentsTemplatingLoaderFilesystemLoader; $loader = new FilesystemLoader( '/path/to/templates/%name%.php' ); $t = new Engine($loader); echo $t->render('index', array('name' => 'Fabien'));
  • 50. Template Loaders •  No assumption about where and how templates are to be found –  Filesystem –  Database –  Memory, … •  Built-in loaders: FilesystemLoader, ChainLoader, CacheLoader •  Template names are « logical » names: $loader = new FilesystemLoader('/path/to/templates/%name%.php');
  • 51. Template Renderers •  No assumption about the format of the templates •  Template names are prefixed with the renderer name: –  index == php:index –  user:index $t = new Engine($loader, array( 'user' => new ProjectTemplateRenderer(), 'php' => new PhpRenderer(), ));
  • 52. Template Embedding Hello <?php echo $name ?> <?php $this->render('embedded', array('name' => $name)) ?> <?php $this->render('smarty:embedded') ?>
  • 53. Template Inheritance <?php $this->extend('layout') ?> Hello <?php echo $name ?> <html> <head> </head> <body> <?php $this->output('content') ?> </body> </html>
  • 54. Template Slots <html> <head> <title><?php $this->output('title') ?></title> </head> <body> <?php $this->output('content') ?> </body> </html> <?php $this->set('title', 'Hello World! ') ?> <?php $this->start('title') ?> Hello World! <?php $this->stop() ?>
  • 55. Template Multiple Inheritance A layout can be decorated by another layout Each layout can override slots
  • 56. Templating A CMS example
  • 57. http://fabien.potencier.org/talk/33/php-barcelona-symfony-2-0-on-PHP-5-3?position=76
  • 58. Symfony Components Dependency Injection
  • 59. SymfonyComponentsDependencyInjection
  • 60. « Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields. » http://www.picocontainer.org/injection.html
  • 61. class Message { public function __construct() { $this->output = new Output(); } } class Message { public function __construct(OutputInterface $output) { $this->output = $output; } }
  • 62. DI Hello World example class Message { public function __construct(OutputInterface $output, array $options) { $this->output = $output; $this->options = array_merge(array('with_newline' => false), $options); } public function say($msg) { $this->output->render($msg.($this->options['with_newline'] ? "n" : '')); } }
  • 63. DI Hello World example interface OutputInterface { public function render($msg); } class Output implements OutputInterface { public function render($msg) { echo $msg; } } class FancyOutput implements OutputInterface { public function render($msg) { echo sprintf("033[33m%s033[0m", $msg); } }
  • 64. DI Hello World example $output = new FancyOutput(); $message = new Message($output, array('with_newline' => true)); $message->say('Hello World');
  • 65. A DI container facilitates objects description and object relationships, configures and instantiates objects
  • 66. DI Container Hello World example use SymfonyComponentsDependencyInjectionBuilder; use SymfonyComponentsDependencyInjectionReference; $container = new Builder(); $container->register('output', 'FancyOutput'); $container-> register('message', 'Message')-> setArguments(array(new Reference('output'), array('with_newline' => true))) ; $container->message->say('Hello World!');
  • 67. $message = $container->message; Get the configuration for the message service The Message constructor must be given an output service Get the output object from the container Create a Message object by passing the constructor arguments
  • 68. $message = $container->message; is roughly equivalent to $output = new FancyOutput(); $message = new Message($output, array('with_newline' => true));!
  • 69. $container = new Builder(); $container->register('output', 'FancyOutput'); $container-> register('message', 'Message')-> setArguments(array(new Reference('output'), array('with_newline' => true))) ; $container->message->say('Hello World!'); PHP <container xmlns="http://symfony-project.org/2.0/container"> <services> XML <service id="output" class="FancyOutput" /> <service id="message" class="Message"> XML is validated <argument type="service" id="output" /> against an XSD <argument type="collection"> <argument key="with_newline">true</argument> </argument> </service> </services> </container> $container = new Builder(); $loader = new XmlFileLoader($container); $loader->load('services.xml');
  • 70. $container = new Builder(); $container->register('output', 'FancyOutput'); $container-> register('message', 'Message')-> setArguments(array(new sfServiceReference('output'), array('with_newline' => true))) ; $container->message->say('Hello World!'); PHP services: output: { class: FancyOutput } message: YAML class: Message arguments: - @output - { with_newline: true } $container = new Builder(); $loader = new YamlFileLoader($container); $loader->load('services.yml');
  • 71. <container xmlns="http://symfony-project.org/2.0/container"> <parameters> <parameter key="output.class">FancyOutput</parameter> <parameter key="message.options" type="collection"> <parameter key="with_newline">true</parameter> </parameter> </parameters> <services> <service id="output" class="%output.class%" /> <service id="message" class="Message"> <argument type="service" id="output" /> <argument>%message.options%</argument> </service> </services> </container> $container = new Builder(); $loader = new XmlFileLoader($container); $loader->load('services.xml');
  • 72. <container xmlns="http://symfony-project.org/2.0/container"> <imports> <import resource="config.xml" /> </imports> <services> <service id="output" class="%output.class%" /> <service id="message" class="Message"> <argument type="service" id="output" /> <argument>%message.options%</argument> </service> </services> </container> <container xmlns="http://symfony-project.org/2.0/container"> <parameters> <parameter key="output.class">FancyOutput</parameter> <parameter key="message.options" type="collection"> <parameter key="with_newline">true</parameter> </parameter> </parameters> </container> $container = new Builder(); $loader = new FileXmlFileLoader($container); $loader->load('services.xml');
  • 73. <services> <import resource="config.yml" class="SymfonyComponentsDependencyInjection LoaderYamlFileLoader" /> <service id="output" class="%output.class%" /> <service id="message" class="Message"> <argument type="service" id="output" /> <argument>%message.options%</argument> </service> </services> parameters: output.class: FancyOutput message.options: { with_newline: true } $container = new Builder(); $loader = new XmlFileLoader($container); $loader->load('services.xml');
  • 74. Loaders & Dumpers •  IniFileLoader •  XmlFileLoader •  YamlFileLoader •  XmlDumper •  YamlDumper Make your container •  PhpDumper VERY fast •  GraphvizDumper
  • 75. use SymfonyComponentsDependencyInjectionBuilder; use SymfonyComponentsDependencyInjectionReference; use SymfonyComponentsDependencyInjectionDumperXmlDumper; use SymfonyComponentsDependencyInjectionDumperYamlDumper; use SymfonyComponentsDependencyInjectionDumperPhpDumper; use SymfonyComponentsDependencyInjectionLoaderXmlFileLoader; use SymfonyComponentsDependencyInjectionLoaderYamlFileLoader; $container = new Builder(); $container->register('output', 'FancyOutput'); $container-> register('message', 'Message')-> setArguments(array(new Reference('output'), array('with_newline' => true))) ;
  • 76. $dumper = new XmlDumper($container); file_put_contents(__DIR__.'/container.xml', $dumper->dump()); $loader = new XmlFileLoader($container); $loader->load(__DIR__.'/container.xml'); $dumper = new YamlDumper($container); file_put_contents(__DIR__.'/container.yml', $dumper->dump()); $loader = new YamlFileLoader($container); $loader->load(__DIR__.'/container.yml'); $dumper = new PhpDumper($container); echo $dumper->dump();
  • 77. use SymfonyComponentsDependencyInjectionContainer; use SymfonyComponentsDependencyInjectionReference; use SymfonyComponentsDependencyInjectionParameter; class ProjectServiceContainer extends Container { protected $shared = array(); protected function getOutputService() { if (isset($this->shared['output'])) return $this->shared['output']; $instance = new FancyOutput(); return $this->shared['output'] = $instance; } protected function getMessageService() { if (isset($this->shared['message'])) return $this->shared['message']; $instance = new Message($this->getService('output'), array('with_newline' => true)); return $this->shared['message'] = $instance; } }
  • 78. use SymfonyComponentsDependencyInjectionDumperGraphvizDumper; $dumper = new GraphvizDumper($container); echo $dumper->dump();
  • 79. digraph sc { ratio="compress" node [fontsize="11" fontname="Arial" shape="record"]; edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; node_output [label="outputnFancyOutputn", shape=record, fillcolor="#eeeeee", style="filled"]; node_message [label="messagenMessagen", shape=record, fillcolor="#eeeeee", style="filled"]; node_service_container [label="service_containernSymfonyComponentsDependencyInjectionBuilder n", shape=record, fillcolor="#9999ff", style="filled"]; node_message -> node_output [label="" style="filled"]; }
  • 80. Symfony Components Output Escaper
  • 81. SymfonyComponentsOutputEscaper
  • 82. Output Escaper •  Provides automatic XSS protection for your templates •  By wrapping template variables •  Works for –  strings –  arrays –  objects •  properties •  methods •  __call(), __get(), … •  Iterators, Coutables, … •  … •  Works for deep method calls
  • 83. use SymfonyComponentsOutputEscaperEscaper; $title = 'Foo <br />'; echo Escaper::escape(ESC_SPECIALCHARS, $title);
  • 84. use SymfonyComponentsOutputEscaperEscaper; $article = array( 'title' => 'Foo <br />', 'author' => array( 'name' => 'Fabien <br/>', ) ); $article = Escaper::escape(ESC_SPECIALCHARS, $article); echo $article['title']."n"; echo $article['author']['name']."n";
  • 85. class Article { protected $title; protected $author; public $full_title; public property public function __construct($title, Author $author) { $this->title = $title; $this->full_title = $title; $this->author = $author; } public method public function getTitle() { return $this->title; } public method returning public function getAuthor() { return $this->author; } public function __get($key) { return $this->$key; } another object public function __call($method, $arguments) magic __get() { return $this->{'get'.$method}(); magic __call() } }
  • 86. class Author { protected $name; public function __construct($name) { $this->name = $name; } public function getName() { return $this->name; } }
  • 87. use SymfonyComponentsOutputEscaperEscaper; $article = new Article( 'foo <br />', new Author('Fabien <br />') ); $article = Escaper::escape(ESC_SPECIALCHARS, $article); echo $article->getTitle()."n"; echo $article->getAuthor()->getName()."n"; echo $article->full_title."n"; echo $article->title."n"; echo $article->title()."n";
  • 88. Escaping Strategy explicitly ask for raw data echo $article->getHtmlContent(ESC_RAW); echo $article->getTitle(ESC_JS); change the default escaping strategy
  • 89. symfony-live.com with Matthew Weier O’Pheinney I will reveal the first alpha release of Symfony 2.0!
  • 90. symfony-live.com with Matthew Weier O’Pheinney … with Matthew Weier O'Phinney as a special guest
  • 91. Thank you! My slides on slideshare.com/fabpot with a bonus: How to use Symfony Components with ZF!
  • 92. Sensio S.A. 92-98, boulevard Victor Hugo 92 115 Clichy Cedex FRANCE Tél. : +33 1 40 99 80 80 Contact Fabien Potencier fabien.potencier at sensio.com http://www.sensiolabs.com/ http://www.symfony-project.org/ http://fabien.potencier.org/
  • 93. BO NUS How to use Symfony Components within a Zend Framework project?
  • 94. Symfony components in a ZF project •  Symfony Components can make your ZF application better –  More configurable: symfony YAML –  More flexible: symfony Event Dispatcher –  Faster: symfony Dependency Injection –  More secure: symfony Output Escaper •  … and more fun of course
  • 95. Symfony Components in a ZF project •  The examples in this presentation are just to get you started faster •  So, be creative with them. They open all kind of opportunities for your next Zend Framework project •  And please, give me feedback, and tell me what you do with the Symfony Components
  • 96. Symfony Components Event Dispatcher
  • 97. class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { public function run() { require_once '/path/to/sfEventDispatcher.php'; $dispatcher = new sfEventDispatcher(); $event = new sfEvent(null, 'bootstrap.prerun'); $dispatcher->notify($event); parent::run(); $event = new sfevent(null, 'bootstrap.postrun'); $dispatcher->notify($event); } }
  • 98. Symfony Components Dependency Injection
  • 99. class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { public function getContainer() { if (null === $this->_container) { $this->setContainer($this->_initContainer()); } return $this->_container; } protected function _initContainer() { require_once '/path/to/sfServiceContainerAutoloader.php'; sfServiceContainerAutoloader::register(); Example from Ben Eberlei (he rocks!): http://www.whitewashing.de/blog/articles/118 $container = new sfServiceContainerBuilder(); $loader = new sfServiceContainerLoaderFileXml($container); $loader->load(dirname(__FILE__).'/configs/resources.xml'); return $container; } }
  • 100. <?xml version="1.0" ?> <container xmlns="http://symfony-project.org/2.0/container"> <parameters> <parameter key="mailer.username">foo</parameter> <parameter key="mailer.password">bar</parameter> <parameter key="mailer.class">Zend_Mail</parameter> </parameters> <services> <service id="mail.transport" class="Zend_Mail_Transport_Smtp" shared="false"> <argument>smtp.gmail.com</argument> <argument type="collection"> <argument key="auth">login</argument> <argument key="username">%mailer.username%</argument> <argument key="password">%mailer.password%</argument> <argument key="ssl">ssl</argument> <argument key="port">465</argument> </argument> </service> <service id="mailer" class="%mailer.class%"> <call method="setDefaultTransport"> <argument type="service" id="mail.transport" /> </call> </service> </services> </container>
  • 101. parameters: mailer.username: foo mailer.password: bar mailer.class: Zend_Mail services: mail.transport: class: Zend_Mail_Transport_Smtp arguments: [smtp.gmail.com, { auth: login, username: %mailer.username%, password: %mailer.password%, ssl: ssl, port: 465 }] shared: false mailer: class: %mailer.class% calls: - [setDefaultTransport, [@mail.transport]]
  • 102. class GuestbookController extends Zend_Controller_Action { public function indexAction() { $guestbook = new Default_Model_Guestbook(); $this->view->entries = $guestbook->fetchAll(); $container = $this->getInvokeArg('bootstrap')->getContainer(); $mailer = $container->mailer; } }
  • 103. Dependency Injection Container •  By default in ZF, new resources can be added to the container but cannot be lazy-loaded –  All resources used by Zend_Application are loaded on every request •  By using symfony Service Container, the resources are lazy-loaded –  Instances and their dependencies are created the first time you get them •  Interesting for resources like DB
  • 104. Symfony Components Output Escaper
  • 105. require_once '/path/to/sfOutputEscaperAutoloader.php'; sfOutputEscaperAutoloader::register(); class My_View extends Zend_View { public function __set($key, $val) { if ('_' === substr($key, 0, 1)) { // throw an exception } $this->$key = sfOutputEscaper::escape(array($this, 'escape'), $val); } }
  • 106. <dl> <?php foreach ($this->entries as $entry): ?> <dt><?php echo $this->escape($entry->email) ?></dt> <dd><?php echo $this->escape($entry->comment) ?></dd> <?php endforeach ?> </dl> <dl> <?php foreach ($this->entries as $entry): ?> <dt><?php echo $entry->email ?></dt> <dd><?php echo $entry->comment ?></dd> <?php endforeach ?> </dl> <?php echo $entry->getRawValue()->comment ?> <?php echo $entry->getComment(ESC_RAW) ?>