PHP 5.3 in practice

Fabien Potencier



   PHP 5.3 in practice – Fabien Potencier
Fabien Potencier
•  Serial entrepreneur and developer by passion
•  Founder of Sensio (in 1998)
   –  A services and consulting company
      specialized in Web technologies
      and Internet marketing (France and USA)
   –  70 people
   –  Open-Source specialists
   –  Big corporate customers
   –  Consulting, training, development, web design, … and more
   –  Sponsor of a lot of Open-Source projects
      like symfony and Doctrine
                               PHP 5.3 in practice – Fabien Potencier
Fabien Potencier
•  Creator and lead developer of symfony…
•  and creator and lead developer of some more OSS:
   –  Symfony components
   –  Swift Mailer : Powerful component based mailing library for PHP
   –  Twig : Fexible, fast, and secure template language for PHP
   –  Pirum : Simple PEAR Channel Server Manager
   –  Sismo : PHP continuous integration server
   –  Lime : Easy to use unit testing library for PHP
   –  Twitto : A web framework in a tweet
   –  Twittee : A Dependency Injection injector in a tweet
   –  Pimple : A small PHP 5.3 dependency injection injector

                                 PHP 5.3 in practice – Fabien Potencier
Fabien Potencier

•  Read my technical blog: http://fabien.potencier.org/

•  Follow me on Twitter: @fabpot

•  Fork my code on Github: http://github.com/fabpot/




                          PHP 5.3 in practice – Fabien Potencier
Migrating to PHP 5.3
… for technical reasons




  PHP 5.3 in practice – Fabien Potencier
Migrating to PHP 5.3?
•  Why?
  – Much faster
  – Less memory


•  When?
  – PHP 5.3.1 is available
  – PHP 5.3.2 is about to be released and stable
  – Migration is simple enough
                             PHP 5.3 in practice – Fabien Potencier
Migrating to PHP 5.3
… for speed




  PHP 5.3 in practice – Fabien Potencier
Dmitry Stogov did some benchmarks
                        for popular PHP applications

                            Drupal 20% faster
                             Typo3 30% faster
                          Wordpress 15% faster
                             Xoops 10% faster
http://news.php.net/php.internals/36484


                                PHP 5.3 in practice – Fabien Potencier
Doctrine 1.X and 2.0
Faster with PHP 5.3 and less memory consumption


         30% less memory

               20% faster


                PHP 5.3 in practice – Fabien Potencier
symfony 1




                                                                                                   -47%


symfony project running on PHP 5.2 vs PHP 5.3 profiled with XHPROF (run 4aeeb7d54b732 is PHP 5.3)



                                                    PHP 5.3 in practice – Fabien Potencier
Migrating to PHP 5.3
… for the ecosystem




  PHP 5.3 in practice – Fabien Potencier
Second generation of PHP frameworks
•  The next major versions of the most popular frameworks and libraries
   will use PHP 5.3
   –  Symfony 2.0
   –  Doctrine 2.0
                                       Late 2010
   –  Zend Framework 2.0


•  Better interoperability between these libraries, thanks to namespaces



                              PHP 5.3 in practice – Fabien Potencier
Namespaces and Symfony 2


SymfonyComponents
SymfonyFoundation
SymfonyFramework

SymfonyComponentsEventDispatcherEvent
SymfonyFoundationUniversalClassLoader


               PHP 5.3 in practice – Fabien Potencier
SymfonyComponentsEventDispatcherEvent


                              vs


                 sfEvent

                                                         Class names are
                                                           NOT shorter

                PHP 5.3 in practice – Fabien Potencier
Namespaces and Symfony 2

require __DIR__.'/lib/Symfony/Core/ClassLoader.php';

use SymfonyFoundationClassLoader;
use SymfonyComponentsEventDispatcherEvent;

$loader = new ClassLoader('Symfony', __DIR__.'/lib');
$loader->register();

$event = new Event();

                        PHP 5.3 in practice – Fabien Potencier
Namespaces and Symfony 2


require __DIR__.'/lib/Symfony/Core/ClassLoader.php';
use SymfonyFoundationClassLoader;
use SymfonyComponentsEventDispatcherEvent;

$loader = new ClassLoader('Symfony', __DIR__.'/lib');
$loader->register();

$event = new Event();


                        PHP 5.3 in practice – Fabien Potencier
PHP 5.3 technical interoperability standards

           « … describes the mandatory requirements
                     that must be adhered to
                for autoloader interoperability »


http://groups.google.com/group/php-standards/web/psr-0-final-proposal




                           PHP 5.3 in practice – Fabien Potencier
Why?
•  Libraries following the specification are interoperable

•  For instance, if you use Symfony 2.0, Doctrine 2.0, and Zend Framework
   2.0 in the same project
   –  They can all share the same autoloader
   –  Symfony 2.0 provides an enhanced autoloader (with PEAR support)
   –  A native C extension has been created




                                PHP 5.3 in practice – Fabien Potencier
Let’s dive into PHP 5.3




  PHP 5.3 in practice – Fabien Potencier
PHP 5.3
•  A lot of changes

   –  Convenient changes: __DIR__, ?:, NOWDOC, …

   –  New features: i18n, SPL, Date management, mysqlnd, …

   –  Language enhancements: namespaces, anonymous functions, closures, late
      static binding, phar, Windows support, …




                               PHP 5.3 in practice – Fabien Potencier
