SlideShare a Scribd company logo
A PHP Christmas Miracle
                A story of deception, wisdom, and finding our
                              common interface

                                  Ryan Weaver
                                  @weaverryan




Saturday, December 3, 11
Who is this dude?
                  • Co-author of the Symfony2 Docs
                  • Core Symfony2 contributor
                  • Co-owner of KnpLabs US
                  • Fiancee of the much more
                       talented @leannapelham


                           http://www.knplabs.com/en
                           http://www.github.com/weaverryan
 @weaverryan
Saturday, December 3, 11
Act 1:

                           A History of modern PHP



     @weaverryan
Saturday, December 3, 11
First, the facts




Saturday, December 3, 11
To put it politely...




Saturday, December 3, 11
We have a surplus of PHP
                            frameworks




Saturday, December 3, 11
To put it honestly...




Saturday, December 3, 11
We have a sh*tload




Saturday, December 3, 11
‣ Symfony
                      ‣ Zend Framework
                      ‣ Lithium
                      ‣ Aura (formerly Solar)
                      ‣ Code Igniter
                      ‣ Yii
                      ‣ Cake
                      ‣ Fuel
                      ‣ Akelos
                      ‣ Kohana
                      ‣ Flow3
                      ‣ ...
Saturday, December 3, 11
And we solve common problems




   @weaverryan
Saturday, December 3, 11
Common Problems
                     ‣ HTTP classes (e.g. request, response)
                     ‣ Routing
                     ‣ Controllers               ‣ Forms
                     ‣ Templates                 ‣ Events
                     ‣ Loggers                   ‣ Mailing
                     ‣ Database tools            ‣ Validation
                     ‣ Service containers        ‣ Pagination
                     ‣ Security                  ‣ Menus
                     ‣ Serialization             ‣ Search
    @weaverryan
Saturday, December 3, 11
Lack of sharing means
                             duplicated efforts




Saturday, December 3, 11
But, there is some hope...




Saturday, December 3, 11
PSR-0

                     • The PHP community came together, sang
                     Kumbaya and wrote up some class-naming
                     standards

                     • PSR-0 isn’t a library, it’s just an agreement to
                     name your classes in one of two ways




 @weaverryan
Saturday, December 3, 11
PSR-0 with namespaces
             Namespace your classes and have the namespaces
                     follow the directory structure

   class: SymfonyComponentHttpFoundationRequest

   path: vendor/src/Symfony/Component/HttpFoundation/Request.php




Saturday, December 3, 11
PSR-0 with underscores
                 Use underscores in your classes and follow the
                             directory structure

   class: Twig_Extension_Core

   path: vendor/twig/lib/Twig/Extension/Core.php




Saturday, December 3, 11
But what does this mean?
                   • An "autoloader" is a tool you can use so that
                   you don't have to worry about “including”
                   classes before you use them

                   • Use anyone’s autoloader

                   • We're all still duplicating each other's work,
                   but at least everyone’s autoloader does the
                   same thing

 @weaverryan
Saturday, December 3, 11
What else can we agree on?




Saturday, December 3, 11
So far, not much




Saturday, December 3, 11
But we’ll see how all the
                            libraries can still work
                                    together


Saturday, December 3, 11
And we can always hope for
                    a Christmas miracle




Saturday, December 3, 11
Act 2:

                   Starting with a Horrible App




   @weaverryan
Saturday, December 3, 11
How many of you use a
                           framework on a regular
                                  basis?



Saturday, December 3, 11
How many of you have been
              told that it's a bad idea to not
                     use a framework?



Saturday, December 3, 11
Was I the one who told you
                              that?



Saturday, December 3, 11
Today we’re going to break
                          the rules...



Saturday, December 3, 11
... build a framework from
                                “scratch” ...



Saturday, December 3, 11
... and see why that’s no
                           longer necessarily a bad
                                     thing.


Saturday, December 3, 11
Today’s 2 goals:
                  • Refactor a crappy flat PHP application into a
                  framework that makes sense

                  • Use as many libraries from as many quarreling
                  PHP tribes as possible
                   ‣ Symfony
                   ‣ Zend Framework
                   ‣ Lithium
                   ‣ ... only lack of time prevents more...

 @weaverryan
Saturday, December 3, 11
Our starting point

                  • Following along with the code of our app at:
                           http://bit.ly/php-xmas


            • Our app is a single file that fuels two pages




Saturday, December 3, 11
Saturday, December 3, 11
• Shucks, we even have a database connection



Saturday, December 3, 11
Open our Database connection

   // index.php

   try {
       $dbPath = __DIR__.'/data/database.sqlite';
       $dbh = new PDO('sqlite:'.$dbPath);
   } catch(PDOException $e) {
       die('Panic! '.$e->getMessage());
   }




Saturday, December 3, 11
Try to get a clean URI

      // index.php


      $uri = $_SERVER['REQUEST_URI'];
      if ($pos = strpos($uri, '?')) {
          $uri = substr($uri, 0, $pos);
      }




Saturday, December 3, 11
Render the homepage

   // index.php
  if ($uri == '/' || $uri == '') {
      echo '<h1>Welcome to PHP Santa</h1>';
      echo '<a href="/letters">Readletters</a>';
      if (isset($_GET['name'])) {
          echo sprintf(
              '<p>Oh, and hello %s!</p>',
              $_GET['name']
         );
      }
  }
Saturday, December 3, 11
Print out some letters
   // index.php
  if ($uri == '/letters') {

              $sql = 'SELECT * FROM php_santa_letters';
              echo '<h1>Read the letters to PHP Santa</h1>';
              echo '<ul>';
              foreach ($dbh->query($sql) as $row) {
                  echo sprintf(
                      '<li>%s - dated %s</li>',
                      $row['content'],
                      $row['received_at']
                  );
              }
              echo '</ul>';
  }
Saturday, December 3, 11
Got it?




Saturday, December 3, 11
Great, let’s clean this mess up




Saturday, December 3, 11
Act 3:

                Symfony's HTTP Foundation




   @weaverryan
Saturday, December 3, 11
Problems
                  • Our code for trying to get a clean URL is a bit
                  archaic and probably error prone

                  • We're echoing content from our controllers,
                  maybe we can evolve




 @weaverryan
Saturday, December 3, 11
Solution
                  • Symfony’s HttpFoundation Component

                  • Gives us (among other things) a solid Request
                  and Response class




 @weaverryan
Saturday, December 3, 11
Bring in HttpFoundation


        mkdir -p vendors/Symfony/Component

        git submodule add 
            git://github.com/symfony/HttpFoundation.git 
            vendors/Symfony/Component/HttpFoundation/

        git submodule add 
            git://github.com/symfony/ClassLoader.git 
            vendors/Symfony/Component/ClassLoader



Saturday, December 3, 11
Current Status




 @weaverryan
Saturday, December 3, 11
Autoloading
                  • No matter what framework or libraries you use,
                  you’ll need an autoloader

                  • We’ll use Symfony’s “ClassLoader”

                  • Each PSR-0 autoloader is very similar




 @weaverryan
Saturday, December 3, 11
Create a bootstrap file
  <?php
  // bootstrap.php

  require __DIR__.'/vendors/Symfony/Component/
  ClassLoader/UniversalClassLoader.php';
  use SymfonyComponentClassLoaderUniversalClassLoader;

  // setup the autoloader
  $loader = new UniversalClassLoader();
  $loader->registerNamespace(
      'Symfony', __DIR__.'/vendors'
  );
  $loader->register();

Saturday, December 3, 11
... and include it



                           <?php
                           // index.php
                           require 'bootstrap.php';

                           // ...




Saturday, December 3, 11
So how does this help?




