Symfony2 revealed

  • 35,131 views
Uploaded on

Symfony2 revealed

Symfony2 revealed

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
35,131
On Slideshare
0
From Embeds
0
Number of Embeds
21

Actions

Shares
Downloads
693
Comments
0
Likes
54

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. Fabien Potencier
  • 2. A bit of history
  • 3. symfony 1.0 – January 2007 •  Started as a glue between existing Open-Source libraries: – Mojavi (heavily modified), Propel, Prado i18n, … •  Borrowed concepts from other languages and frameworks: – Routing, CLI, functional tests, YAML, Rails helpers… •  Added new concepts to the mix – Web Debug Toolbar, admin generator, configuration cascade, …
  • 4. symfony 1.2 – November 2008 •  Based on decoupled but cohesive components –  Forms, Routing, Cache, YAML, ORMs, … •  Controller still based on Mojavi –  View, Filter Chain, …
  • 5. symfony 1.4 – November 2009 •  Added some polish on existing features •  Removed the support for deprecated features •  Current LTS release, maintained until late 2012
  • 6. Symfony Components YAML Dependency Injection Container Event Dispatcher Templating Routing Console Output Escaper Request Handler …
  • 7. What is Symfony 2?
  • 8. Symfony 2 is the next version of the symfony framework… except Symfony now takes a S instead of a s
  • 9. Talk about Symfony 2 or symfony 1
  • 10. To make it clear: Symfony 1 does not make any sense
  • 11. symfony 2 does not make more sense
  • 12. Symfony 2
  • 13. Same philosophy, just better
  • 14. MVC
  • 15. hmmm, now that I think about it…
  • 16. …it’s now probably more a Fabien’s style framework than anything else
  • 17. Highly configurable Highly extensible Same Symfony Components Same great developer tools Full-featured
  • 18. Ok, but why a major version then?
  • 19. Symfony 2 has a brand new low-level architecture
  • 20. PHP 5.3
  • 21. A Quick Tour
  • 22. <?php require_once __DIR__.'/../blog/BlogKernel.php'; $kernel = new BlogKernel('prod', false); $kernel->run();
  • 23. <?php Everything is namespaced namespace ApplicationHelloBundleController; use SymfonyFrameworkWebBundleController; class HelloController extends Controller { public function indexAction($name) Variables come from the routing { return $this->render('HelloBundle:Hello:index', array('name' => $name)); } } Template name Variables to pass to the template
  • 24. Layout <?php $view->extend('HelloBundle::layout') ?> Hello <?php echo $name ?>!
  • 25. <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <?php $view->slots->output('_content') ?> </body> </html> Helpers are objects
  • 26. hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index
  • 27. hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 28. hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 29. hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 30. hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 31. hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 32. hello: pattern: /hello/:year/:month/:slug defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($slug, $year) { // ... } }
  • 33. Extremely Configurable
  • 34. Dependency Injection Container
  • 35. Replaces a lot of symfony 1 “things” sfConfig All config handlers sfProjectConfiguration / sfApplicationConfiguration sfContext (No Singleton anymore) The configuration cache system … and some more
  • 36. in one easy-to-master unified and cohesive package
  • 37. Thanks to the DIC, Configuration has never been so easy and so flexible
  • 38. Name your configuration files the way you want
  • 39. Store them where you want
  • 40. Use PHP, XML, YAML, or INI
  • 41. $configuration = new BuilderConfiguration(); $configuration->addResource(new FileResource(__FILE__)); $configuration ->mergeExtension('web.user', array('default_culture' => 'fr', 'session' => array('name' => 'SYMFONY', 'type' => 'Native', 'lifetime' => 3600))) ->mergeExtension('doctrine.dbal', array('dbname' => 'sfweb', 'username' => 'root')) ->mergeExtension('web.templating', array('escaping' => 'htmlspecialchars', 'assets_version' => 'SomeVersionScheme')) ->mergeExtension('swift.mailer', array('transport' => 'gmail', 'username' => 'fabien.potencier', 'password' => 'xxxxxx')) PHP   ;
  • 42. web.user: default_culture: fr session: { name: SYMFONY, type: Native, lifetime: 3600 } web.templating: escaping: htmlspecialchars assets_version: SomeVersionScheme doctrine.dbal: { dbname: sfweb, username: root, password: null } swift.mailer: transport: gmail username: fabien.potencier password: xxxxxxxx YAML  
  • 43. <web:user default_culture="fr"> <web:session name="SYMFONY" type="Native" lifetime="3600" /> </web:user> <web:templating escaping="htmlspecialchars" assets_version="SomeVersionScheme" /> <doctrine:dbal dbname="sfweb" username="root" password="" /> <swift:mailer transport="gmail" username="fabien.potencier" password="xxxxxxxx" /> XML  
  • 44. $configuration->mergeExtension('swift.mailer', array( 'transport' => 'gmail', 'username' => 'fabien.potencier', 'password' => 'xxxxxx', )); PHP  
  • 45. swift.mailer: transport: gmail username: fabien.potencier password: xxxxxxxx YAML  
  • 46. <swift:mailer transport="gmail" username="fabien.potencier" password="xxxxxxxx" /> XML  
  • 47. <?xml version="1.0" ?> <container xmlns="http://www.symfony-project.org/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance » xmlns:doctrine="http://www.symfony-project.org/schema/dic/doctrine" xmlns:zend="http://www.symfony-project.org/schema/dic/zend" xmlns:swift="http://www.symfony-project.org/schema/dic/swiftmailer" > XML  
  • 48. <?xml version="1.0" ?> <container xmlns="http://www.symfony-project.org/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance » xmlns:doctrine="http://www.symfony-project.org/schema/dic/doctrine" xmlns:zend="http://www.symfony-project.org/schema/dic/zend" xmlns:swift="http://www.symfony-project.org/schema/dic/swiftmailer" xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd http://www.symfony-project.org/schema/dic/doctrine http://www.symfony-project.org/schema/dic/doctrine/doctrine-1.0.xsd http://www.symfony-project.org/schema/dic/zend http:// www.symfony-project.org/schema/dic/zend/zend-1.0.xsd http://www.symfony-project.org/schema/dic/swiftmailer http://www.symfony-project.org/schema/dic/swiftmailer/swiftmailer-1.0.xsd »> XML  
  • 49. Inherit them as much as you want
  • 50. Mix and match configuration files written in any format useful when using third-party plugins
  • 51. <imports> <import resource="parent.xml" /> <import resource="config.yml" /> Mix and match formats <import resource="parameters.ini" /> </imports> <zend:logger priority="debug" path="%kernel.logs_dir%/%kernel.environment%.log" /> <web:debug exception="%kernel.debug%" toolbar="%kernel.debug%" ide="textmate" />
  • 52. You choose the format you want Pros Cons XML validation verbose (not that much) IDE completion & help YAML concise needs the YAML component simple to read no validation easy to change no IDE auto-completion PHP flexible no validation more expressive
  • 53. Store sensitive settings outside of your project
  • 54. <doctrine:dbal dbname="sfweb" username="root" password="SuperSecretPasswordThatAnyoneCanSee" />
  • 55. in a .htaccess or httpd.conf file SetEnv SYMFONY__DOCTRINE__DBAL__PASSWORD "foobar" %doctrine.dbal.password%
  • 56. Semantic Configuration
  • 57. <swift:mailer transport="gmail" username="fabien.potencier" password="xxxxxxxx" /> XML  
  • 58. <swift:mailer transport="smtp" encryption="ssl" auth_mode="login" host="smtp.gmail.com" username="fabien.potencier" password="xxxxxxxx" /> XML  
  • 59. <parameters> <parameter key="swiftmailer.class">Swift_Mailer</parameter> <parameter key="swiftmailer.transport.smtp.class">Swift_Transport_EsmtpTransport</parameter> <parameter key="swiftmailer.transport.smtp.host">smtp.gmail.com</parameter> <parameter key="swiftmailer.transport.smtp.port">25</parameter> <parameter key="swiftmailer.transport.smtp.encryption">ssl</parameter> <parameter key="swiftmailer.transport.smtp.username">fabien.potencier</parameter> <parameter key="swiftmailer.transport.smtp.password">xxxxxx</parameter> <parameter key="swiftmailer.transport.smtp.auth_mode">login</parameter> <parameter key="swiftmailer.init_file">swift_init.php</parameter> </parameters> <services> <service id="swiftmailer.mailer" class="%swiftmailer.class%"> <argument type="service" id="swiftmailer.transport" /> <file>%swiftmailer.init_file%</file> </service> <service id="swiftmailer.transport.smtp" class="%swiftmailer.transport.smtp.class%"> <argument type="service" id="swiftmailer.transport.buffer" /> <argument type="collection"> <argument type="service" id="swiftmailer.transport.authhandler" /> </argument> <argument type="service" id="swiftmailer.transport.eventdispatcher" /> <call method="setHost"><argument>%swiftmailer.transport.smtp.host%</argument></call> <call method="setPort"><argument>%swiftmailer.transport.smtp.port%</argument></call> <call method="setEncryption"><argument>%swiftmailer.transport.smtp.encryption%</argument></call> <call method="setUsername"><argument>%swiftmailer.transport.smtp.username%</argument></call> <call method="setPassword"><argument>%swiftmailer.transport.smtp.password%</argument></call> <call method="setAuthMode"><argument>%swiftmailer.transport.smtp.auth_mode%</argument></call> </service> <service id="swiftmailer.transport.buffer" class="Swift_Transport_StreamBuffer"> <argument type="service" id="swiftmailer.transport.replacementfactory" /> </service> <service id="swiftmailer.transport.authhandler" class="Swift_Transport_Esmtp_AuthHandler"> <argument type="collection"> <argument type="service"><service class="Swift_Transport_Esmtp_Auth_CramMd5Authenticator" /></argument> <argument type="service"><service class="Swift_Transport_Esmtp_Auth_LoginAuthenticator" /></argument> <argument type="service"><service class="Swift_Transport_Esmtp_Auth_PlainAuthenticator" /></argument> </argument> XML   </service> <service id="swiftmailer.transport.eventdispatcher" class="Swift_Events_SimpleEventDispatcher" /> <service id="swiftmailer.transport.replacementfactory" class="Swift_StreamFilters_StringReplacementFilterFactory" /> <service id="swiftmailer.transport" alias="swiftmailer.transport.smtp" /> </services>
  • 60. Creating DIC extensions is insanely simple
  • 61. Very Fast thanks to a Smart Caching mechanism it always knows when to flush the cache
  • 62. /** * Gets the 'swiftmailer.mailer' service. * * This service is shared. * This method always returns the same instance of the service. * * @return Swift_Mailer A Swift_Mailer instance. PHPDoc for auto-completion */ protected function getSwiftmailer_MailerService() { if (isset($this->shared['swiftmailer.mailer'])) return $this->shared['swiftmailer.mailer']; As fast as it could be $instance = new Swift_Mailer($this->getSwiftmailer_Transport_SmtpService()); return $this->shared['swiftmailer.mailer'] = $instance; }
  • 63. The DIC can manage ANY PHP object (POPO)
  • 64. Plugins…
  • 65. or Bundles
  • 66. Plugins are first-class citizens They are called Bundles
  • 67. Everything is a bundle Core features Third-party code Application code
  • 68. app/ src/ web/
  • 69. app/ AppKernel.php cache/ config/ console logs/
  • 70. src/ autoload.php Application/ Bundle/ vendor/ doctrine/ swiftmailer/ symfony/ zend/
  • 71. web/ index.php index_dev.php
  • 72. .../ SomeBundle/ Bundle.php Controller/ Model/ Resources/ config/ views/
  • 73. public function registerBundleDirs() { return array( 'Application' => __DIR__.'/../src/Application', 'Bundle' => __DIR__.'/../src/Bundle', 'SymfonyFramework' => __DIR__.'/../src/vendor/ symfony/src/Symfony/Framework', ); }
  • 74. $this->render('SomeBundle:Hello:index', $params)
  • 75. hello: pattern: /hello/:name defaults: { _bundle: SomeBundle, ... }
  • 76. SomeBundle can be any of ApplicationSomeBundle BundleSomeBundle SymfonyFrameworkSomeBundle
  • 77. Less concepts… but more powerful ones
  • 78. symfony 1 View Layer templates layouts slots components partials component slots
  • 79. Symfony 2 View Layer templates slots
  • 80. A layout is just another template with _content as a special slot A partial is just a template you embed in another one A component is just another action embedded in a template
  • 81. <?php $view->output('BlogBundle:Post:list', array('posts' => $posts)) ?>
  • 82. <?php $view->actions->output('BlogBundle:Post:list', array ('limit' => 2)) ?>
  • 83. <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <?php $view->slots->output('_content') ?> </body> </html>
  • 84. Big and Small Improvements
  • 85. multiple level of layouts
  • 86. partials can be decorated!
  • 87. Better Logs
  • 88. INFO: Matched route "blog_home" (parameters: array ( '_bundle' => 'BlogBundle', '_controller' => 'Post', '_action' => 'index', '_route' => 'blog_home',)) INFO: Using controller "BundleBlogBundleController PostController::indexAction" INFO: SELECT s0_.id AS id0, s0_.title AS title1, s0_.html_body AS html_body2, s0_.excerpt AS excerpt3, s0_.published_at AS published_at4 FROM sf_weblog_post s0_ ORDER BY s0_.published_at DESC LIMIT 10 (array ())
  • 89. INFO: Matched route "blog_post" (parameters: array ( '_bundle' => 'BlogBundle', '_controller' => 'Post', '_action' => 'show', '_format' => 'html', 'id' => '3456', '_route' => 'blog_post',)) INFO: Using controller "BundleBlogBundleController PostController::showAction » INFO: SELECT s0_.id AS id0, s0_.title AS title1, s0_.html_body AS html_body2, s0_.excerpt AS excerpt3, s0_.published_at AS published_at4 FROM sf_weblog_post s0_ WHERE s0_.id = ? (array ( 0 => '3456',)) ERR: Post "3456" not found! (No result was found for query although at least one row was expected.) (uncaught SymfonyComponentsRequestHandlerException NotFoundHttpException exception) INFO: Using controller "SymfonyFrameworkWebBundleController ExceptionController::exceptionAction"
  • 90. <zend:logger priority="debug" />
  • 91. DEBUG: Notifying (until) event "core.request" to listener "(SymfonyFrameworkWebBundleListenerRequestParser, resolve)" INFO: Matched route "blog_post" (parameters: array ( '_bundle' => 'BlogBundle', '_controller' => 'Post', '_action' => 'show', '_format' => 'html', 'id' => '3456', '_route' => 'blog_post',)) DEBUG: Notifying (until) event "core.load_controller" to listener "(SymfonyFrameworkWebBundleListenerControllerLoader, resolve)" INFO: Using controller "BundleBlogBundleControllerPostController::showAction" DEBUG: Listener "(SymfonyFrameworkWebBundleListenerControllerLoader, resolve)" processed the event "core.load_controller" INFO: Trying to get post "3456" from database INFO: SELECT s0_.id AS id0, s0_.title AS title1, s0_.html_body AS html_body2, s0_.excerpt AS excerpt3, s0_.published_at AS published_at4 FROM sf_weblog_post s0_ WHERE s0_.id = ? (array ( 0 => '3456',)) DEBUG: Notifying (until) event "core.exception" to listener "(SymfonyFrameworkWebBundleListenerExceptionHandler, handle)" ERR: Post "3456" not found! (No result was found for query although at least one row was expected.) (uncaught SymfonyComponents RequestHandlerExceptionNotFoundHttpException exception) DEBUG: Notifying (until) event "core.request" to listener "(SymfonyFrameworkWebBundleListenerRequestParser, resolve)" DEBUG: Notifying (until) event "core.load_controller" to listener "(SymfonyFrameworkWebBundleListenerControllerLoader, resolve)" INFO: Using controller "SymfonyFrameworkWebBundleControllerExceptionController::exceptionAction" DEBUG: Listener "(SymfonyFrameworkWebBundleListenerControllerLoader, resolve)" processed the event "core.load_controller" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleListenerResponseFilter, filter)" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugDataCollector DataCollectorManager, handle)" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugWebDebugToolbar, handle)" DEBUG: Listener "(SymfonyFrameworkWebBundleListenerExceptionHandler, handle)" processed the event "core.exception" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleListenerResponseFilter, filter)" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugDataCollector DataCollectorManager, handle)" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugWebDebugToolbar, handle)"
  • 92. Even Better Exception Error Pages
  • 93. An Event Better Web Debug Toolbar
  • 94. Everything you need is at the bottom of the screen
  • 95. Web Designer “friendly”
  • 96. app/ views/ BlogBundle/ Post/ index.php AdminGeneratorBundle/ DefaultTheme/ list.php edit.php ...
  • 97. “Mount” Routing Configuration
  • 98. blog: resource: BlogBundle/Resources/config/routing.yml forum: resource: ForumBundle/Resources/config/routing.yml prefix: /forum
  • 99. Symfony 2 is a lazy framework
  • 100. Smart Autoloading
  • 101. require_once __DIR__.'/vendor/symfony/src/Symfony/Foundation/UniversalClassLoader.php'; use SymfonyFoundationUniversalClassLoader; $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( 'Symfony' => __DIR__.'/vendor/symfony/src', 'Application' => __DIR__, 'Bundle' => __DIR__, 'Doctrine' => __DIR__.'/vendor/doctrine/lib', )); $loader->registerPrefixes(array( 'Swift_' => __DIR__.'/vendor/swiftmailer/lib/classes', 'Zend_' => __DIR__.'/vendor/zend/library', )); $loader->register(); // for Zend Framework & SwiftMailer set_include_path(__DIR__.'/vendor/zend/library'.PATH_SEPARATOR.__DIR__.'/vendor/ swiftmailer/lib'.PATH_SEPARATOR.get_include_path());
  • 102. lazy-loading of services
  • 103. lazy-loading of listeners
  • 104. lazy-loading of helpers
  • 105. <?php echo $view->router->generate('blog_post', array('id' => $post->getId())) ?>
  • 106. Symfony 2 is a “cachy” framework
  • 107. blog/ cache/ prod/ blogProjectContainer.php blogUrlGenerator.php blogUrlMatcher.php classes.php
  • 108. class blogUrlMatcher extends SymfonyComponentsRoutingMatcherUrlMatcher { public function __construct(array $context = array(), array $defaults = array()) { $this->context = $context; $this->defaults = $defaults; } public function match($url) { $url = $this->normalizeUrl($url); if (0 === strpos($url, '/webblog') && preg_match('#^/webblog/(? P<id>[^/.]+?)$#x', $url, $matches)) return array_merge($this->mergeDefaults($matches, array ( '_bundle' => 'WebBundle', '_controller' => 'Redirect', '_action' => 'redirect', 'route' => 'blog_post',)), array('_route' => 'old_blog_post_redirect'));
  • 109. You can use Apache for Routing matching
  • 110. A Very Fast Dev. Env.
  • 111. blog/ cache/ dev/ blogProjectContainer.meta blogProjectContainer.php blogUrlGenerator.meta blogUrlGenerator.php blogUrlMatcher.meta blogUrlMatcher.php classes.meta classes.php prod/ blogProjectContainer.php blogUrlGenerator.php blogUrlMatcher.php classes.php
  • 112. Symfony 2
  • 113. Easy to learn Easy to use Extensible at will
  • 114. Easy to learn Easy to use
  • 115. Extensible at will
  • 116. But Symfony 2 should be slow, right?
  • 117. Fast as hell
  • 118. Benchmark on a simple application
  • 119. 2x faster than Solar 1.0.0
  • 120. 2.5x faster than symfony 1.4.2
  • 121. 3x faster than Zend Framework 1.10
  • 122. 4x faster than Lithium
  • 123. 6x faster than CakePHP 1.2.6
  • 124. 60x faster than Flow3
  • 125. …and Symfony 2.0 uses half the memory needed by both symfony 1 and ZF
  • 126. We have barely scratched the surface of all the goodness of Symfony 2.0
  • 127. Controller except for the nice default pages Autoloading Cache via ZF - DI extension coming soon CLI commands still missing Configuration Database via Doctrine DBAL Debug except Timer and extended WDT Escaper Event Dispatcher Form / Validation / Widget can use the 1.4 version as is Admin Generator Helpers I18n / L10n can use the 1.4 version as is Logger via ZF Mailer except commands Bundles except installing Doctrine Plugin just the DBAL part Propel Plugin Request / Response Routing no REST support, no Object support Storage / User Test View
  • 128. Final Release Target Date Late 2010
  • 129. If you want the bleeding edge of news, follow me on Twitter @fabpot on Github github.com/fabpot
  • 130.
  • 131. http://symfony-reloaded.org/
  • 132. Questions? My slides will be available on http://slideshare.com/fabpot
  • 133. 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/