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