What Symfony Components can Do for You

What Symfony Components can Do for You

E9612cd342dbddff6640b99db21deee7?s=128

Andreas Hucks

May 15, 2013
Tweet

Transcript

  1. What Symfony Components can do for you. php[tek] 2013 Chicago,

    May 15th Andreas Hucks
  2. @meandmymonkey Andreas Hucks Trainer & Consultant at SensioLabs Deutschland

  3. Symfony 2.0 Is a Full Stack Framework ...

  4. None
  5. ... not only.

  6. Symfony2 is a reusable set of standalone, decoupled, and cohesive

    PHP 5.3 components that solve common web development problems.
  7. Stuff built using SF2 Components (to varying degrees) • Symfony2

    (duh) • Drupal 8 • Silex • Laravel • PPI • PHPUnit • Composer • ... I probably forgot something important
  8. None
  9. if  ($_GET['action']  ==  'close') {        $query  =

     'UPDATE  todo  SET  is_done  =  1  WHERE  id  =  '.  mysql_real_escape_string($_GET['id']);        mysql_query($query,  $conn)  or  die('Unable  to  update  existing  task  :  '.  mysql_error());        header('Location:  /app.php/'); } $result  =  mysql_query('SELECT  COUNT(*)  FROM  todo',  $conn); $count    =  current(mysql_fetch_row($result)); $result  =  mysql_query('SELECT  *  FROM  todo',  $conn) ?> <table>        <?php                while  ($todo  =  mysql_fetch_assoc($result))  {                        echo  '<tr>';                        echo  '    <td  class="center">'.  $todo['id']  .'</td>';                        echo  '    <td><a  href="/app.php/show?id='.  $todo['id']  .'">'.  $todo['title']  .'</a></td>';                        echo  '    <td  class="center">'; 4.0 TM
  10. None
  11. None
  12. None
  13. HttpFoundation

  14. HttpFoundation • OOP Interface for HTTP • No more Superglobals

    • Helper Methods
  15. Legacy Wrapper public  function  execute($file,  Request  $request) {    

       $file  =  $this-­‐>basePath  .  $file;        if  (!is_file($file)  &&  someSanityCheck($file))  {                throw  new  \Exception('Invalid  controller.');        }        extract($this-­‐>context);        ob_start();        require_once  $this-­‐>basePath  .  $file;        return  new  Response(ob_get_clean()); }
  16. web/app.php use  Legacy\Wrapper; use  Symfony\Component\Debug\Debug; use  Symfony\Component\HttpFoundation\Request; $wrapper  =  new

     Wrapper(__DIR__  .  '/../legacy'); $request  =  Request::createFromGlobals(); $response  =  $wrapper-­‐>execute(        $request-­‐>getPathInfo(),        $request ); $response-­‐>send();
  17. Testability & Compatibility $request  =  Request::createFromGlobals(); $response  =  doSomethingToGenerateResponse($request); $response-­‐>send();

  18. None
  19. Wrap Up • Starting point for refactoring: • Isolated legacy

    code • Can now be integrated into new app • Testable!
  20. None
  21. None
  22. Debug

  23. Debug • Error Handler • Exception Handler • Error Logging

    (optionally)
  24. Error Handler ErrorHandler::register($level  =  null); ErrorHandler::setLogger(LoggerInterface  $logger  =  null);

  25. Exception Handler ExceptionHandler::register($debug  =  true);

  26. None
  27. Debug Debug::enable($level  =  null);

  28. None
  29. None
  30. Routing

  31. Routing • Define routes as patterns • Assign attributes •

    Match an incoming URI to a route • Generate an URI from a route object
  32. Defining a Route $routeHome  =  new  Route('/index.php'); $routeHome-­‐>setDefault(    

       '_controller',        function(Request  $request)  use  ($wrapper)        {                return  $wrapper-­‐>execute('/index.php',  $request);        } );
  33. Matching $routes  =  new  RouteCollection(); $routes-­‐>add('list',  $routeHome); //  ... $context

     =  new  RequestContext(); $context-­‐>fromRequest($request); $matcher  =  new  UrlMatcher($routes,  $context); $parameters  =  $matcher-­‐>match($request-­‐>getPathInfo());
  34. Parameters? $routeHome-­‐>setDefault('_controller',  'MyController'); $routeHome-­‐>setDefault('page',  1); $routes-­‐>add('list',  $routeHome); array  (  

         '_controller'  =>  'MyController',        '_route'            =>  'list',        'page'                =>  1 )
  35. Determine Controller $parameters  =  $matcher-­‐>match($request-­‐>getPathInfo()); $response  =  $parameters['_controller']($request); $response-­‐>send();

  36. None
  37. None
  38. None
  39. Templating

  40. • Simple, extensible templating engine • PHP! • Escaping, Inheritance

    • Generic Interface to allow for easy engine replacement (Twig!)
  41. <?php        include  'config.php';        include

     'header.php'; ?> <table>        [...]  / / here be dragons </table> <?php        include  'footer.php' ?> 4.0 TM
  42. layout.php [...] <div  id="content">        <?php  $view['slots']-­‐>output('content');  ?>

    </div> [...]
  43. list.php <?php  $view-­‐>extend('layout.php')  ?> <?php  $view['slots']-­‐>start('content')  ?>      

     [...]                <?php  foreach  ($tasks  as  $task):  ?>                        <tr>                                <td><?php  echo  $task['title'];  ?></td>/td>                                [...]                        </tr>                <?php  endforeach;  ?>        [...] <?php  $view['slots']-­‐>stop()  ?>
  44. Rendering $templating  =  new  PhpEngine(        new  TemplateNameParser(),

           new  FilesystemLoader(                array(__DIR__  .  '/../templates/%name%')        ) ); $html  =  $templating-­‐>render(    'list.php',    array(            'tasks'  =>  $tasks    ) );
  45. Inside the Controller public  function  listAction(Request  $request) {    

       //  load  tasks  from  db        return  new  Response(                $this-­‐>templating-­‐>render(                      'list.php',                      array(                          'urlGenerator'  =>  $router-­‐>getGenerator(),                          'tasks'                =>  $tasks                  )          )      ); }
  46. Generating URLs [...] <div  id="content">        <h1>  

                 <a  href=  "<?php  $urlGenerator-­‐>generate('list');  ?>">                        My  Todo  List                </a>        </h1>        [...] </div> [...]
  47. None
  48. None
  49. The all-in-one Router $context  =  new  RequestContext(); $context-­‐>fromRequest($request); $locator  =

     new  FileLocator(__DIR__  .  '/../config'); $router  =  new  Router(        new  YamlFileLoader($locator),        'routing.yml',        array(                'cache_dir'  =>  __DIR__  .  '/../cache'        ),        $context );
  50. YAML Configuration list:        pattern:  /    

       methods:  GET        defaults:  {  _controller:  list  } show:        pattern:  /{id}        methods:  GET        defaults:  {  _controller:  show  } create:        pattern:  /        methods:  POST        defaults:  {  _controller:  create  }
  51. Matcher and Generator $urlGenerator  =  $router-­‐>getGenerator(); $matcher  =  $router-­‐>getMatcher();

  52. Call the (new) Controller $controller  =  //  create  controller  instance

    $parameters  =  $matcher-­‐>match($request-­‐>getPathInfo()); $response  =  call_user_func(   array(     $controller,     $parameters['_controller']  .  'Action'   ),   $request ); $response-­‐>send();
  53. None
  54. None
  55. HttpKernel

  56. HttpKernel • Provides a predefined workflow to convert a Request

    into a Response • Events to hook into • Error Logging (optionally)
  57. Let the kernel handle it. $dispatcher  =  new  EventDispatcher(); $dispatcher-­‐>addSubscriber(

           new  RouterListener($router-­‐>getMatcher()) ); $resolver  =  new  MyControllerResolver($controller); $kernel  =  new  HttpKernel($dispatcher,  $resolver); $request  =  Request::createFromGlobals(); $response  =  $kernel-­‐>handle($request); $response-­‐>send();
  58. Controller Resolver? interface  ControllerResolverInterface {      public  function  getController(Request

     $request);      public  function  getArguments(Request  $request,  $controller); }
  59. None
  60. None
  61. A look back $request  =  Request::createFromGlobals(); $response  =  $kernel-­‐>handle($request);

  62. BrowserKit & CssSelector

  63. Base Test use  Symfony\Component\HttpKernel\Client; class  FunctionalTestCase  extends  \PHPUnit_Framework_TestCase {  

         protected  static  function  createClient()        {                $kernel  =  //  somehow  create  a  kernel                return  new  Client($kernel);        } }
  64. A Test Case class  IndexPageTest  extends  FunctionalTestCase {    

       public  function  testList()        {                $client  =  static::createClient();                $crawler  =  $client-­‐>request('GET',  '/');                $this-­‐>assertEquals(200,  $client-­‐>getResponse()-­‐>getStatusCode());                $this-­‐>assertCount(1,  $crawler-­‐>filter('h1:contains("My  Todos  List")'));        } }
  65. Testing Forms $form  =  $crawler-­‐>selectButton('send')-­‐>form(); $client-­‐>submit($form,  array('title'  =>  'MyTask')); $this-­‐>assertEquals(302,

     $client-­‐>getResponse()-­‐>getStatusCode()); $crawler  =  $client-­‐>followRedirect(); $this-­‐>assertCount(1,  $crawler-­‐>filter(sprintf('a:contains("%s")',  'MyTask')));
  66. None
  67. None
  68. Config & Yaml

  69. Config • Locating, Loading, Caching of config files • Validation

    of config files • Merging of cascading config sets
  70. Loading Configuration Data $configPath  =  __DIR__  .  '/../config/config.yml'; $config  =

     Yaml::parse($configPath);
  71. Loading Configuration Data $cachePath  =  __DIR__  .  '/../cache/config.php'; $configPath  =

     __DIR__  .  '/../config/config.yml'; $configCache  =  new  ConfigCache($cachePath,  true); if  (!$configCache-­‐>isFresh())  {        $resource  =  new  FileResource($configPath);        $code  =  '<?php  return  '  .  var_export(Yaml::parse($configPath),  true)  .  ';';        $configCache-­‐>write($code,  array($resource)); } $config  =  require  $cachePath;
  72. None
  73. Console

  74. Console • Easy setup for CLI scripts • Output formatting,

    help system • Interactive dialogs
  75. Commands use  Symfony\Component\Console\Command\Command; class  AddTaskCommand  extends  Command {    

       public  function  configure()        {                $this-­‐>setName('todo:add');                $this-­‐>setDescription('Add  a  new  task  to  your  todo  list');                $this-­‐>addArgument('title',  InputArgument::OPTIONAL,  'The  task  title');        }        //  ... }
  76. The Application use  Symfony\Component\Console\Application; $db  =  //  create  PDO  instance

    $app  =  new  Application('Todo  List  Helpers'); $app-­‐>add(new  AddTaskCommand($db)); $app-­‐>add(new  ExpireTasksCommand($db)); $app-­‐>run();
  77. None
  78. None
  79. Execution public  function  execute(InputInterface  $input,  OutputInterface  $output) {    

       $dialog  =  $this-­‐>getHelperSet()-­‐>get('dialog');        $title  =  $dialog-­‐>ask(                $output,                '<question>What  do  you  have  to  do?</question>  '        );        if  ($title)  {                //  do  stuff                $output-­‐>writeln('<info>Task  created.</info>');        }  else  {                $output-­‐>writeln('<error>No  input  given!</error>');        } }
  80. None
  81. None
  82. What else is there? • Form • Security • Validator

    • Event Dispatcher • Finder • Process • PropertyAccess • OptionsResolver • ...
  83. Go forth and learn! http://goo.gl/a0bCJ

  84. Thanks! Questions? Please give feedback: http://goo.gl/IMK9n