PHP 5.3

How does it change the implementation
of some well-known Design Pattern?


   PHP 5.3 in practice – Fabien Potencier
PHP 5.3
… and the Singleton




  PHP 5.3 in practice – Fabien Potencier
The Singleton may cause
     serious damage
       to your code
         PHP 5.3 in practice – Fabien Potencier
History of the Singleton




  PHP 5.3 in practice – Fabien Potencier
The Singleton in PHP 4
class Singleton
{
  function &getInstance()
  {
    static $instance;

        if (!$instance)
        {
          $instance = new Singleton();
        }


    }
        return $instance;                                                        You can still
}                                                                           ins tantiate the class
$obj =& Singleton::getInstance();
                                                                                    directly
                                   PHP 5.3 in practice – Fabien Potencier
The Singleton in PHP 5.0/5.1/5.2
class Singleton
{
  static private $instance;

    private function __construct() {}

    static public function getInstance()
    {
      if (null === self::$instance)
      {
        self::$instance = new self();
      }

        return self::$instance;

                                                                                  do not forget to
    }


}
    final private function __clone() {}
                                                                                 ove rride __clone()
$obj = Singleton::getInstance();
                                        PHP 5.3 in practice – Fabien Potencier
The Singleton in PHP 5.3
abstract class Singleton
{
  private static $instances = array();

    final private function __construct()
    {
      if (isset(self::$instances[get_called_class()]))
      {
        throw new Exception("An instance of ".get_called_class()." already exists.");
      }
      static::initialize();
    }

    protected function initialize() {}

    final public static function getInstance()
    {
      $class = get_called_class();
      if (!isset(self::$instances[$class]))
      {
        self::$instances[$class] = new static();
      }
      return self::$instances[$class];
    }

    final private function __clone() {}
}

                                                    PHP 5.3 in practice – Fabien Potencier
The Singleton in PHP 5.3

class Foo extends Singleton {}
class Bar extends Singleton {}

$a = Foo::getInstance();
$b = Bar::getInstance();




                      PHP 5.3 in practice – Fabien Potencier
PHP 5.3
…Late Static Binding

The ORM problem



   PHP 5.3 in practice – Fabien Potencier
class Model
{
  static public function getMe()
  {
    return __CLASS__;
  }
}

class Article extends Model {}

echo Article::getMe();

                   PHP 5.3 in practice – Fabien Potencier
<?php

class Model
{
  static public function getMe()
  {
    return get_called_class(); as of PHP 5.3
  }
}

class Article extends Model {}

echo Article::getMe();
                     PHP 5.3 in practice – Fabien Potencier
class Model
{
  static public function findByPk($id)
  {
    $table = strtolower(get_called_class());

        return $db->get(
         sprintf('SELECT * FROM %s WHERE id = %d', $table, $id)
        );
    }
}

class Article extends Model {}

$article = Article::findByPk(1);

                             PHP 5.3 in practice – Fabien Potencier
class Model
{
  static public function __callStatic($method, $arguments)
  {
    $table = strtolower(get_called_class());
    $column = strtolower(substr($method, 6));
    $value = $arguments[0];

    $sql = sprintf('SELECT * FROM %s WHERE %s = ?', $table,
$column);

        return $db->get($sql, $value);
    }
}

class Article extends Model {}

$article = Article::findByTitle('foo');
                               PHP 5.3 in practice – Fabien Potencier
PHP 5.3
…interlude

Anonymous functions
Lambdas


   PHP 5.3 in practice – Fabien Potencier
An anonymous function
                  is a function
           defined on the fly (no name)


function () { echo 'Hello world!'; };


                   PHP 5.3 in practice – Fabien Potencier
Can be stored in a variable

$hello = function () { echo 'Hello world!'; };




                     PHP 5.3 in practice – Fabien Potencier
… to be used later on


$hello();


call_user_func($hello);



                   PHP 5.3 in practice – Fabien Potencier
… or can be passed as a function argument

function foo(Closure $func)
{
  $func();
}

foo($hello);

                    PHP 5.3 in practice – Fabien Potencier
Fonctions anonymes
                    Can take arguments

$hello = function ($name) { echo 'Hello '.$name; };

$hello('Fabien');

call_user_func($hello, 'Fabien');




                            PHP 5.3 in practice – Fabien Potencier
Fonctions anonymes

function foo(Closure $func, $name)
{
  $func($name);
}

foo($hello, 'Fabien');




                         PHP 5.3 in practice – Fabien Potencier
When is it useful?




  PHP 5.3 in practice – Fabien Potencier
array_*
Greatly simplify usage of some array_* functions

  array_map()

  array_reduce()

  array_filter()




                              PHP 5.3 in practice – Fabien Potencier
class Article
{
  public function __construct($title)
  {
    $this->title = $title;
  }

    public function getTitle()
    {
      return $this->title;
    }
}
                     PHP 5.3 in practice – Fabien Potencier
How to get an array of all article titles?




$articles = array(
   new Article('PHP UK - part 1'),
   new Article('PHP UK - part 2'),
   new Article('See you next year!'),
);



                                 PHP 5.3 in practice – Fabien Potencier