Saturday, December 3, 11
$uri = $_SERVER['REQUEST_URI'];
         if ($pos = strpos($uri, '?')) {
             $uri = substr($uri, 0, $pos);
         }


                           ... becomes ...
         use SymfonyComponentHttpFoundationRequest;
         $request = Request::createFromGlobals();

         // the clean URI - a lot of logic behind it!!!
         $uri = $request->getPathInfo();



Saturday, December 3, 11
if (isset($_GET['name'])) {
             echo sprintf(
                 '<p>Oh, and hello %s!</p>',
                 $_GET['name']
            );
         }
                           ... becomes ...
     if ($name = $request->query->get('name')) {
         echo sprintf(
              '<p>Oh, and hello %s!</p>',
              $name
         );
     }

Saturday, December 3, 11
The “Request” object
              • Normalizes server variables across systems

              • Shortcut methods to common things like
              getClientIp(), getHost(), getContent(), etc

              • Nice object-oriented interface




 @weaverryan
Saturday, December 3, 11
The “Response” object
                  • We also have a Response object

                  • Instead of echoing out content, we populate
                  this fluid object




 @weaverryan
Saturday, December 3, 11
header("HTTP/1.1 404 Not Found");
      echo '<h1>404 Page not Found</h1>';
      echo '<p>This is most certainly *not* an xmas
      miracle</p>';


                           ... becomes ...
    $content = '<h1>404 Page not Found</h1>';
    $content .= '<p>This is most certainly *not*
    an xmas miracle</p>';

    $response = new Response($content);
    $response->setStatusCode(404);
    $response->send();

Saturday, December 3, 11
Act 4:

                           Routing




    @weaverryan
Saturday, December 3, 11
Problems
                  • Our app is a giant gross “if” statement
                           if ($uri == '/' || $uri == '') {
                               // ...
                           } elseif ($uri == '/letters') {
                               // ...
                           } else {
                               // ...
                           }

                • Grabbing a piece from the URL like
                       /blog/my-blog-post will take some work
 @weaverryan
Saturday, December 3, 11
Solution
                  • Lithium’s Routing library

                  • Routing matches URIs (e.g. /foo) and returns
                  information we attached to that URI pattern

                  • All the nasty regex matching is out-of-sight




 @weaverryan
Saturday, December 3, 11
3 Steps to Bringing in an
                                 external tool




Saturday, December 3, 11
#1 Download the library


        git submodule add git://github.com/UnionOfRAD/
        lithium.git vendors/lithium




Saturday, December 3, 11
#2 Configure the autoloader
  // bootstrap.php
  // ...

  $loader = new UniversalClassLoader();
  $loader->registerNamespace('Symfony', __DIR__.'/vendors');
  $loader->registerNamespace('lithium', __DIR__.'/vendors');

  $loader->register();




Saturday, December 3, 11
#3 Celebrate!

  use lithiumnethttpRouter;
  $router = new Router();
  // ...




Saturday, December 3, 11
Current Status




 @weaverryan
Saturday, December 3, 11
So how do we use the router?




Saturday, December 3, 11
Full disclosure: “use”
           statements I’m hiding from the
                      next page

  use SymfonyComponentHttpFoundationRequest;
  use lithiumnethttpRouter;
  use lithiumactionRequest as Li3Request;




Saturday, December 3, 11
a) Map URI to “controller”
   $request = Request::createFromGlobals();
   $li3Request = new Li3Request();
   // get the URL from Symfony's request, give it to lithium
   $li3Request->url = $request->getPathInfo();

    // create a router, build the routes, and then execute it
    $router = new Router();
    $router->connect('/letters', array('controller' => 'letters'));
    $router->connect('/', array('controller' => 'homepage'));
    $router->parse($li3Request);

    if (isset($li3Request->params['controller'])) {
        $controller = $li3Request->params['controller'];
    } else {
        $controller = 'error404';
    }
Saturday, December 3, 11
b) Execute the controller*

   // execute the controller, send the request, get the response
   $response = call_user_func_array($controller, array($request));
   if (!$response instanceof Response) {
       throw new Exception(sprintf(
           'WTF! Your controller "%s" didn't return a response!!',
           $controller
       ));
   }

   $response->send();




      * each controller is a flat function
Saturday, December 3, 11
The Controllers
 function homepage(Request $request) {
     $content = '<h1>Welcome to PHP Santa</h1>';
     $content .= '<a href="/letters">Read the letters</a>';
     if ($name = $request->query->get('name')) {
         $content .= sprintf(
             '<p>Oh, and hello %s!</p>',
             $name
         );
     }

             return new Response($content);
 }




Saturday, December 3, 11
The Controllers
 function letters(Request $request)
 {
     global $dbh;                                 $kitten--
            $sql = 'SELECT * FROM php_santa_letters';
            $content = '<h1>Read the letters to PHP Santa</h1>';
            $content .= '<ul>';
            foreach ($dbh->query($sql) as $row) {
                $content .= sprintf(
                    '<li>%s - dated %s</li>',
                    $row['content'],
                    $row['received_at']
                );
            }
            $content .= '</ul>';

            return new Response($content);
 }
Saturday, December 3, 11
The Controllers

 function error404(Request $request)
 {
     $content = '<h1>404 Page not Found</h1>';
     $content .= 'This is most certainly *not* an xmas miracle';

            $response = new Response($content);
            $response->setStatusCode(404);

            return $response;
 }




Saturday, December 3, 11
The Big Picture
                    1. Request cleans the URI
                    2. Router matches the URI to a route, returns a
                    “controller” string
                    3. We execute the controller function
                    4. The controller creates a Response object
                    5. We send the Response headers and content


 @weaverryan
Saturday, December 3, 11
Your 20 line framework




Saturday, December 3, 11
Act 5:

                           Pimple!




     @weaverryan
Saturday, December 3, 11
Problems
                  • We’ve got lots of random, disorganized
                  objects floating around


                  • And we can’t easily access them from within
                  our controllers
                           function letters(Request $request)
                           {
                               global $dbh;

                               // ....
                           }
 @weaverryan
Saturday, December 3, 11
Solution
                  • Pimple! - a Dependency Injection Container

                  • Dependency Injection Container:
                           the scariest word we could think of to
                           describe an array of objects on steroids




 @weaverryan
Saturday, December 3, 11
Remember: 3 Steps to
                     bringing in an external tool




Saturday, December 3, 11
#1 Download the library


        git submodule add git://github.com/fabpot/
        Pimple.git vendors/Pimple




Saturday, December 3, 11
#2 Configure the autoloader

  // bootstrap.php
  // ...

  require __DIR__.'/vendors/Pimple/lib/Pimple.php';




       actually, it’s only one file - so just require it!

Saturday, December 3, 11
#3 Celebrate!


  $c = new Pimple();




Saturday, December 3, 11
Pimple Creates Objects
                  • Use Pimple to create and store your objects in
                  a central place

                  • If you have the Pimple container object, then
                  you have access to every other object in your
                  application




 @weaverryan
Saturday, December 3, 11
Centralize the db connection

   $c = new Pimple();

   $c['connection'] = $c->share(function() {
       $dsn = 'sqlite:'.__DIR__.'/data/database.sqlite';
       return new PDO($dsn);
   });




Saturday, December 3, 11
Centralize the db connection

       $c1 = $c['connection'];
       $c2 = $c['connection'];

       // they are the same - only one object is created!
       $c1 === $c2




Saturday, December 3, 11
Centralize the db connection

       $c1 = $c['connection'];
       $c2 = $c['connection'];

       // they are the same - only one object is created!
       $c1 === $c2




Saturday, December 3, 11
Access to what we need
                  • So far, we’re using a “global” keyword to
                  access our database connection

                  • But if we pass around our Pimple container,
                  we always have access to anything we need -
                  including the database connection




 @weaverryan
Saturday, December 3, 11
Pass the container to the
                             controller
       $c = new Pimple();

       // ...

       $response = call_user_func_array(
           $controller,
           array($request, $c)
       );



