Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What Symfony Components can Do for You

What Symfony Components can Do for You

Andreas Hucks

May 15, 2013
Tweet

More Decks by Andreas Hucks

Other Decks in Programming

Transcript

  1. What Symfony Components
    can do for you.
    php[tek] 2013 Chicago, May 15th
    Andreas Hucks

    View Slide

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

    View Slide

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

    View Slide

  4. View Slide

  5. ... not only.

    View Slide

  6. Symfony2 is a reusable set of
    standalone, decoupled, and cohesive
    PHP 5.3 components that solve
    common web development problems.

    View Slide

  7. Stuff built using SF2 Components (to varying degrees)
    • Symfony2 (duh)
    • Drupal 8
    • Silex
    • Laravel
    • PPI
    • PHPUnit
    • Composer
    • ... I probably forgot something important

    View Slide

  8. View Slide

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

                           while  ($todo  =  mysql_fetch_assoc($result))  {
                           echo  '';
                           echo  '    '.  $todo['id']  .'';
                           echo  '    '.  $todo['title']  .'';
                           echo  '    ';
    4.0
    TM

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. HttpFoundation

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  17. Testability & Compatibility
    $request  =  Request::createFromGlobals();
    $response  =  doSomethingToGenerateResponse($request);
    $response-­‐>send();

    View Slide

  18. View Slide

  19. Wrap Up
    • Starting point for refactoring:
    • Isolated legacy code
    • Can now be integrated into new app
    • Testable!

    View Slide

  20. View Slide

  21. View Slide

  22. Debug

    View Slide

  23. Debug
    • Error Handler
    • Exception Handler
    • Error Logging (optionally)

    View Slide

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

    View Slide

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

    View Slide

  26. View Slide

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

    View Slide

  28. View Slide

  29. View Slide

  30. Routing

    View Slide

  31. Routing
    • Define routes as patterns
    • Assign attributes
    • Match an incoming URI to a route
    • Generate an URI from a route object

    View Slide

  32. Defining a Route
    $routeHome  =  new  Route('/index.php');
    $routeHome-­‐>setDefault(
           '_controller',
           function(Request  $request)  use  ($wrapper)
           {
                   return  $wrapper-­‐>execute('/index.php',  $request);
           }
    );

    View Slide

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

    View Slide

  34. Parameters?
    $routeHome-­‐>setDefault('_controller',  'MyController');
    $routeHome-­‐>setDefault('page',  1);
    $routes-­‐>add('list',  $routeHome);
    array  (
           '_controller'  =>  'MyController',
           '_route'            =>  'list',
           'page'                =>  1
    )

    View Slide

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

    View Slide

  36. View Slide

  37. View Slide

  38. View Slide

  39. Templating

    View Slide

  40. • Simple, extensible templating engine
    • PHP!
    • Escaping, Inheritance
    • Generic Interface to allow for easy
    engine replacement (Twig!)

    View Slide

  41.        include  'config.php';
           include  'header.php';
    ?>

           [...]  /
    / here be dragons

           include  'footer.php'
    ?>
    4.0
    TM

    View Slide

  42. layout.php
    [...]

           output('content');  ?>

    [...]

    View Slide

  43. list.php
    extend('layout.php')  ?>
    start('content')  ?>
           [...]
                   
                           
                                   /td>
                                   [...]
                           
                   
           [...]
    stop()  ?>

    View Slide

  44. Rendering
    $templating  =  new  PhpEngine(
           new  TemplateNameParser(),
           new  FilesystemLoader(
                   array(__DIR__  .  '/../templates/%name%')
           )
    );
    $html  =  $templating-­‐>render(
       'list.php',
       array(
               'tasks'  =>  $tasks
       )
    );

    View Slide

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

    View Slide

  46. Generating URLs
    [...]

           
                   generate('list');  ?>">
                           My  Todo  List
                   
           
           [...]

    [...]

    View Slide

  47. View Slide

  48. View Slide

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

    View Slide

  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  }

    View Slide

  51. Matcher and Generator
    $urlGenerator  =  $router-­‐>getGenerator();
    $matcher  =  $router-­‐>getMatcher();

    View Slide

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

    View Slide

  53. View Slide

  54. View Slide

  55. HttpKernel

    View Slide

  56. HttpKernel
    • Provides a predefined workflow
    to convert a Request into a Response
    • Events to hook into
    • Error Logging (optionally)

    View Slide

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

    View Slide

  58. Controller Resolver?
    interface  ControllerResolverInterface
    {
         public  function  getController(Request  $request);
         public  function  getArguments(Request  $request,  $controller);
    }

    View Slide

  59. View Slide

  60. View Slide

  61. A look back
    $request  =  Request::createFromGlobals();
    $response  =  $kernel-­‐>handle($request);

    View Slide

  62. BrowserKit & CssSelector

    View Slide

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

    View Slide

  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")'));
           }
    }

    View Slide

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

    View Slide

  66. View Slide

  67. View Slide

  68. Config & Yaml

    View Slide

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

    View Slide

  70. Loading Configuration Data
    $configPath  =  __DIR__  .  '/../config/config.yml';
    $config  =  Yaml::parse($configPath);

    View Slide

  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  =  '        $configCache-­‐>write($code,  array($resource));
    }
    $config  =  require  $cachePath;

    View Slide

  72. View Slide

  73. Console

    View Slide

  74. Console
    • Easy setup for CLI scripts
    • Output formatting, help system
    • Interactive dialogs

    View Slide

  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');
           }
           //  ...
    }

    View Slide

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

    View Slide

  77. View Slide

  78. View Slide

  79. Execution
    public  function  execute(InputInterface  $input,  OutputInterface  $output)
    {
           $dialog  =  $this-­‐>getHelperSet()-­‐>get('dialog');
           $title  =  $dialog-­‐>ask(
                   $output,
                   'What  do  you  have  to  do?  '
           );
           if  ($title)  {
                   //  do  stuff
                   $output-­‐>writeln('Task  created.');
           }  else  {
                   $output-­‐>writeln('No  input  given!');
           }
    }

    View Slide

  80. View Slide

  81. View Slide

  82. What else is there?
    • Form
    • Security
    • Validator
    • Event Dispatcher
    • Finder
    • Process
    • PropertyAccess
    • OptionsResolver
    • ...

    View Slide

  83. Go forth and learn!
    http://goo.gl/a0bCJ

    View Slide

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

    View Slide