$titles = array();
foreach ($articles as $article)
{
  $titles[] = $article->getTitle();
}



               PHP 5.3 in practice – Fabien Potencier
$titles = array_map(
   create_function('$article', 'return $article->getTitle();'),
   $articles
);




                          PHP 5.3 in practice – Fabien Potencier
$titles = array_map(
   function ($article) { return $article->getTitle(); },
   $articles
);




                       PHP 5.3 in practice – Fabien Potencier
$titles = array();
foreach ($articles as $article)
{
  $titles[] = $article->getTitle();
}

                                                                                100     100
$titles = array_map(create_function('$article', 'return $article->getTitle();'), $articles);

                                                                                300     1800
$titles = array_map(function ($article) { return $article->getTitle(); }, $articles);

                                                                                100     200
$mapper = function ($article) { return $article->getTitle(); };
$titles = array_map($mapper, $articles);

                                                                                100     180

      memory            speed

                                       PHP 5.3 in practice – Fabien Potencier
$mapper = function ($article) {
   return $article->getTitle();
};

$titles = array_map($mapper, $articles);

$mapper = function ($article) {
   return $article->getAuthor();
};

$authors = array_map($mapper, $articles);
                    PHP 5.3 in practice – Fabien Potencier
A closure is a lambda
that remembers the context
      of its creation…

        PHP 5.3 in practice – Fabien Potencier
$mapper = function ($method)
{
   return function ($article) use($method)
   {
      return $article->$method();
   };
};



                    PHP 5.3 in practice – Fabien Potencier
$method = 'getTitle';

$mapper = function ($article) use($method)
{
   return $article->$method();
};

$method = 'getAuthor';

$titles = array_map($mapper, $articles);

                   PHP 5.3 in practice – Fabien Potencier
$titleMapper = $mapper('getTitle');
$titles = array_map($titleMapper, $articles);


$authorMapper = $mapper('getAuthor');
$authors = array_map($authorMapper, $articles);



                   PHP 5.3 in practice – Fabien Potencier
$titles = array_map($mapper('getTitle'), $articles);


$authors = array_map($mapper('getAuthor'), $articles);




                      PHP 5.3 in practice – Fabien Potencier
Dependency Injector




  PHP 5.3 in practice – Fabien Potencier
« Dependency Injection is where components
 are given their dependencies through their
constructors, methods, or directly into fields. »

http://www.picoinjector.org/injection.html



                                             PHP 5.3 in practice – Fabien Potencier
Dependency Injection

A real world « web » example



   PHP 5.3 in practice – Fabien Potencier
In most web applications, you need to manage the user preferences

   –  The user language
   –  Whether the user is authenticated or not
   –  The user credentials
   –  …




                                 PHP 5.3 in practice – Fabien Potencier
This can be done with a User object

   –  setLanguage(), getLanguage()
   –  setAuthenticated(), isAuthenticated()
   –  addCredential(), hasCredential()
   –  ...




                              PHP 5.3 in practice – Fabien Potencier
The User information
         need to be persisted
        between HTTP requests

We use the PHP session for the Storage


             PHP 5.3 in practice – Fabien Potencier
class SessionStorage
{
  function __construct($cookieName = 'PHP_SESS_ID')
  {
    session_name($cookieName);
    session_start();
  }

    function set($key, $value)
    {
      $_SESSION[$key] = $value;
    }

    // ...
}

                            PHP 5.3 in practice – Fabien Potencier
class User
{
  protected $storage;

    function __construct()                                           Very hard to
    {                                                                 customize
      $this->storage = new SessionStorage();
    }

    function setLanguage($language)
    {
      $this->storage->set('language', $language);
    }

    // ...
}                         Very easy
                            to use
$user = new User();         PHP 5.3 in practice – Fabien Potencier
class User
{
  protected $storage;                                    Very easy to
                                                          customize
    function __construct($storage)
    {
      $this->storage = $storage;
    }
}

$storage = new SessionStorage();
$user = new User($storage);
                                                  Slightly more
                                                  difficult to use
                            PHP 5.3 in practice – Fabien Potencier
That’s Dependency Injection

      Nothing more


         PHP 5.3 in practice – Fabien Potencier
Instead of harcoding
    the Storage dependency
inside the User class constructor

Inject the Storage dependency
       in the User object

           PHP 5.3 in practice – Fabien Potencier
A Dependency Injector
      Describes objects
   and their dependencies

  Instantiates and configures
      objects on-demand
         PHP 5.3 in practice – Fabien Potencier
An injector
SHOULD be able to manage
  ANY PHP object (POPO)


The objects MUST not know
  that they are managed
      by the injector

      PHP 5.3 in practice – Fabien Potencier
•  Parameters
   –  The SessionStorage implementation we want to use (the class name)
   –  The session name
•  Objects
   –  SessionStorage
   –  User
•  Dependencies
   –  User depends on a SessionStorage implementation


                                PHP 5.3 in practice – Fabien Potencier
Let’s build a simple injector
with PHP 5.3




  PHP 5.3 in practice – Fabien Potencier
Dependency Injector

Managing parameters



   PHP 5.3 in practice – Fabien Potencier
class Injector
{
  protected $parameters = array();

    public function setParameter($key, $value)
    {
      $this->parameters[$key] = $value;
    }

    public function getParameter($key)
    {
      return $this->parameters[$key];
    }
}
                        PHP 5.3 in practice – Fabien Potencier