Saturday, December 3, 11
function letters(Request $request, Pimple $c)
 {
     $dbh = $c['connection'];                  $kitten++
             $sql = 'SELECT * FROM php_santa_letters';
             $content = '<h1>Read the letters to PHP Santa</h1>';
             $content .= '<ul>';
             foreach ($dbh->query($sql) as $row) {
                 // ...
             }
             // ...
 }



Saturday, December 3, 11
What else?

                      How about configuration?



Saturday, December 3, 11
$c = new Pimple();

 // configuration
 $c['connection_string'] = 'sqlite:'.__DIR__
     .'/data/database.sqlite';

 $c['connection'] = $c->share(function(Pimple $c) {
     return new PDO($c['connection_string']);
 });




Saturday, December 3, 11
Further?

                  What about dependencies?



Saturday, December 3, 11
$c['request'] = $c->share(function() {
      return Request::createFromGlobals();
  });

  $c['li3_request'] = $c->share(function($c) {
      $li3Request = new Li3Request();
      $li3Request->url = $c['request']->getPathInfo();

               return $li3Request;
  });

  // ...
  $li3Request = $c['li3_request'];



Saturday, December 3, 11
With everything in the
                    container, our “framework”
                          just got skinny



Saturday, December 3, 11
$c = new Pimple();
  // create objects in Pimple

  // execute our routing, merge attributes to request
  $result = $c['router']->parse($c['li3_request']);
  $c['request']->attributes
      ->add($c['li3_request']->params);

  // get controller and execute!
  $controller = $c['request']->attributes
      ->get('controller', 'error404');
  $response = call_user_func_array(
      $controller,
      array($c['request'], $c)
  );

  $response->send();
Saturday, December 3, 11
Logging with ZF2



     @weaverryan
Saturday, December 3, 11
Problems
                  • I don’t have enough frameworks in my
                  framework


                  • Oh yeah, and we need logging...




 @weaverryan
Saturday, December 3, 11
Solution
                  • Zend Framework2

                  • ZF2 has a ton of components, including a
                  logger




 @weaverryan
Saturday, December 3, 11
3 Steps to bringing in an
                                 external tool




Saturday, December 3, 11
#1 Download the library


        git submodule add git://github.com/
        zendframework/zf2.git vendors/zf2




Saturday, December 3, 11
#2 Configure the autoloader
  // bootstrap.php
  // ...

  $loader = new UniversalClassLoader();
  $loader->registerNamespace('Symfony', __DIR__.'/vendors');
  $loader->registerNamespace('lithium', __DIR__.'/vendors');
  $loader->registerNamespace(
      'Zend',
      __DIR__.'/vendors/zf2/library'
  );

  $loader->register();


Saturday, December 3, 11
#3 Celebrate!

  use ZendLogLogger;
  use ZendLogWriterStream;

  $logger = Logger($pimple['logger_writer']);


         Yes we did just bring in a 100k+ lines of
               code for a simple logger :)

Saturday, December 3, 11
Current Status




 @weaverryan
Saturday, December 3, 11
Create the Logger in our
                              Fancy Container
   use ZendLogLogger;
   use ZendLogWriterStream;

   $c['log_path'] = __DIR__.'/data/web.log';
   $c['logger_writer'] = $c->share(function($pimple) {
       return new Stream($pimple['log_path']);
   });

   $c['logger'] = $c->share(function($pimple) {
       return new Logger($pimple['logger_writer']);
   });

Saturday, December 3, 11
And use it anywhere
   function error404(Request $request, Pimple $c)
   {
       $c['logger']->log(
           'Crap, 404 for '.$request->getPathInfo(),
           Logger::ERR
       );

                $content = '<h1>404 Page not Found</h1>';
                // ...
   }


Saturday, December 3, 11
Getting kinda easy, right?




Saturday, December 3, 11
What other libraries can you
                     think to integrate?




Saturday, December 3, 11
Getting Organized



     @weaverryan
Saturday, December 3, 11
Problems
                  • Our application has 4 major parts:
                           1) autoloading setup
                           2) Creation of container
                           3) Definition of routes
                           4) Definition of controllers
                           5) The code that executes everything

                  • For our business, only #3 and #4 are important

                  • ... but it’s all jammed together
 @weaverryan
Saturday, December 3, 11
Solution
                  • Some definitions

                           “Application” - the code that makes you money

                           “Framework” - under-the-hood code that
                           impresses your geek friends

                  • To be productive, let’s “hide” the framework


 @weaverryan
Saturday, December 3, 11
Starting point
                  • Our app basically has 2 files

                           ‣ bootstrap.php: holds autoloading
                           ‣ index.php: holds
                             - container setup
                             - definition of routes
                             - definition of controllers
                             - the code that executes it all

 @weaverryan
Saturday, December 3, 11
Ending point
                           ‣ bootstrap.php: holds
                             - autoloading
                             - container setup
                             - the code that executes it all
                                (as a function called _run_application())
                           ‣ controllers.php: holds controllers
                           ‣ routes.php: holds routes

                           ‣ index.php: pulls it all together
 @weaverryan
Saturday, December 3, 11
Nothing to see here...
   <?php

   // index.php
   $c = require 'bootstrap.php';
   require 'routing.php';
   require 'controllers.php';

   $response = _run_application($c);
   $response->send();




Saturday, December 3, 11
“Framework” hidden away...
  // bootstrap.php
  function _run_application(Pimple $c)
  {
      $c['router']->parse($c['li3_request']);
      $c['request']->attributes
          ->add($c['li3_request']->params);

              $controller = $c['request']->attributes
                  ->get('controller', 'error404');

              return call_user_func_array(
                  $controller,
                  array($c['request'], $c)
              );
  }
Saturday, December 3, 11
Routes have a home
   // routing.php
   $c['router']->connect(
       '/letters',
       array('controller' => 'letters')
   );

   $c['router']->connect(
       '/{:name}',
       array(
           'controller' => 'homepage',
           'name' => null
       )
   );
Saturday, December 3, 11
Controllers have a home
   // controllers.php
   use SymfonyComponentHttpFoundationRequest;
   use SymfonyComponentHttpFoundationResponse;

   function homepage(Request $request) {
       // ...
   }

   function letters(Request $request, $c)
   {
       // ...
   }

   function error404(Request $request)
   {
       // ...
   }
Saturday, December 3, 11
To make $$$, work in
          routes.php and controllers.php




Saturday, December 3, 11
Final Thoughts



     @weaverryan
Saturday, December 3, 11
It doesn’t matter if you use a
                        framework...




Saturday, December 3, 11
... inherited a legacy spaghetti
                        system...




Saturday, December 3, 11
... or practice “not-invented-
                       here” development




Saturday, December 3, 11
...an innumerable number of
                      tools are available...




Saturday, December 3, 11
... so you can stop writing
                            your framework ...




Saturday, December 3, 11
... and start writing your
                                  application




Saturday, December 3, 11
What’s available? Search GitHub




Saturday, December 3, 11
Thanks!
                           Questions?

                           Ryan Weaver
                           @weaverryan




Saturday, December 3, 11
References
                 • http://bit.ly/php-xmas
                 • http://github.com/symfony
                 • https://github.com/UnionOfRAD/lithium
                 • https://github.com/zendframework/zf2
                 • http://pimple.sensiolabs.org/
                         And if we had more time...
                • http://twig.sensiolabs.org/
                • https://github.com/knplabs/KnpMenu
                • https://github.com/Seldaek/monolog
                • https://github.com/avalanche123/Imagine
 @weaverryan
Saturday, December 3, 11
Ad

More Related Content

Similar to A PHP Christmas Miracle - 3 Frameworks, 1 app (20)

