php 5.3 in practice
TRANSCRIPT
PHP 5.3 in practice – Fabien Potencier
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
symfony project running on PHP 5.2 vs PHP 5.3 profiled with XHPROF (run 4aeeb7d54b732 is PHP 5.3)
-47%
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 – Zend Framework 2.0
• Better interoperability between these libraries, thanks to namespaces
Late 2010
PHP 5.3 in practice – Fabien Potencier
Namespaces and Symfony 2
Symfony\Components Symfony\Foundation Symfony\Framework
Symfony\Components\EventDispatcher\Event Symfony\Foundation\UniversalClassLoader
PHP 5.3 in practice – Fabien Potencier
Symfony\Components\EventDispatcher\Event
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 Symfony\Foundation\ClassLoader; use Symfony\Components\EventDispatcher\Event;
$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 Symfony\Foundation\ClassLoader; use Symfony\Components\EventDispatcher\Event;
$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; } }
$obj =& Singleton::getInstance();
You can still
instantiate the class 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; }
final private function __clone() {} }
$obj = Singleton::getInstance();
do not forget to
override __clone()
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(); } }
class Article extends Model {}
echo Article::getMe();
as of PHP 5.3
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
$hello = function ($name) { echo 'Hello '.$name; };
$hello('Fabien');
call_user_func($hello, 'Fabien');
Can take arguments
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);
1800 300
$titles = array_map(function ($article) { return $article->getTitle(); }, $articles);
200 100
memory speed
$mapper = function ($article) { return $article->getTitle(); }; $titles = array_map($mapper, $articles);
180 100
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() { $this->storage = new SessionStorage(); }
function setLanguage($language) { $this->storage->set('language', $language); }
// ... }
$user = new User();
Very easy to use
Very hard to
customize
PHP 5.3 in practice – Fabien Potencier
class User { protected $storage;
function __construct($storage) { $this->storage = $storage; } }
$storage = new SessionStorage(); $user = new User($storage);
Slightly more
difficult to use
Very easy to
customize
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
$injector = new Injector(); $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);
Decoupling
Customization
Objects creation
PHP 5.3 in practice – Fabien Potencier
class Injector { protected $parameters = array();
public function __set($key, $value) { $this->parameters[$key] = $value; }
public function __get($key) { return $this->parameters[$key]; } }
Using PHP
magic methods
PHP 5.3 in practice – Fabien Potencier
$injector = new Injector(); $injector->session_name = 'SESSION_ID'; $injector->storage_class = 'SessionStorage';
$sessionStorage = new $injector->storage_class($injector->session_name); $user = new User($sessionStorage);
Interface
is cleaner
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) { return $this->parameters[$key]; }
public function setService($key, Closure $service) { $this->objects[$key] = $service; }
public function getService($key) { return $this->objects[$key]($this); } }
Store a lambda
able to create the
object on-demand
Ask the closure to create
the object and pass the
current injector
PHP 5.3 in practice – Fabien Potencier
$injector = new Injector(); $injector->session_name = 'SESSION_ID'; $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); });
$user = $injector->getService('user');
Creating the User
is now as easy as before
Description
PHP 5.3 in practice – Fabien Potencier
class Injector { protected $values = array();
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]; } } }
Simplify the code
PHP 5.3 in practice – Fabien Potencier
$injector = new Injector(); $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;
Unified interface
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) { 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]; } } }
Error management
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) { return function ($c) use ($callable) { static $object;
if (is_null($object)) { $object = $callable($c); } return $object; }; } }
40 LOC for a fully-
featured injector
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/