Decoupling


$injector = new Injector();                               Customization
$injector->setParameter('session_name', 'SESSION_ID');
$injector->setParameter('storage_class', 'SessionStorage');




$class = $injector->getParameter('storage_class');
$sessionStorage = new $class($injector->getParameter('session_name'));
$user = new User($sessionStorage);
                                                                      Objects creation

                             PHP 5.3 in practice – Fabien Potencier
class Injector                                                    Using PHP
{                                                                magic methods
  protected $parameters = array();

    public function __set($key, $value)
    {
      $this->parameters[$key] = $value;
    }

    public function __get($key)
    {
      return $this->parameters[$key];
    }
}
                        PHP 5.3 in practice – Fabien Potencier
Interface
                                                                       is cleaner


$injector = new Injector();
$injector->session_name = 'SESSION_ID';
$injector->storage_class = 'SessionStorage';

$sessionStorage = new $injector->storage_class($injector->session_name);
$user = new User($sessionStorage);




                              PHP 5.3 in practice – Fabien Potencier
Dependency Injector

Managing objects



   PHP 5.3 in practice – Fabien Potencier
We need a way to describe how to create objects,
    without actually instantiating anything!

      Anonymous functions to the rescue!

                  PHP 5.3 in practice – Fabien Potencier
class Injector
{
  protected $parameters = array();
  protected $objects = array();

    public function __set($key, $value)
    {
      $this->parameters[$key] = $value;
    }

    public function __get($key)                                                Store a lambda
    {
      return $this->parameters[$key];                                         able to create the
                                                                              object on-demand
    }

    public function setService($key, Closure $service)
    {
      $this->objects[$key] = $service;
    }

    public function getService($key)                              Ask the closure to create
    {
      return $this->objects[$key]($this);                          th e object and pass the
}
    }                                                                   current injector
                                     PHP 5.3 in practice – Fabien Potencier
$injector = new Injector();
$injector->session_name = 'SESSION_ID';                                         Description
$injector->storage_class = 'SessionStorage';
$injector->setService('user', function ($c)
{
  return new User($c->getService('storage'));
});
$injector->setService('storage', function ($c)
{
  return new $c->storage_class($c->session_name);
});


                                                                       Creating the User
                                                                   is now as easy as before
$user = $injector->getService('user');
                          PHP 5.3 in practice – Fabien Potencier
class Injector
{
  protected $values = array();                                            Simplify the code
    function __set($id, $value)
    {
      $this->values[$id] = $value;
    }

    function __get($id)
    {
      if (is_callable($this->values[$id]))
      {
        return $this->values[$id]($this);
      }
      else
      {
        return $this->values[$id];
      }
    }
}
                                 PHP 5.3 in practice – Fabien Potencier
$injector = new Injector();
                                            Unified interface
$injector->session_name = 'SESSION_ID';
$injector->storage_class = 'SessionStorage';
$injector->user = function ($c)
{
   return new User($c->storage);
};
$injector->storage = function ($c)
{
   return new $c->storage_class($c->session_name);
};




$user = $injector->user;
                           PHP 5.3 in practice – Fabien Potencier
Dependency Injector

Scope



   PHP 5.3 in practice – Fabien Potencier
For some objects, like the user,
    the injector must always
    return the same instance


          PHP 5.3 in practice – Fabien Potencier
spl_object_hash($injector->user)



            !==
spl_object_hash($injector->user)

            PHP 5.3 in practice – Fabien Potencier
$injector->user = function ($c)
{
  static $user;

  if (is_null($user))
  {
    $user = new User($c->storage);
  }

  return $user;
};
                   PHP 5.3 in practice – Fabien Potencier
spl_object_hash($injector->user)



            ===
spl_object_hash($injector->user)

            PHP 5.3 in practice – Fabien Potencier
$injector->user = $injector->asShared(function ($c)
{
  return new User($c->storage);
});




                      PHP 5.3 in practice – Fabien Potencier
function asShared(Closure $lambda)
{
  return function ($injector) use ($lambda)
  {
    static $object;

      if (is_null($object))
      {
        $object = $lambda($injector);
      }
      return $object;
    };
}
                     PHP 5.3 in practice – Fabien Potencier
class Injector
{
  protected $values = array();

    function __set($id, $value)
    {
      $this->values[$id] = $value;
    }

    function __get($id)
    {                                                                            Error management
      if (!isset($this->values[$id]))
      {
        throw new InvalidArgumentException(sprintf('Value "%s" is not defined.', $id));
      }

        if (is_callable($this->values[$id]))
        {
          return $this->values[$id]($this);
        }
        else
        {
          return $this->values[$id];
        }
    }
}                                       PHP 5.3 in practice – Fabien Potencier
class injector
{
  protected $values = array();

    function __set($id, $value)
    {
      $this->values[$id] = $value;
    }

    function __get($id)
    {
      if (!isset($this->values[$id]))
      {
        throw new InvalidArgumentException(sprintf('Value "%s" is not defined.', $id));
      }

        if (is_callable($this->values[$id]))
        {
          return $this->values[$id]($this);
        }
        else
        {
          return $this->values[$id];
        }
    }