Infinispan for Dummies
Infinispan for DummiesInfinispan for Dummies
Infinispan for Dummies
Galder Zamarreño
 
soft-shake.ch - Data grids and Data Caching
soft-shake.ch - Data grids and Data Cachingsoft-shake.ch - Data grids and Data Caching
soft-shake.ch - Data grids and Data Caching
soft-shake.ch
 
Linux Administration in 10 Minutes
Linux Administration in 10 MinutesLinux Administration in 10 Minutes
Linux Administration in 10 Minutes
ajones_mannix
 
Rails ORM De-mystifying Active Record has_many
Rails ORM De-mystifying Active Record has_manyRails ORM De-mystifying Active Record has_many
Rails ORM De-mystifying Active Record has_many
Blazing Cloud
 
Clojure: Return of the Jedi
Clojure: Return of the JediClojure: Return of the Jedi
Clojure: Return of the Jedi
Bozhidar Batsov
 
What's new in HTML5, CSS3 and JavaScript, James Pearce
What's new in HTML5, CSS3 and JavaScript, James PearceWhat's new in HTML5, CSS3 and JavaScript, James Pearce
What's new in HTML5, CSS3 and JavaScript, James Pearce
Sencha
 
Social apps & social scalability
Social apps & social scalabilitySocial apps & social scalability
Social apps & social scalability
Matias Paterlini
 
Java Tech & Tools | Big Blobs: Moving Big Data In and Out of the Cloud | Adri...
Java Tech & Tools | Big Blobs: Moving Big Data In and Out of the Cloud | Adri...Java Tech & Tools | Big Blobs: Moving Big Data In and Out of the Cloud | Adri...
Java Tech & Tools | Big Blobs: Moving Big Data In and Out of the Cloud | Adri...
JAX London
 
Ruby-on-Infinispan
Ruby-on-InfinispanRuby-on-Infinispan
Ruby-on-Infinispan
Galder Zamarreño
 
Some simple tips for front-end performance in WordPress
Some simple tips for front-end performance in WordPressSome simple tips for front-end performance in WordPress
Some simple tips for front-end performance in WordPress
iparr
 
Being Mobile: Mobile Web Development with WordPress
Being Mobile: Mobile Web Development with WordPressBeing Mobile: Mobile Web Development with WordPress
Being Mobile: Mobile Web Development with WordPress
BraveNewCode Inc.
 
soft-shake.ch - Data grids and Data Grids
soft-shake.ch - Data grids and Data Gridssoft-shake.ch - Data grids and Data Grids
soft-shake.ch - Data grids and Data Grids
soft-shake.ch
 
Introduction to ansible
Introduction to ansibleIntroduction to ansible
Introduction to ansible
Javier Arturo Rodríguez
 
Developers! Y U No Open Source Ur Code?
Developers! Y U No Open Source Ur Code?Developers! Y U No Open Source Ur Code?
Developers! Y U No Open Source Ur Code?
Craig Marvelley
 
Non Relational Databases And World Domination
Non Relational Databases And World DominationNon Relational Databases And World Domination
Non Relational Databases And World Domination
Jason Davies
 
Active Record Introduction - 3
Active Record Introduction - 3Active Record Introduction - 3
Active Record Introduction - 3
Blazing Cloud
 
Ruby + Rails
Ruby + RailsRuby + Rails
Ruby + Rails
betabeers
 
Apache Flume NG
Apache Flume NGApache Flume NG
Apache Flume NG
huguk
 
Einführung in Node.js
Einführung in Node.jsEinführung in Node.js
Einführung in Node.js
Sebastian Springer
 
Drupal and the rise of the documents
Drupal and the rise of the documentsDrupal and the rise of the documents
Drupal and the rise of the documents
Claudio Beatrice
 
soft-shake.ch - Data grids and Data Caching
soft-shake.ch - Data grids and Data Cachingsoft-shake.ch - Data grids and Data Caching
soft-shake.ch - Data grids and Data Caching
soft-shake.ch
 
Linux Administration in 10 Minutes
Linux Administration in 10 MinutesLinux Administration in 10 Minutes
Linux Administration in 10 Minutes
ajones_mannix
 
Rails ORM De-mystifying Active Record has_many
Rails ORM De-mystifying Active Record has_manyRails ORM De-mystifying Active Record has_many
Rails ORM De-mystifying Active Record has_many
Blazing Cloud
 
Clojure: Return of the Jedi
Clojure: Return of the JediClojure: Return of the Jedi
Clojure: Return of the Jedi
Bozhidar Batsov
 
What's new in HTML5, CSS3 and JavaScript, James Pearce
What's new in HTML5, CSS3 and JavaScript, James PearceWhat's new in HTML5, CSS3 and JavaScript, James Pearce
What's new in HTML5, CSS3 and JavaScript, James Pearce
Sencha
 
Social apps & social scalability
Social apps & social scalabilitySocial apps & social scalability
Social apps & social scalability
Matias Paterlini
 
Java Tech & Tools | Big Blobs: Moving Big Data In and Out of the Cloud | Adri...
Java Tech & Tools | Big Blobs: Moving Big Data In and Out of the Cloud | Adri...Java Tech & Tools | Big Blobs: Moving Big Data In and Out of the Cloud | Adri...
Java Tech & Tools | Big Blobs: Moving Big Data In and Out of the Cloud | Adri...
JAX London
 
Some simple tips for front-end performance in WordPress
Some simple tips for front-end performance in WordPressSome simple tips for front-end performance in WordPress
Some simple tips for front-end performance in WordPress
iparr
 
Being Mobile: Mobile Web Development with WordPress
Being Mobile: Mobile Web Development with WordPressBeing Mobile: Mobile Web Development with WordPress
Being Mobile: Mobile Web Development with WordPress
BraveNewCode Inc.
 
soft-shake.ch - Data grids and Data Grids
soft-shake.ch - Data grids and Data Gridssoft-shake.ch - Data grids and Data Grids
soft-shake.ch - Data grids and Data Grids
soft-shake.ch
 
Developers! Y U No Open Source Ur Code?
Developers! Y U No Open Source Ur Code?Developers! Y U No Open Source Ur Code?
Developers! Y U No Open Source Ur Code?
Craig Marvelley
 
Non Relational Databases And World Domination
Non Relational Databases And World DominationNon Relational Databases And World Domination
Non Relational Databases And World Domination
Jason Davies
 
Active Record Introduction - 3
Active Record Introduction - 3Active Record Introduction - 3
Active Record Introduction - 3
Blazing Cloud
 
Ruby + Rails
Ruby + RailsRuby + Rails
Ruby + Rails
betabeers
 
Apache Flume NG
Apache Flume NGApache Flume NG
Apache Flume NG
huguk
 
Drupal and the rise of the documents
Drupal and the rise of the documentsDrupal and the rise of the documents
Drupal and the rise of the documents
Claudio Beatrice
 

More from Ryan Weaver (19)

Webpack Encore Symfony Live 2017 San Francisco
Webpack Encore Symfony Live 2017 San FranciscoWebpack Encore Symfony Live 2017 San Francisco
Webpack Encore Symfony Live 2017 San Francisco
Ryan Weaver
 
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
Ryan Weaver
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Ryan Weaver
 
Guard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful Security
Ryan Weaver
 
Grand Rapids PHP Meetup: Behavioral Driven Development with Behat
Grand Rapids PHP Meetup: Behavioral Driven Development with BehatGrand Rapids PHP Meetup: Behavioral Driven Development with Behat
Grand Rapids PHP Meetup: Behavioral Driven Development with Behat
Ryan Weaver
 
Twig: Friendly Curly Braces Invade Your Templates!
Twig: Friendly Curly Braces Invade Your Templates!Twig: Friendly Curly Braces Invade Your Templates!
Twig: Friendly Curly Braces Invade Your Templates!
Ryan Weaver
 
