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

The Wonderful World of Symfony Components

weaverryan
September 28, 2012

The Wonderful World of Symfony Components

Wow, Symfony Components!

In this talk, we'll look at the history of PHP, and the struggles as a community to create shared libraries between our large community. Find out the significance of PSR-0 and Composer in *your* life and how you can leverage libraries from all of PHP in your projects.

We'll also look at the most fundamental Symfony2 components - HttpFoundation, HttpKernel, EventDispatcher, & Routing - including those that have been adopted by Drupal 8. We'll also check out a bunch of the other interesting Symfony2 components that can be used as tools in any PHP project.

The goal of this talk is to show you just how easy finding and using high quality libraries has become in PHP. By the end, you'll be excited and ready to high-five all of your PHP friends.

weaverryan

September 28, 2012
Tweet

More Decks by weaverryan

Other Decks in Technology

Transcript

  1. The Wonderful World of the Symfony Components by your friend:

    Ryan Weaver @weaverryan Friday, September 28, 12
  2. Who is this dude? • That “Docs” guy • KnpLabs

    US - Symfony consulting, training, Kumbaya • Writer for KnpUniversity.com screencasts • Husband of the much more talented @leannapelham knplabs.com github.com/weaverryan @weaverryan Friday, September 28, 12
  3. The Big Bummer :( @weaverryan • How do I autoload

    their files? • Does their library depend on anything else? • How do I even store their files in my project? Friday, September 28, 12
  4. if you don’t want the WHOLE library, carefully delete everything

    except the dependent components Friday, September 28, 12
  5. If PHP is big, we’ll thrive If PHP is small,

    we’ll die @weaverryan 3 Friday, September 28, 12
  6. Fragmentation @weaverryan But fragmentation makes us tiny, isolated, and misguided

    trend-setters http://www.flickr.com/photos/slpunk99/7329609744 Friday, September 28, 12
  7. Fragmentation @weaverryan • More information we have to know •

    Difficult to hire • Disjointed forums, StackOverflow • Interoperability? What’s that? Friday, September 28, 12
  8. My Autoloader doesn’t like your PHPs @weaverryan • The symfony1

    autoloader doesn’t know where ZF1 classes live. The Zf1 autoloader doesn’t know where symfony1 classes live • Each library has its own autoloader that you must discover, configure and use Friday, September 28, 12
  9. From The Mountain: PSR-0 Class Naming Conventions @weaverryan “Thou shalt

    name your classes by following a predictable pattern” Friday, September 28, 12
  10. The Symfony Components • HttpFoundation • HttpKernel • Event Dispatcher

    • Routing • CSSSelector • DomCrawler • BrowserKit • Config • Yaml • DependencyInjection • Security @weaverryan • OptionsResolver • Console • Filesystem • Finder • Locale • Process • Serializer • Templating • Form • Translation • Validator Friday, September 28, 12
  11. The Symfony Components • HttpFoundation • HttpKernel • Event Dispatcher

    • Routing • CSSSelector • DomCrawler • BrowserKit • Config • Yaml • DependencyInjection • Security @weaverryan • OptionsResolver • Console • Filesystem • Finder • Locale • Process • Serializer • Templating • Form • Translation • Validator The “Framework” Components Friday, September 28, 12
  12. @weaverryan { "require": { "symfony/http-foundation": "2.1.x-dev" } } php composer.phar

    install <?php // your app require 'vendor/autoload.php'; 1 2 3 Fun to Install! Friday, September 28, 12
  13. @weaverryan Request /foo Response <h1>Hi!</h1> ? ? ? ? ?

    ? Mystery PHP Code Friday, September 28, 12
  14. GET /foo?p=v1 HTTP/1.1 Host: knplabs.com Accept: text/html User-Agent: Mozilla/5.0 //

    /foo?p=v1 $uri = $_SERVER['REQUEST_URI']; $p = isset($_GET['p']) ? $_GET['p'] : ''; $host = $_SERVER['HTTP_HOST']; $accept = $_SERVER['HTTP_ACCEPT']; $ua = $_SERVER['HTTP_USER_AGENT']; Friday, September 28, 12
  15. use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); $uri = $request->getPathInfo(); /foo $p

    = $request->query->get('p'); $host = $request->getHost(); $accept = $request->headers->get('Accept'); GET /foo?p=v1 HTTP/1.1 Host: knplabs.com Accept: text/html User-Agent: Mozilla/5.0 Friday, September 28, 12
  16. HTTP/1.1 200 OK Date: Tue, 04 Jun 2011 21:05:05 GMT

    Content-Type: text/html Set-Cookie:foo=fooval; path=/; httponly <h1>Foo!</h1> header('Content-Type', 'text/html'); setcookie('foo', 'fooval'); echo '<h1>Foo!</h1>'; Friday, September 28, 12
  17. HTTP/1.1 200 OK Date: Tue, 04 Jun 2011 21:05:05 GMT

    Content-Type: text/html Set-Cookie:foo=fooval; path=/; httponly <h1>Foo!</h1> use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Cookie; $response = new Response('<h1>Foo!</h1>'); $cookie = new Cookie('foo', 'fooval'); $response->headers->setCookie($cookie); $response->send(); Friday, September 28, 12
  18. // not dependable! $uri = $_SERVER['REQUEST_URI']; ob_start(); if ($uri ==

    '/') { echo 'Homepage! \o/'; } else { echo 'sad missing page :/'; } header('X-TOKEN: Foo'); ob_end_flush(); Friday, September 28, 12
  19. $req = Request::createFromGlobals(); if ($req->getPathInfo() == '/') { $res =

    new Response('Homepage! \o/'); } else { $res = new Response('sad missing page :/'); } $res->headers->set('X-SECRET', 'Foo'); $res->send(); Symfony3 Friday, September 28, 12
  20. Get some learning about HttpFoundation @weaverryan • Docs: http://bit.ly/sf2-http-foundation •

    Code: http://bit.ly/sf2-http-foundation-code • API: http://bit.ly/sf2-http-foundation-api Friday, September 28, 12
  21. @weaverryan Framework Request Determine the controller ? ? ? Execute

    it Flush the Response Response Friday, September 28, 12
  22. @weaverryan Framework Request Determine the controller ? ? ? Execute

    it Flush the Response Response Friday, September 28, 12
  23. @weaverryan HttpKernel Request Determine the controller ? ? ? Execute

    it Flush the Response Response Friday, September 28, 12
  24. @weaverryan Event Dispatcher A classic “observer pattern” Listener Listener Tell

    me when Liz gets to work Tell me when Liz gets to work Friday, September 28, 12
  25. @weaverryan Event Dispatcher • Event Dispatcher: a classic “observer pattern”

    Listener Listener Yo! I’m at work! Friday, September 28, 12
  26. @weaverryan Event Dispatcher • Event Dispatcher: a classic “observer pattern”

    Listener Listener Liz is at work! Liz is at work! Friday, September 28, 12
  27. Event Dispatcher • Event Dispatcher: a classic “observer pattern” Listener

    Listener She’s the best around! I’m totally going to go talk with her!! Friday, September 28, 12
  28. @weaverryan Router Url Match against a route Return info about

    the route Route Route Friday, September 28, 12
  29. $loader = new ClosureLoader(); $router = new Router($loader, function() {

    $collection = new RouteCollection(); $route = new Route('/blog/{slug}'); $collection->add('blog', $route); return $collection; }); $results = $router->match('/blog/sflive'); array( 'slug' => 'sflive', '_route' => 'blog', ) Friday, September 28, 12
  30. $loader = new ClosureLoader(); $router = new Router($loader, function() {

    $collection = new RouteCollection(); $route = new Route('/blog/{slug}', array( '_controller' => 'SomeBundle:Blog:show' )); $collection->add('blog', $route); return $collection; }); $results = $router->match('/blog/sflive'); array( '_controller' => 'SomeBundle:Blog:show', 'slug' => 'sflive', '_route' => 'blog' ) Friday, September 28, 12
  31. @weaverryan HttpKernel::handleRaw() private function handleRaw(Request $request, $type = self::MASTER_REQUEST) {

    // request $event = new GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch(KernelEvents::REQUEST, $event); if ($event->hasResponse()) { return $this->filterResponse($event->getResponse(), $request, $type); } // load controller if (false === $controller = $this->resolver->getController($request)) { throw new NotFoundHttpException('Unable to find the controller); } $event = new FilterControllerEvent($this, $controller, $request, $type); $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event); $controller = $event->getController(); // ... returns a response } The kewlest code I know about... seriously Friday, September 28, 12
  32. @weaverryan • Symfony\Component\HttpKernel\HttpKernel::handle() http://bit.ly/sf2-http-kernel-code • Symfony Framework Important Classes Symfony\Component\HttpKernel\EventListener\RouterListener

    - Executes the routing Symfomy\Bundle\FrameworkBundle\Controller\ControllerResolver - Parses _controller and instantiates the controller object step/var_dump()die; the core Friday, September 28, 12
  33. The Symfony Components • HttpFoundation • HttpKernel • Event Dispatcher

    • Routing • CSSSelector • DomCrawler • BrowserKit • Config • Yaml • DependencyInjection • Security @weaverryan • OptionsResolver • Console • Filesystem • Finder • Locale • Process • Serializer • Templating • Form • Translation • Validator Awesome Tools Friday, September 28, 12
  34. @weaverryan { "require": { "symfony/process": "2.1.x-dev" } } Find Symfony’s

    Process on Packagist.org php composer.phar install 1 2 3 <?php // your app require 'vendor/autoload.php'; Friday, September 28, 12
  35. <?php // dinner.php for ($i = 1; $i <= 3;

    $i++) { sleep(1); echo sprintf("Cooking... %s\n", $i); } echo "DING! \n"; External Script to Cook Dinner Friday, September 28, 12
  36. // app.php require 'vendor/autoload.php'; use Symfony\Component\Process\Process; $process = new Process('php

    dinner.php'); // if dinner takes longer than 5 seconds // to cook, freak out! $process->setTimeout(5); $process->run(function($type, $buffer) { echo $buffer; }); // executed after the command finishes if (!$process->isSuccessful()) { throw new \Exception('Process misbehaved') } Friday, September 28, 12
  37. $process = new Process('php dinner.php'); $process->start(); // do more work

    here echo "WORKING!!! \n"; while ($process->isRunning()) { // wait for the process to finish }; echo $process->getOutput(); Friday, September 28, 12
  38. $process = new Process('php dinner.php'); $process->run(function($type, $buffer) { if ($type

    == 'err') { // perhaps do some logging? echo $buffer; } }); Handling things that blow up! if (!$process->isSuccessful()) { echo sprintf( "Process failed! Exit code %s, '%s'\n", $process->getExitCode(), $process->getExitCodeText() ); } Friday, September 28, 12
  39. Find out more about Process @weaverryan • Docs: http://bit.ly/sf2-process •

    Code: http://bit.ly/sf2-process-code • API: http://bit.ly/sf2-process-api Friday, September 28, 12
  40. SPORK! @kriswallsmith $manager = new Spork\ProcessManager(); $manager->fork(function() { // do

    something in another process! return 'Hello from '.getmypid(); })->then(function(Spork\Fork $fork) { // do something in the parent process afterwards echo "{$fork->getPid()} says '{$fork->getResult()}'"; }); Fork yourself some more PHP https://github.com/kriswallsmith/spork Friday, September 28, 12
  41. From the Fablog http://fabien.potencier.org/article/44/php-iterators-and- streams-are-awesome use Symfony\Components\Finder\Finder; $s3 = new

    \Zend_Service_Amazon_S3($key, $secret); $s3->registerStreamWrapper("s3"); $finder = new Finder(); $finder->name('photos*') ->size('< 100K') ->date('since 1 hour ago'); foreach ($finder->in('s3://bucket-name') as $file) { // do something print $file->getFilename()."\n"; } Friday, September 28, 12
  42. Find(er) out more about Finder @weaverryan • Docs: http://bit.ly/sf2-finder •

    Code: http://bit.ly/sf2-finder-code • API: http://bit.ly/sf2-finder-api Friday, September 28, 12
  43. @weaverryan { "require": { "symfony/filesystem": "2.1.x-dev" } } Find Symfony’s

    Filesystem on Packagist.org php composer.phar update symfony/filesystem 1 2 3 <?php // your app require 'vendor/autoload.php'; Friday, September 28, 12
  44. // ... use Symfony\Component\Filesystem\Filesystem; $filesystem = new Filesystem(); $filesystem->copy( 'cowboy.jpg',

    's3://my-bucket/cowboy.jpg' ); $filesystem->copy( 's3://my-bucket/rodeo.mov', 'rodeo.mov' ); $filesystem->mirror( 'thumbnails', 's3://my-bucket/thumbnails' ); Friday, September 28, 12
  45. // ... use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; $finder = new Finder();

    $finder->name('*.jpg') ->date('since 1 hour ago'); ->in('thumbs'); $filesystem = new Filesystem(); $filesystem->chmod($finder, 0777); Friday, September 28, 12
  46. Find out more about Filesystem @weaverryan • Docs: http://bit.ly/sf2-filesystem •

    Code: http://bit.ly/sf2-filesystem-code • API: http://bit.ly/sf2-filesystem-api Friday, September 28, 12
  47. <?php // command.php $loader = require 'vendor/autoload.php'; $loader->add('App', __DIR__); use

    Symfony\Component\Console\Application; use App\Command\EchoCommand; $app = new Application(); $app->add(new EchoCommand()); $app->run(); Friday, September 28, 12
  48. namespace App\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class EchoCommand

    extends Command { protected function configure() { $this->setName('echo')->addArgument('msg'); } public function execute( InputInterface $input, OutputInterface $output) { $output->writeln('Yo '.$input->getArgument('msg')); } } Friday, September 28, 12
  49. Find out more about Command @weaverryan • Docs http://bit.ly/sf2-console •

    Code http://bit.ly/sf2-console-code • API http://api.symfony.com/master/namespaces.html -> and search for “Console” Friday, September 28, 12
  50. If you download Composer in the next 5 minutes, we’ll

    throw in a special gift! Friday, September 28, 12
  51. require 'vendor/autoload.php'; use Symfony\Component\DomCrawler\Crawler; $html = <<<'HTML' <!DOCTYPE html> <html>

    <body> <p class="message">Hello World!</p> <p>Hello Crawler!</p> </body> </html> HTML; $crawler = new Crawler($html); $crawler->filter('p')->eq(0)->attr('class'); $crawler->filter('p')->eq(1)->text(); Friday, September 28, 12
  52. Config @weaverryan • Load configuration from many formats (YAML, PHP

    files, XML) • Config Validation • Caching for parsed config Friday, September 28, 12
  53. require 'vendor/autoload.php'; use Symfony\Component\Validator\Validator; use Symfony\Component\Validator\Mapping\ClassMetadataFactory; use Symfony\Component\Validator\ConstraintValidatorFactory; // a

    little setup $metadata = new ClassMetadataFactory(); $constraintFactory = new ConstraintValidatorFactory(); $validator = new Validator($metadata, $constraintFactory); Friday, September 28, 12
  54. use Symfony\Component\Validator\Constraints\Email; $value = 'ryan[AT]knplabs.com'; $email = new Email(); $email->message

    = 'Invalid! You entered {{ value }}'; $errors = $validator->validateValue($value, $email); if (count($errors) > 0) { echo strtr( $errors[0]->getMessageTemplate(), $errors[0]->getMessageParameters() ); } Friday, September 28, 12
  55. 34 Core Validators and counting • String length • Regular

    expressions • Ip addresses • Dates • Collections • File size, mime-type, etc ... and the brand new Luhn validator for credit card numbers thanks to @merk -----------------------------------> @weaverryan See the “Reference” section of the docs Friday, September 28, 12
  56. More Components • DependencyInjection • Security • OptionsResolver • Locale

    • Serializer • Templating • Form • Translation @weaverryan Friday, September 28, 12
  57. The PHP Ecosphere • Symfony • Zend Framework • EZ

    Components • Drupal • ... • Individuals @weaverryan Symfony is only one part of the picture What other stuff exists? Friday, September 28, 12
  58. Command a browser, find elements, click them, and fill out

    forms $driver = new \Behat\Mink\Driver\SahiDriver('firefox'); $session->visit('http://my_project.dev/some_page.php'); $page = $session->getPage(); $anchor = $page->find('css', '.something'); $anchor->click(); // get the content of the new page echo $page->getContent(); Friday, September 28, 12
  59. A Logger, where bells and whistles come standard use Monolog\Logger;

    use Monolog\Handler\StreamHandler; use Monolog\Handler\FirePHPHandler; // Create the logger $logger = new Logger('my_logger'); $logger->pushHandler(new StreamHandler( __DIR__.'/my_app.log' )); $logger->pushHandler(new FirePHPHandler()); $logger->addInfo('My logger is now ready'); Friday, September 28, 12
  60. Shared Building-blocks • Drupal • phpBB • Midgard • Zikula

    • ez Publish 5 • ... @weaverryan Friday, September 28, 12
  61. Less Library Duplication? • Zend/Form • Zend/Serializer • Zend/Http •

    Zend/EventManager • Zend/Log • Zend/Navigation • ... @weaverryan • Symfony/Form • Symfony/Serializer • Symfony/HttpFoundation • Symfony/EventDispatcher • Monlog • KnpMenu • ... Friday, September 28, 12
  62. Fabien will not come to your house and tell you

    about them Friday, September 28, 12
  63. ... and we love you! Ryan Weaver @weaverryan Ryan Weaver

    @weaverryan https://joind.in/7218 Friday, September 28, 12