    function asShared($callable)
    {


                                                                                                    40 LOC for a fully-
      return function ($c) use ($callable)
      {
        static $object;

          if (is_null($object))
          {
            $object = $callable($c);
                                                                                                     featured injector
          }
          return $object;
        };
    }
}                                                          PHP 5.3 in practice – Fabien Potencier
I’m NOT advocating
the usage of lambdas everywhere

  This presentation was about
    showing how they work
     on practical examples
            PHP 5.3 in practice – Fabien Potencier
Questions?




  PHP 5.3 in practice – Fabien Potencier
Sensio S.A.
    92-98, boulevard Victor Hugo
        92 115 Clichy Cedex
              FRANCE
       Tél. : +33 1 40 99 80 80

               Contact
           Fabien Potencier
    fabien.potencier at sensio.com




  http://www.sensiolabs.com/
http://www.symfony-project.org/
 http://fabien.potencier.org/


       PHP 5.3 in practice – Fabien Potencier

PHP 5.3 in practice

  • 1.
    PHP 5.3 inpractice Fabien Potencier PHP 5.3 in practice – Fabien Potencier
  • 2.
    Fabien Potencier •  Serialentrepreneur and developer by passion •  Founder of Sensio (in 1998) –  A services and consulting company specialized in Web technologies and Internet marketing (France and USA) –  70 people –  Open-Source specialists –  Big corporate customers –  Consulting, training, development, web design, … and more –  Sponsor of a lot of Open-Source projects like symfony and Doctrine PHP 5.3 in practice – Fabien Potencier
  • 3.
    Fabien Potencier •  Creatorand lead developer of symfony… •  and creator and lead developer of some more OSS: –  Symfony components –  Swift Mailer : Powerful component based mailing library for PHP –  Twig : Fexible, fast, and secure template language for PHP –  Pirum : Simple PEAR Channel Server Manager –  Sismo : PHP continuous integration server –  Lime : Easy to use unit testing library for PHP –  Twitto : A web framework in a tweet –  Twittee : A Dependency Injection injector in a tweet –  Pimple : A small PHP 5.3 dependency injection injector PHP 5.3 in practice – Fabien Potencier
  • 4.
    Fabien Potencier •  Readmy technical blog: http://fabien.potencier.org/ •  Follow me on Twitter: @fabpot •  Fork my code on Github: http://github.com/fabpot/ PHP 5.3 in practice – Fabien Potencier
  • 5.
    Migrating to PHP5.3 … for technical reasons PHP 5.3 in practice – Fabien Potencier
  • 6.
    Migrating to PHP5.3? •  Why? – Much faster – Less memory •  When? – PHP 5.3.1 is available – PHP 5.3.2 is about to be released and stable – Migration is simple enough PHP 5.3 in practice – Fabien Potencier
  • 7.
    Migrating to PHP5.3 … for speed PHP 5.3 in practice – Fabien Potencier
  • 8.
    Dmitry Stogov did somebenchmarks for popular PHP applications Drupal 20% faster Typo3 30% faster Wordpress 15% faster Xoops 10% faster http://news.php.net/php.internals/36484 PHP 5.3 in practice – Fabien Potencier
  • 9.
    Doctrine 1.X and2.0 Faster with PHP 5.3 and less memory consumption 30% less memory 20% faster PHP 5.3 in practice – Fabien Potencier
  • 10.
    symfony 1 -47% symfony project running on PHP 5.2 vs PHP 5.3 profiled with XHPROF (run 4aeeb7d54b732 is PHP 5.3) PHP 5.3 in practice – Fabien Potencier
  • 11.
    Migrating to PHP5.3 … for the ecosystem PHP 5.3 in practice – Fabien Potencier
  • 12.
    Second generation ofPHP frameworks •  The next major versions of the most popular frameworks and libraries will use PHP 5.3 –  Symfony 2.0 –  Doctrine 2.0 Late 2010 –  Zend Framework 2.0 •  Better interoperability between these libraries, thanks to namespaces PHP 5.3 in practice – Fabien Potencier
  • 13.
    Namespaces and Symfony2 SymfonyComponents SymfonyFoundation SymfonyFramework SymfonyComponentsEventDispatcherEvent SymfonyFoundationUniversalClassLoader PHP 5.3 in practice – Fabien Potencier
  • 14.
    SymfonyComponentsEventDispatcherEvent vs sfEvent Class names are NOT shorter PHP 5.3 in practice – Fabien Potencier
  • 15.
    Namespaces and Symfony2 require __DIR__.'/lib/Symfony/Core/ClassLoader.php'; use SymfonyFoundationClassLoader; use SymfonyComponentsEventDispatcherEvent; $loader = new ClassLoader('Symfony', __DIR__.'/lib'); $loader->register(); $event = new Event(); PHP 5.3 in practice – Fabien Potencier
  • 16.
    Namespaces and Symfony2 require __DIR__.'/lib/Symfony/Core/ClassLoader.php'; use SymfonyFoundationClassLoader; use SymfonyComponentsEventDispatcherEvent; $loader = new ClassLoader('Symfony', __DIR__.'/lib'); $loader->register(); $event = new Event(); PHP 5.3 in practice – Fabien Potencier
  • 17.
    PHP 5.3 technicalinteroperability standards « … describes the mandatory requirements that must be adhered to for autoloader interoperability » http://groups.google.com/group/php-standards/web/psr-0-final-proposal PHP 5.3 in practice – Fabien Potencier
  • 18.
    Why? •  Libraries followingthe specification are interoperable •  For instance, if you use Symfony 2.0, Doctrine 2.0, and Zend Framework 2.0 in the same project –  They can all share the same autoloader –  Symfony 2.0 provides an enhanced autoloader (with PEAR support) –  A native C extension has been created PHP 5.3 in practice – Fabien Potencier
  • 19.
    Let’s dive intoPHP 5.3 PHP 5.3 in practice – Fabien Potencier
  • 20.
    PHP 5.3 •  Alot of changes –  Convenient changes: __DIR__, ?:, NOWDOC, … –  New features: i18n, SPL, Date management, mysqlnd, … –  Language enhancements: namespaces, anonymous functions, closures, late static binding, phar, Windows support, … PHP 5.3 in practice – Fabien Potencier
  • 21.
    PHP 5.3 How doesit change the implementation of some well-known Design Pattern? PHP 5.3 in practice – Fabien Potencier
  • 22.
    PHP 5.3 … andthe Singleton PHP 5.3 in practice – Fabien Potencier
  • 23.
    The Singleton maycause serious damage to your code PHP 5.3 in practice – Fabien Potencier
  • 24.
    History of theSingleton PHP 5.3 in practice – Fabien Potencier
  • 25.
    The Singleton inPHP 4 class Singleton { function &getInstance() { static $instance; if (!$instance) { $instance = new Singleton(); } } return $instance; You can still } ins tantiate the class $obj =& Singleton::getInstance(); directly PHP 5.3 in practice – Fabien Potencier
  • 26.
    The Singleton inPHP 5.0/5.1/5.2 class Singleton { static private $instance; private function __construct() {} static public function getInstance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; do not forget to } } final private function __clone() {} ove rride __clone() $obj = Singleton::getInstance(); PHP 5.3 in practice – Fabien Potencier
  • 27.
    The Singleton inPHP 5.3 abstract class Singleton { private static $instances = array(); final private function __construct() { if (isset(self::$instances[get_called_class()])) { throw new Exception("An instance of ".get_called_class()." already exists."); } static::initialize(); } protected function initialize() {} final public static function getInstance() { $class = get_called_class(); if (!isset(self::$instances[$class])) { self::$instances[$class] = new static(); } return self::$instances[$class]; } final private function __clone() {} } PHP 5.3 in practice – Fabien Potencier
  • 28.
    The Singleton inPHP 5.3 class Foo extends Singleton {} class Bar extends Singleton {} $a = Foo::getInstance(); $b = Bar::getInstance(); PHP 5.3 in practice – Fabien Potencier
  • 29.
    PHP 5.3 …Late StaticBinding The ORM problem PHP 5.3 in practice – Fabien Potencier
  • 30.
    class Model { static public function getMe() { return __CLASS__; } } class Article extends Model {} echo Article::getMe(); PHP 5.3 in practice – Fabien Potencier
  • 31.
    <?php class Model { static public function getMe() { return get_called_class(); as of PHP 5.3 } } class Article extends Model {} echo Article::getMe(); PHP 5.3 in practice – Fabien Potencier
  • 32.
    class Model { static public function findByPk($id) { $table = strtolower(get_called_class()); return $db->get( sprintf('SELECT * FROM %s WHERE id = %d', $table, $id) ); } } class Article extends Model {} $article = Article::findByPk(1); PHP 5.3 in practice – Fabien Potencier
  • 33.
    class Model { static public function __callStatic($method, $arguments) { $table = strtolower(get_called_class()); $column = strtolower(substr($method, 6)); $value = $arguments[0]; $sql = sprintf('SELECT * FROM %s WHERE %s = ?', $table, $column); return $db->get($sql, $value); } } class Article extends Model {} $article = Article::findByTitle('foo'); PHP 5.3 in practice – Fabien Potencier
  • 34.
    PHP 5.3 …interlude Anonymous functions Lambdas PHP 5.3 in practice – Fabien Potencier
  • 35.
    An anonymous function is a function defined on the fly (no name) function () { echo 'Hello world!'; }; PHP 5.3 in practice – Fabien Potencier
  • 36.
    Can be storedin a variable $hello = function () { echo 'Hello world!'; }; PHP 5.3 in practice – Fabien Potencier
  • 37.
    … to beused later on $hello(); call_user_func($hello); PHP 5.3 in practice – Fabien Potencier
  • 38.
    … or canbe passed as a function argument function foo(Closure $func) { $func(); } foo($hello); PHP 5.3 in practice – Fabien Potencier
  • 39.
    Fonctions anonymes Can take arguments $hello = function ($name) { echo 'Hello '.$name; }; $hello('Fabien'); call_user_func($hello, 'Fabien'); PHP 5.3 in practice – Fabien Potencier
  • 40.
    Fonctions anonymes function foo(Closure$func, $name) { $func($name); } foo($hello, 'Fabien'); PHP 5.3 in practice – Fabien Potencier
  • 41.
    When is ituseful? PHP 5.3 in practice – Fabien Potencier
  • 42.
    array_* Greatly simplify usageof some array_* functions array_map() array_reduce() array_filter() PHP 5.3 in practice – Fabien Potencier
  • 43.
    class Article { public function __construct($title) { $this->title = $title; } public function getTitle() { return $this->title; } } PHP 5.3 in practice – Fabien Potencier
  • 44.
    How to getan array of all article titles? $articles = array( new Article('PHP UK - part 1'), new Article('PHP UK - part 2'), new Article('See you next year!'), ); PHP 5.3 in practice – Fabien Potencier
  • 45.
    $titles = array(); foreach($articles as $article) { $titles[] = $article->getTitle(); } PHP 5.3 in practice – Fabien Potencier
  • 46.
    $titles = array_map( create_function('$article', 'return $article->getTitle();'), $articles ); PHP 5.3 in practice – Fabien Potencier
  • 47.
    $titles = array_map( function ($article) { return $article->getTitle(); }, $articles ); PHP 5.3 in practice – Fabien Potencier
  • 48.
    $titles = array(); foreach($articles as $article) { $titles[] = $article->getTitle(); } 100 100 $titles = array_map(create_function('$article', 'return $article->getTitle();'), $articles); 300 1800 $titles = array_map(function ($article) { return $article->getTitle(); }, $articles); 100 200 $mapper = function ($article) { return $article->getTitle(); }; $titles = array_map($mapper, $articles); 100 180 memory speed PHP 5.3 in practice – Fabien Potencier
  • 49.
    $mapper = function($article) { return $article->getTitle(); }; $titles = array_map($mapper, $articles); $mapper = function ($article) { return $article->getAuthor(); }; $authors = array_map($mapper, $articles); PHP 5.3 in practice – Fabien Potencier
  • 50.
    A closure isa lambda that remembers the context of its creation… PHP 5.3 in practice – Fabien Potencier
  • 51.
    $mapper = function($method) { return function ($article) use($method) { return $article->$method(); }; }; PHP 5.3 in practice – Fabien Potencier
  • 52.
    $method = 'getTitle'; $mapper= function ($article) use($method) { return $article->$method(); }; $method = 'getAuthor'; $titles = array_map($mapper, $articles); PHP 5.3 in practice – Fabien Potencier
  • 53.
    $titleMapper = $mapper('getTitle'); $titles= array_map($titleMapper, $articles); $authorMapper = $mapper('getAuthor'); $authors = array_map($authorMapper, $articles); PHP 5.3 in practice – Fabien Potencier
  • 54.
    $titles = array_map($mapper('getTitle'),$articles); $authors = array_map($mapper('getAuthor'), $articles); PHP 5.3 in practice – Fabien Potencier
  • 55.
    Dependency Injector PHP 5.3 in practice – Fabien Potencier
  • 56.
    « Dependency Injection iswhere components are given their dependencies through their constructors, methods, or directly into fields. » http://www.picoinjector.org/injection.html PHP 5.3 in practice – Fabien Potencier
  • 57.
    Dependency Injection A realworld « web » example PHP 5.3 in practice – Fabien Potencier
  • 58.
    In most webapplications, you need to manage the user preferences –  The user language –  Whether the user is authenticated or not –  The user credentials –  … PHP 5.3 in practice – Fabien Potencier
  • 59.
    This can bedone with a User object –  setLanguage(), getLanguage() –  setAuthenticated(), isAuthenticated() –  addCredential(), hasCredential() –  ... PHP 5.3 in practice – Fabien Potencier
  • 60.
    The User information need to be persisted between HTTP requests We use the PHP session for the Storage PHP 5.3 in practice – Fabien Potencier
  • 61.
    class SessionStorage { function __construct($cookieName = 'PHP_SESS_ID') { session_name($cookieName); session_start(); } function set($key, $value) { $_SESSION[$key] = $value; } // ... } PHP 5.3 in practice – Fabien Potencier
  • 62.
    class User { protected $storage; function __construct() Very hard to { customize $this->storage = new SessionStorage(); } function setLanguage($language) { $this->storage->set('language', $language); } // ... } Very easy to use $user = new User(); PHP 5.3 in practice – Fabien Potencier
  • 63.
    class User { protected $storage; Very easy to customize function __construct($storage) { $this->storage = $storage; } } $storage = new SessionStorage(); $user = new User($storage); Slightly more difficult to use PHP 5.3 in practice – Fabien Potencier
  • 64.
    That’s Dependency Injection Nothing more PHP 5.3 in practice – Fabien Potencier
  • 65.
    Instead of harcoding the Storage dependency inside the User class constructor Inject the Storage dependency in the User object PHP 5.3 in practice – Fabien Potencier
  • 66.
    A Dependency Injector Describes objects and their dependencies Instantiates and configures objects on-demand PHP 5.3 in practice – Fabien Potencier
  • 67.
    An injector SHOULD beable to manage ANY PHP object (POPO) The objects MUST not know that they are managed by the injector PHP 5.3 in practice – Fabien Potencier
  • 68.
    •  Parameters –  The SessionStorage implementation we want to use (the class name) –  The session name •  Objects –  SessionStorage –  User •  Dependencies –  User depends on a SessionStorage implementation PHP 5.3 in practice – Fabien Potencier
  • 69.
    Let’s build asimple injector with PHP 5.3 PHP 5.3 in practice – Fabien Potencier
  • 70.
    Dependency Injector Managing parameters PHP 5.3 in practice – Fabien Potencier
  • 71.
    class Injector { protected $parameters = array(); public function setParameter($key, $value) { $this->parameters[$key] = $value; } public function getParameter($key) { return $this->parameters[$key]; } } PHP 5.3 in practice – Fabien Potencier
  • 72.
    Decoupling $injector = newInjector(); Customization $injector->setParameter('session_name', 'SESSION_ID'); $injector->setParameter('storage_class', 'SessionStorage'); $class = $injector->getParameter('storage_class'); $sessionStorage = new $class($injector->getParameter('session_name')); $user = new User($sessionStorage); Objects creation PHP 5.3 in practice – Fabien Potencier
  • 73.
    class Injector Using PHP { magic methods protected $parameters = array(); public function __set($key, $value) { $this->parameters[$key] = $value; } public function __get($key) { return $this->parameters[$key]; } } PHP 5.3 in practice – Fabien Potencier
  • 74.
    Interface is cleaner $injector = new Injector(); $injector->session_name = 'SESSION_ID'; $injector->storage_class = 'SessionStorage'; $sessionStorage = new $injector->storage_class($injector->session_name); $user = new User($sessionStorage); PHP 5.3 in practice – Fabien Potencier
  • 75.
    Dependency Injector Managing objects PHP 5.3 in practice – Fabien Potencier
  • 76.
    We need away to describe how to create objects, without actually instantiating anything! Anonymous functions to the rescue! PHP 5.3 in practice – Fabien Potencier
  • 77.
    class Injector { protected $parameters = array(); protected $objects = array(); public function __set($key, $value) { $this->parameters[$key] = $value; } public function __get($key) Store a lambda { return $this->parameters[$key]; able to create the object on-demand } public function setService($key, Closure $service) { $this->objects[$key] = $service; } public function getService($key) Ask the closure to create { return $this->objects[$key]($this); th e object and pass the } } current injector PHP 5.3 in practice – Fabien Potencier
  • 78.
    $injector = newInjector(); $injector->session_name = 'SESSION_ID'; Description $injector->storage_class = 'SessionStorage'; $injector->setService('user', function ($c) { return new User($c->getService('storage')); }); $injector->setService('storage', function ($c) { return new $c->storage_class($c->session_name); }); Creating the User is now as easy as before $user = $injector->getService('user'); PHP 5.3 in practice – Fabien Potencier
  • 79.
    class Injector { protected $values = array(); Simplify the code function __set($id, $value) { $this->values[$id] = $value; } function __get($id) { if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } } } PHP 5.3 in practice – Fabien Potencier
  • 80.
    $injector = newInjector(); Unified interface $injector->session_name = 'SESSION_ID'; $injector->storage_class = 'SessionStorage'; $injector->user = function ($c) { return new User($c->storage); }; $injector->storage = function ($c) { return new $c->storage_class($c->session_name); }; $user = $injector->user; PHP 5.3 in practice – Fabien Potencier
  • 81.
    Dependency Injector Scope PHP 5.3 in practice – Fabien Potencier
  • 82.
    For some objects,like the user, the injector must always return the same instance PHP 5.3 in practice – Fabien Potencier
  • 83.
    spl_object_hash($injector->user) !== spl_object_hash($injector->user) PHP 5.3 in practice – Fabien Potencier
  • 84.
    $injector->user = function($c) { static $user; if (is_null($user)) { $user = new User($c->storage); } return $user; }; PHP 5.3 in practice – Fabien Potencier
  • 85.
    spl_object_hash($injector->user) === spl_object_hash($injector->user) PHP 5.3 in practice – Fabien Potencier
  • 86.
    $injector->user = $injector->asShared(function($c) { return new User($c->storage); }); PHP 5.3 in practice – Fabien Potencier
  • 87.
    function asShared(Closure $lambda) { return function ($injector) use ($lambda) { static $object; if (is_null($object)) { $object = $lambda($injector); } return $object; }; } PHP 5.3 in practice – Fabien Potencier
  • 88.
    class Injector { protected $values = array(); function __set($id, $value) { $this->values[$id] = $value; } function __get($id) { Error management if (!isset($this->values[$id])) { throw new InvalidArgumentException(sprintf('Value "%s" is not defined.', $id)); } if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } } } PHP 5.3 in practice – Fabien Potencier
  • 89.
    class injector { protected $values = array(); function __set($id, $value) { $this->values[$id] = $value; } function __get($id) { if (!isset($this->values[$id])) { throw new InvalidArgumentException(sprintf('Value "%s" is not defined.', $id)); } if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } } function asShared($callable) { 40 LOC for a fully- return function ($c) use ($callable) { static $object; if (is_null($object)) { $object = $callable($c); featured injector } return $object; }; } } PHP 5.3 in practice – Fabien Potencier
  • 90.
    I’m NOT advocating theusage of lambdas everywhere This presentation was about showing how they work on practical examples PHP 5.3 in practice – Fabien Potencier
  • 91.
    Questions? PHP5.3 in practice – Fabien Potencier
  • 92.
    Sensio S.A. 92-98, boulevard Victor Hugo 92 115 Clichy Cedex FRANCE Tél. : +33 1 40 99 80 80 Contact Fabien Potencier fabien.potencier at sensio.com http://www.sensiolabs.com/ http://www.symfony-project.org/ http://fabien.potencier.org/ PHP 5.3 in practice – Fabien Potencier