Master the New Core of Drupal 8 Now: with Symfony and Silex
Master the New Core of Drupal 8 Now: with Symfony and SilexMaster the New Core of Drupal 8 Now: with Symfony and Silex
Master the New Core of Drupal 8 Now: with Symfony and Silex
Ryan Weaver
 
Silex: Microframework y camino fácil de aprender Symfony
Silex: Microframework y camino fácil de aprender SymfonySilex: Microframework y camino fácil de aprender Symfony
Silex: Microframework y camino fácil de aprender Symfony
Ryan Weaver
 
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love itDrupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Ryan Weaver
 
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other ToolsCool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Ryan Weaver
 
The Wonderful World of Symfony Components
The Wonderful World of Symfony ComponentsThe Wonderful World of Symfony Components
The Wonderful World of Symfony Components
Ryan Weaver
 
Symfony2: Get your project started
Symfony2: Get your project startedSymfony2: Get your project started
Symfony2: Get your project started
Ryan Weaver
 
Symony2 A Next Generation PHP Framework
Symony2 A Next Generation PHP FrameworkSymony2 A Next Generation PHP Framework
Symony2 A Next Generation PHP Framework
Ryan Weaver
 
Hands-on with the Symfony2 Framework
Hands-on with the Symfony2 FrameworkHands-on with the Symfony2 Framework
Hands-on with the Symfony2 Framework
Ryan Weaver
 
Being Dangerous with Twig (Symfony Live Paris)
Being Dangerous with Twig (Symfony Live Paris)Being Dangerous with Twig (Symfony Live Paris)
Being Dangerous with Twig (Symfony Live Paris)
Ryan Weaver
 
Being Dangerous with Twig
Being Dangerous with TwigBeing Dangerous with Twig
Being Dangerous with Twig
Ryan Weaver
 
Doctrine2 In 10 Minutes
Doctrine2 In 10 MinutesDoctrine2 In 10 Minutes
Doctrine2 In 10 Minutes
Ryan Weaver
 
Dependency Injection: Make your enemies fear you
Dependency Injection: Make your enemies fear youDependency Injection: Make your enemies fear you
Dependency Injection: Make your enemies fear you
Ryan Weaver
 
The Art of Doctrine Migrations
The Art of Doctrine MigrationsThe Art of Doctrine Migrations
The Art of Doctrine Migrations
Ryan Weaver
 
Webpack Encore Symfony Live 2017 San Francisco
Webpack Encore Symfony Live 2017 San FranciscoWebpack Encore Symfony Live 2017 San Francisco
Webpack Encore Symfony Live 2017 San Francisco
Ryan Weaver
 
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
Ryan Weaver
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Ryan Weaver
 
Guard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful Security
Ryan Weaver
 
Grand Rapids PHP Meetup: Behavioral Driven Development with Behat
Grand Rapids PHP Meetup: Behavioral Driven Development with BehatGrand Rapids PHP Meetup: Behavioral Driven Development with Behat
Grand Rapids PHP Meetup: Behavioral Driven Development with Behat
Ryan Weaver
 
Twig: Friendly Curly Braces Invade Your Templates!
Twig: Friendly Curly Braces Invade Your Templates!Twig: Friendly Curly Braces Invade Your Templates!
Twig: Friendly Curly Braces Invade Your Templates!
Ryan Weaver
 
Master the New Core of Drupal 8 Now: with Symfony and Silex
Master the New Core of Drupal 8 Now: with Symfony and SilexMaster the New Core of Drupal 8 Now: with Symfony and Silex
Master the New Core of Drupal 8 Now: with Symfony and Silex
Ryan Weaver
 
Silex: Microframework y camino fácil de aprender Symfony
Silex: Microframework y camino fácil de aprender SymfonySilex: Microframework y camino fácil de aprender Symfony
Silex: Microframework y camino fácil de aprender Symfony
Ryan Weaver
 
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love itDrupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Ryan Weaver
 
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other ToolsCool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Ryan Weaver
 
The Wonderful World of Symfony Components
The Wonderful World of Symfony ComponentsThe Wonderful World of Symfony Components
The Wonderful World of Symfony Components
Ryan Weaver
 
Symfony2: Get your project started
Symfony2: Get your project startedSymfony2: Get your project started
Symfony2: Get your project started
Ryan Weaver
 
Symony2 A Next Generation PHP Framework
Symony2 A Next Generation PHP FrameworkSymony2 A Next Generation PHP Framework
Symony2 A Next Generation PHP Framework
Ryan Weaver
 
Hands-on with the Symfony2 Framework
Hands-on with the Symfony2 FrameworkHands-on with the Symfony2 Framework
Hands-on with the Symfony2 Framework
Ryan Weaver
 
Being Dangerous with Twig (Symfony Live Paris)
Being Dangerous with Twig (Symfony Live Paris)Being Dangerous with Twig (Symfony Live Paris)
Being Dangerous with Twig (Symfony Live Paris)
Ryan Weaver
 
Being Dangerous with Twig
Being Dangerous with TwigBeing Dangerous with Twig
Being Dangerous with Twig
Ryan Weaver
 
Doctrine2 In 10 Minutes
Doctrine2 In 10 MinutesDoctrine2 In 10 Minutes
Doctrine2 In 10 Minutes
Ryan Weaver
 
Dependency Injection: Make your enemies fear you
Dependency Injection: Make your enemies fear youDependency Injection: Make your enemies fear you
Dependency Injection: Make your enemies fear you
Ryan Weaver
 
The Art of Doctrine Migrations
The Art of Doctrine MigrationsThe Art of Doctrine Migrations
The Art of Doctrine Migrations
Ryan Weaver
 
Ad

Recently uploaded (20)

HCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser EnvironmentsHCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser Environments
panagenda
 
Big Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur MorganBig Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur Morgan
Arthur Morgan
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
UiPath Community Berlin: Orchestrator API, Swagger, and Test Manager API
UiPath Community Berlin: Orchestrator API, Swagger, and Test Manager APIUiPath Community Berlin: Orchestrator API, Swagger, and Test Manager API
UiPath Community Berlin: Orchestrator API, Swagger, and Test Manager API
UiPathCommunity
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
How Can I use the AI Hype in my Business Context?
How Can I use the AI Hype in my Business Context?How Can I use the AI Hype in my Business Context?
How Can I use the AI Hype in my Business Context?
Daniel Lehner
 
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdfThe Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
Abi john
 
Role of Data Annotation Services in AI-Powered Manufacturing
Role of Data Annotation Services in AI-Powered ManufacturingRole of Data Annotation Services in AI-Powered Manufacturing
Role of Data Annotation Services in AI-Powered Manufacturing
Andrew Leo
 
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Impelsys Inc.
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep DiveDesigning Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
ScyllaDB
 
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc
 
Into The Box Conference Keynote Day 1 (ITB2025)
Into The Box Conference Keynote Day 1 (ITB2025)Into The Box Conference Keynote Day 1 (ITB2025)
Into The Box Conference Keynote Day 1 (ITB2025)
Ortus Solutions, Corp
 
What is Model Context Protocol(MCP) - The new technology for communication bw...
What is Model Context Protocol(MCP) - The new technology for communication bw...What is Model Context Protocol(MCP) - The new technology for communication bw...
What is Model Context Protocol(MCP) - The new technology for communication bw...
Vishnu Singh Chundawat
 
Rusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond SparkRusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond Spark
carlyakerly1
 
How analogue intelligence complements AI
How analogue intelligence complements AIHow analogue intelligence complements AI
How analogue intelligence complements AI
Paul Rowe
 
Mobile App Development Company in Saudi Arabia
Mobile App Development Company in Saudi ArabiaMobile App Development Company in Saudi Arabia
Mobile App Development Company in Saudi Arabia
Steve Jonas
 
HCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser EnvironmentsHCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser Environments
panagenda
 
Big Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur MorganBig Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur Morgan
Arthur Morgan
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul Shares 5 Steps to Implement AI Agents for Maximum Business Efficien...
Noah Loul
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
UiPath Community Berlin: Orchestrator API, Swagger, and Test Manager API
UiPath Community Berlin: Orchestrator API, Swagger, and Test Manager APIUiPath Community Berlin: Orchestrator API, Swagger, and Test Manager API
UiPath Community Berlin: Orchestrator API, Swagger, and Test Manager API
UiPathCommunity
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
How Can I use the AI Hype in my Business Context?
How Can I use the AI Hype in my Business Context?How Can I use the AI Hype in my Business Context?
How Can I use the AI Hype in my Business Context?
Daniel Lehner
 
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdfThe Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
Abi john
 
Role of Data Annotation Services in AI-Powered Manufacturing
Role of Data Annotation Services in AI-Powered ManufacturingRole of Data Annotation Services in AI-Powered Manufacturing
Role of Data Annotation Services in AI-Powered Manufacturing
Andrew Leo
 
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Impelsys Inc.
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep DiveDesigning Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
ScyllaDB
 
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc
 
Into The Box Conference Keynote Day 1 (ITB2025)
Into The Box Conference Keynote Day 1 (ITB2025)Into The Box Conference Keynote Day 1 (ITB2025)
Into The Box Conference Keynote Day 1 (ITB2025)
Ortus Solutions, Corp
 
What is Model Context Protocol(MCP) - The new technology for communication bw...
What is Model Context Protocol(MCP) - The new technology for communication bw...What is Model Context Protocol(MCP) - The new technology for communication bw...
What is Model Context Protocol(MCP) - The new technology for communication bw...
Vishnu Singh Chundawat
 
Rusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond SparkRusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond Spark
carlyakerly1
 
How analogue intelligence complements AI
How analogue intelligence complements AIHow analogue intelligence complements AI
How analogue intelligence complements AI
Paul Rowe
 
Mobile App Development Company in Saudi Arabia
Mobile App Development Company in Saudi ArabiaMobile App Development Company in Saudi Arabia
Mobile App Development Company in Saudi Arabia
Steve Jonas
 
Ad

A PHP Christmas Miracle - 3 Frameworks, 1 app

  • 1. A PHP Christmas Miracle A story of deception, wisdom, and finding our common interface Ryan Weaver @weaverryan Saturday, December 3, 11
  • 2. Who is this dude? • Co-author of the Symfony2 Docs • Core Symfony2 contributor • Co-owner of KnpLabs US • Fiancee of the much more talented @leannapelham http://www.knplabs.com/en http://www.github.com/weaverryan @weaverryan Saturday, December 3, 11
  • 3. Act 1: A History of modern PHP @weaverryan Saturday, December 3, 11
  • 4. First, the facts Saturday, December 3, 11
  • 5. To put it politely... Saturday, December 3, 11
  • 6. We have a surplus of PHP frameworks Saturday, December 3, 11
  • 7. To put it honestly... Saturday, December 3, 11
  • 8. We have a sh*tload Saturday, December 3, 11
  • 9. ‣ Symfony ‣ Zend Framework ‣ Lithium ‣ Aura (formerly Solar) ‣ Code Igniter ‣ Yii ‣ Cake ‣ Fuel ‣ Akelos ‣ Kohana ‣ Flow3 ‣ ... Saturday, December 3, 11
  • 10. And we solve common problems @weaverryan Saturday, December 3, 11
  • 11. Common Problems ‣ HTTP classes (e.g. request, response) ‣ Routing ‣ Controllers ‣ Forms ‣ Templates ‣ Events ‣ Loggers ‣ Mailing ‣ Database tools ‣ Validation ‣ Service containers ‣ Pagination ‣ Security ‣ Menus ‣ Serialization ‣ Search @weaverryan Saturday, December 3, 11
  • 12. Lack of sharing means duplicated efforts Saturday, December 3, 11
  • 13. But, there is some hope... Saturday, December 3, 11
  • 14. PSR-0 • The PHP community came together, sang Kumbaya and wrote up some class-naming standards • PSR-0 isn’t a library, it’s just an agreement to name your classes in one of two ways @weaverryan Saturday, December 3, 11
  • 15. PSR-0 with namespaces Namespace your classes and have the namespaces follow the directory structure class: SymfonyComponentHttpFoundationRequest path: vendor/src/Symfony/Component/HttpFoundation/Request.php Saturday, December 3, 11
  • 16. PSR-0 with underscores Use underscores in your classes and follow the directory structure class: Twig_Extension_Core path: vendor/twig/lib/Twig/Extension/Core.php Saturday, December 3, 11
  • 17. But what does this mean? • An "autoloader" is a tool you can use so that you don't have to worry about “including” classes before you use them • Use anyone’s autoloader • We're all still duplicating each other's work, but at least everyone’s autoloader does the same thing @weaverryan Saturday, December 3, 11
  • 18. What else can we agree on? Saturday, December 3, 11
  • 19. So far, not much Saturday, December 3, 11
  • 20. But we’ll see how all the libraries can still work together Saturday, December 3, 11
  • 21. And we can always hope for a Christmas miracle Saturday, December 3, 11
  • 22. Act 2: Starting with a Horrible App @weaverryan Saturday, December 3, 11
  • 23. How many of you use a framework on a regular basis? Saturday, December 3, 11
  • 24. How many of you have been told that it's a bad idea to not use a framework? Saturday, December 3, 11
  • 25. Was I the one who told you that? Saturday, December 3, 11
  • 26. Today we’re going to break the rules... Saturday, December 3, 11
  • 27. ... build a framework from “scratch” ... Saturday, December 3, 11
  • 28. ... and see why that’s no longer necessarily a bad thing. Saturday, December 3, 11
  • 29. Today’s 2 goals: • Refactor a crappy flat PHP application into a framework that makes sense • Use as many libraries from as many quarreling PHP tribes as possible ‣ Symfony ‣ Zend Framework ‣ Lithium ‣ ... only lack of time prevents more... @weaverryan Saturday, December 3, 11
  • 30. Our starting point • Following along with the code of our app at: http://bit.ly/php-xmas • Our app is a single file that fuels two pages Saturday, December 3, 11
  • 32. • Shucks, we even have a database connection Saturday, December 3, 11
  • 33. Open our Database connection // index.php try { $dbPath = __DIR__.'/data/database.sqlite'; $dbh = new PDO('sqlite:'.$dbPath); } catch(PDOException $e) { die('Panic! '.$e->getMessage()); } Saturday, December 3, 11
  • 34. Try to get a clean URI // index.php $uri = $_SERVER['REQUEST_URI']; if ($pos = strpos($uri, '?')) { $uri = substr($uri, 0, $pos); } Saturday, December 3, 11
  • 35. Render the homepage // index.php if ($uri == '/' || $uri == '') { echo '<h1>Welcome to PHP Santa</h1>'; echo '<a href="/letters">Readletters</a>'; if (isset($_GET['name'])) { echo sprintf( '<p>Oh, and hello %s!</p>', $_GET['name'] ); } } Saturday, December 3, 11
  • 36. Print out some letters // index.php if ($uri == '/letters') { $sql = 'SELECT * FROM php_santa_letters'; echo '<h1>Read the letters to PHP Santa</h1>'; echo '<ul>'; foreach ($dbh->query($sql) as $row) { echo sprintf( '<li>%s - dated %s</li>', $row['content'], $row['received_at'] ); } echo '</ul>'; } Saturday, December 3, 11
  • 38. Great, let’s clean this mess up Saturday, December 3, 11
  • 39. Act 3: Symfony's HTTP Foundation @weaverryan Saturday, December 3, 11
  • 40. Problems • Our code for trying to get a clean URL is a bit archaic and probably error prone • We're echoing content from our controllers, maybe we can evolve @weaverryan Saturday, December 3, 11
  • 41. Solution • Symfony’s HttpFoundation Component • Gives us (among other things) a solid Request and Response class @weaverryan Saturday, December 3, 11
  • 42. Bring in HttpFoundation mkdir -p vendors/Symfony/Component git submodule add git://github.com/symfony/HttpFoundation.git vendors/Symfony/Component/HttpFoundation/ git submodule add git://github.com/symfony/ClassLoader.git vendors/Symfony/Component/ClassLoader Saturday, December 3, 11
  • 44. Autoloading • No matter what framework or libraries you use, you’ll need an autoloader • We’ll use Symfony’s “ClassLoader” • Each PSR-0 autoloader is very similar @weaverryan Saturday, December 3, 11
  • 45. Create a bootstrap file <?php // bootstrap.php require __DIR__.'/vendors/Symfony/Component/ ClassLoader/UniversalClassLoader.php'; use SymfonyComponentClassLoaderUniversalClassLoader; // setup the autoloader $loader = new UniversalClassLoader(); $loader->registerNamespace( 'Symfony', __DIR__.'/vendors' ); $loader->register(); Saturday, December 3, 11
  • 46. ... and include it <?php // index.php require 'bootstrap.php'; // ... Saturday, December 3, 11
  • 47. So how does this help? Saturday, December 3, 11
  • 48. $uri = $_SERVER['REQUEST_URI']; if ($pos = strpos($uri, '?')) { $uri = substr($uri, 0, $pos); } ... becomes ... use SymfonyComponentHttpFoundationRequest; $request = Request::createFromGlobals(); // the clean URI - a lot of logic behind it!!! $uri = $request->getPathInfo(); Saturday, December 3, 11
  • 49. if (isset($_GET['name'])) { echo sprintf( '<p>Oh, and hello %s!</p>', $_GET['name'] ); } ... becomes ... if ($name = $request->query->get('name')) { echo sprintf( '<p>Oh, and hello %s!</p>', $name ); } Saturday, December 3, 11
  • 50. The “Request” object • Normalizes server variables across systems • Shortcut methods to common things like getClientIp(), getHost(), getContent(), etc • Nice object-oriented interface @weaverryan Saturday, December 3, 11
  • 51. The “Response” object • We also have a Response object • Instead of echoing out content, we populate this fluid object @weaverryan Saturday, December 3, 11
  • 52. header("HTTP/1.1 404 Not Found"); echo '<h1>404 Page not Found</h1>'; echo '<p>This is most certainly *not* an xmas miracle</p>'; ... becomes ... $content = '<h1>404 Page not Found</h1>'; $content .= '<p>This is most certainly *not* an xmas miracle</p>'; $response = new Response($content); $response->setStatusCode(404); $response->send(); Saturday, December 3, 11
  • 53. Act 4: Routing @weaverryan Saturday, December 3, 11
  • 54. Problems • Our app is a giant gross “if” statement if ($uri == '/' || $uri == '') { // ... } elseif ($uri == '/letters') { // ... } else { // ... } • Grabbing a piece from the URL like /blog/my-blog-post will take some work @weaverryan Saturday, December 3, 11
  • 55. Solution • Lithium’s Routing library • Routing matches URIs (e.g. /foo) and returns information we attached to that URI pattern • All the nasty regex matching is out-of-sight @weaverryan Saturday, December 3, 11
  • 56. 3 Steps to Bringing in an external tool Saturday, December 3, 11
  • 57. #1 Download the library git submodule add git://github.com/UnionOfRAD/ lithium.git vendors/lithium Saturday, December 3, 11
  • 58. #2 Configure the autoloader // bootstrap.php // ... $loader = new UniversalClassLoader(); $loader->registerNamespace('Symfony', __DIR__.'/vendors'); $loader->registerNamespace('lithium', __DIR__.'/vendors'); $loader->register(); Saturday, December 3, 11
  • 59. #3 Celebrate! use lithiumnethttpRouter; $router = new Router(); // ... Saturday, December 3, 11
  • 61. So how do we use the router? Saturday, December 3, 11
  • 62. Full disclosure: “use” statements I’m hiding from the next page use SymfonyComponentHttpFoundationRequest; use lithiumnethttpRouter; use lithiumactionRequest as Li3Request; Saturday, December 3, 11
  • 63. a) Map URI to “controller” $request = Request::createFromGlobals(); $li3Request = new Li3Request(); // get the URL from Symfony's request, give it to lithium $li3Request->url = $request->getPathInfo(); // create a router, build the routes, and then execute it $router = new Router(); $router->connect('/letters', array('controller' => 'letters')); $router->connect('/', array('controller' => 'homepage')); $router->parse($li3Request); if (isset($li3Request->params['controller'])) { $controller = $li3Request->params['controller']; } else { $controller = 'error404'; } Saturday, December 3, 11
  • 64. b) Execute the controller* // execute the controller, send the request, get the response $response = call_user_func_array($controller, array($request)); if (!$response instanceof Response) { throw new Exception(sprintf( 'WTF! Your controller "%s" didn't return a response!!', $controller )); } $response->send(); * each controller is a flat function Saturday, December 3, 11
  • 65. The Controllers function homepage(Request $request) { $content = '<h1>Welcome to PHP Santa</h1>'; $content .= '<a href="/letters">Read the letters</a>'; if ($name = $request->query->get('name')) { $content .= sprintf( '<p>Oh, and hello %s!</p>', $name ); } return new Response($content); } Saturday, December 3, 11
  • 66. The Controllers function letters(Request $request) { global $dbh; $kitten-- $sql = 'SELECT * FROM php_santa_letters'; $content = '<h1>Read the letters to PHP Santa</h1>'; $content .= '<ul>'; foreach ($dbh->query($sql) as $row) { $content .= sprintf( '<li>%s - dated %s</li>', $row['content'], $row['received_at'] ); } $content .= '</ul>'; return new Response($content); } Saturday, December 3, 11
  • 67. The Controllers function error404(Request $request) { $content = '<h1>404 Page not Found</h1>'; $content .= 'This is most certainly *not* an xmas miracle'; $response = new Response($content); $response->setStatusCode(404); return $response; } Saturday, December 3, 11
  • 68. The Big Picture 1. Request cleans the URI 2. Router matches the URI to a route, returns a “controller” string 3. We execute the controller function 4. The controller creates a Response object 5. We send the Response headers and content @weaverryan Saturday, December 3, 11
  • 69. Your 20 line framework Saturday, December 3, 11
  • 70. Act 5: Pimple! @weaverryan Saturday, December 3, 11
  • 71. Problems • We’ve got lots of random, disorganized objects floating around • And we can’t easily access them from within our controllers function letters(Request $request) { global $dbh; // .... } @weaverryan Saturday, December 3, 11
  • 72. Solution • Pimple! - a Dependency Injection Container • Dependency Injection Container: the scariest word we could think of to describe an array of objects on steroids @weaverryan Saturday, December 3, 11
  • 73. Remember: 3 Steps to bringing in an external tool Saturday, December 3, 11
  • 74. #1 Download the library git submodule add git://github.com/fabpot/ Pimple.git vendors/Pimple Saturday, December 3, 11
  • 75. #2 Configure the autoloader // bootstrap.php // ... require __DIR__.'/vendors/Pimple/lib/Pimple.php'; actually, it’s only one file - so just require it! Saturday, December 3, 11
  • 76. #3 Celebrate! $c = new Pimple(); Saturday, December 3, 11
  • 77. Pimple Creates Objects • Use Pimple to create and store your objects in a central place • If you have the Pimple container object, then you have access to every other object in your application @weaverryan Saturday, December 3, 11
  • 78. Centralize the db connection $c = new Pimple(); $c['connection'] = $c->share(function() { $dsn = 'sqlite:'.__DIR__.'/data/database.sqlite'; return new PDO($dsn); }); Saturday, December 3, 11
  • 79. Centralize the db connection $c1 = $c['connection']; $c2 = $c['connection']; // they are the same - only one object is created! $c1 === $c2 Saturday, December 3, 11
  • 80. Centralize the db connection $c1 = $c['connection']; $c2 = $c['connection']; // they are the same - only one object is created! $c1 === $c2 Saturday, December 3, 11
  • 81. Access to what we need • So far, we’re using a “global” keyword to access our database connection • But if we pass around our Pimple container, we always have access to anything we need - including the database connection @weaverryan Saturday, December 3, 11
  • 82. Pass the container to the controller $c = new Pimple(); // ... $response = call_user_func_array( $controller, array($request, $c) ); Saturday, December 3, 11
  • 83. function letters(Request $request, Pimple $c) { $dbh = $c['connection']; $kitten++ $sql = 'SELECT * FROM php_santa_letters'; $content = '<h1>Read the letters to PHP Santa</h1>'; $content .= '<ul>'; foreach ($dbh->query($sql) as $row) { // ... } // ... } Saturday, December 3, 11
  • 84. What else? How about configuration? Saturday, December 3, 11
  • 85. $c = new Pimple(); // configuration $c['connection_string'] = 'sqlite:'.__DIR__ .'/data/database.sqlite'; $c['connection'] = $c->share(function(Pimple $c) { return new PDO($c['connection_string']); }); Saturday, December 3, 11
  • 86. Further? What about dependencies? Saturday, December 3, 11
  • 87. $c['request'] = $c->share(function() { return Request::createFromGlobals(); }); $c['li3_request'] = $c->share(function($c) { $li3Request = new Li3Request(); $li3Request->url = $c['request']->getPathInfo(); return $li3Request; }); // ... $li3Request = $c['li3_request']; Saturday, December 3, 11
  • 88. With everything in the container, our “framework” just got skinny Saturday, December 3, 11
  • 89. $c = new Pimple(); // create objects in Pimple // execute our routing, merge attributes to request $result = $c['router']->parse($c['li3_request']); $c['request']->attributes ->add($c['li3_request']->params); // get controller and execute! $controller = $c['request']->attributes ->get('controller', 'error404'); $response = call_user_func_array( $controller, array($c['request'], $c) ); $response->send(); Saturday, December 3, 11
  • 90. Logging with ZF2 @weaverryan Saturday, December 3, 11
  • 91. Problems • I don’t have enough frameworks in my framework • Oh yeah, and we need logging... @weaverryan Saturday, December 3, 11
  • 92. Solution • Zend Framework2 • ZF2 has a ton of components, including a logger @weaverryan Saturday, December 3, 11
  • 93. 3 Steps to bringing in an external tool Saturday, December 3, 11
  • 94. #1 Download the library git submodule add git://github.com/ zendframework/zf2.git vendors/zf2 Saturday, December 3, 11
  • 95. #2 Configure the autoloader // bootstrap.php // ... $loader = new UniversalClassLoader(); $loader->registerNamespace('Symfony', __DIR__.'/vendors'); $loader->registerNamespace('lithium', __DIR__.'/vendors'); $loader->registerNamespace( 'Zend', __DIR__.'/vendors/zf2/library' ); $loader->register(); Saturday, December 3, 11
  • 96. #3 Celebrate! use ZendLogLogger; use ZendLogWriterStream; $logger = Logger($pimple['logger_writer']); Yes we did just bring in a 100k+ lines of code for a simple logger :) Saturday, December 3, 11
  • 98. Create the Logger in our Fancy Container use ZendLogLogger; use ZendLogWriterStream; $c['log_path'] = __DIR__.'/data/web.log'; $c['logger_writer'] = $c->share(function($pimple) { return new Stream($pimple['log_path']); }); $c['logger'] = $c->share(function($pimple) { return new Logger($pimple['logger_writer']); }); Saturday, December 3, 11
  • 99. And use it anywhere function error404(Request $request, Pimple $c) { $c['logger']->log( 'Crap, 404 for '.$request->getPathInfo(), Logger::ERR ); $content = '<h1>404 Page not Found</h1>'; // ... } Saturday, December 3, 11
  • 100. Getting kinda easy, right? Saturday, December 3, 11
  • 101. What other libraries can you think to integrate? Saturday, December 3, 11
  • 102. Getting Organized @weaverryan Saturday, December 3, 11
  • 103. Problems • Our application has 4 major parts: 1) autoloading setup 2) Creation of container 3) Definition of routes 4) Definition of controllers 5) The code that executes everything • For our business, only #3 and #4 are important • ... but it’s all jammed together @weaverryan Saturday, December 3, 11
  • 104. Solution • Some definitions “Application” - the code that makes you money “Framework” - under-the-hood code that impresses your geek friends • To be productive, let’s “hide” the framework @weaverryan Saturday, December 3, 11
  • 105. Starting point • Our app basically has 2 files ‣ bootstrap.php: holds autoloading ‣ index.php: holds - container setup - definition of routes - definition of controllers - the code that executes it all @weaverryan Saturday, December 3, 11
  • 106. Ending point ‣ bootstrap.php: holds - autoloading - container setup - the code that executes it all (as a function called _run_application()) ‣ controllers.php: holds controllers ‣ routes.php: holds routes ‣ index.php: pulls it all together @weaverryan Saturday, December 3, 11
  • 107. Nothing to see here... <?php // index.php $c = require 'bootstrap.php'; require 'routing.php'; require 'controllers.php'; $response = _run_application($c); $response->send(); Saturday, December 3, 11
  • 108. “Framework” hidden away... // bootstrap.php function _run_application(Pimple $c) { $c['router']->parse($c['li3_request']); $c['request']->attributes ->add($c['li3_request']->params); $controller = $c['request']->attributes ->get('controller', 'error404'); return call_user_func_array( $controller, array($c['request'], $c) ); } Saturday, December 3, 11
  • 109. Routes have a home // routing.php $c['router']->connect( '/letters', array('controller' => 'letters') ); $c['router']->connect( '/{:name}', array( 'controller' => 'homepage', 'name' => null ) ); Saturday, December 3, 11
  • 110. Controllers have a home // controllers.php use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; function homepage(Request $request) { // ... } function letters(Request $request, $c) { // ... } function error404(Request $request) { // ... } Saturday, December 3, 11
  • 111. To make $$$, work in routes.php and controllers.php Saturday, December 3, 11
  • 112. Final Thoughts @weaverryan Saturday, December 3, 11
  • 113. It doesn’t matter if you use a framework... Saturday, December 3, 11
  • 114. ... inherited a legacy spaghetti system... Saturday, December 3, 11
  • 115. ... or practice “not-invented- here” development Saturday, December 3, 11
  • 116. ...an innumerable number of tools are available... Saturday, December 3, 11
  • 117. ... so you can stop writing your framework ... Saturday, December 3, 11
  • 118. ... and start writing your application Saturday, December 3, 11
  • 119. What’s available? Search GitHub Saturday, December 3, 11
  • 120. Thanks! Questions? Ryan Weaver @weaverryan Saturday, December 3, 11
  • 121. References • http://bit.ly/php-xmas • http://github.com/symfony • https://github.com/UnionOfRAD/lithium • https://github.com/zendframework/zf2 • http://pimple.sensiolabs.org/ And if we had more time... • http://twig.sensiolabs.org/ • https://github.com/knplabs/KnpMenu • https://github.com/Seldaek/monolog • https://github.com/avalanche123/Imagine @weaverryan Saturday, December 3, 11