PHP 5.3 in practice

92
PHP 5.3 in practice – Fabien Potencier PHP 5.3 in practice Fabien Potencier

Transcript of PHP 5.3 in practice

Page 1: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

PHP 5.3 in practice

Fabien Potencier

Page 2: PHP 5.3 in practice

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

Page 3: PHP 5.3 in practice

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

Page 4: PHP 5.3 in practice

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/

Page 5: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Migrating to PHP 5.3 … for technical reasons

Page 6: PHP 5.3 in practice

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

Page 7: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Migrating to PHP 5.3 … for speed

Page 8: PHP 5.3 in practice

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

Page 9: PHP 5.3 in practice

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

Page 10: PHP 5.3 in practice

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%

Page 11: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Migrating to PHP 5.3 … for the ecosystem

Page 12: PHP 5.3 in practice

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

Page 13: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Namespaces and Symfony 2

Symfony\Components Symfony\Foundation Symfony\Framework

Symfony\Components\EventDispatcher\Event Symfony\Foundation\UniversalClassLoader

Page 14: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Symfony\Components\EventDispatcher\Event

vs

sfEvent

Class names are NOT shorter

Page 15: PHP 5.3 in practice

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();

Page 16: PHP 5.3 in practice

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();

Page 17: PHP 5.3 in practice

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

Page 18: PHP 5.3 in practice

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

Page 19: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Let’s dive into PHP 5.3

Page 20: PHP 5.3 in practice

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, …

Page 21: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

PHP 5.3

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

Page 22: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

PHP 5.3 … and the Singleton

Page 23: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

The Singleton may cause serious damage

to your code

Page 24: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

History of the Singleton

Page 25: PHP 5.3 in practice

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

Page 26: PHP 5.3 in practice

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()

Page 27: PHP 5.3 in practice

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() {} }

Page 28: PHP 5.3 in practice

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();

Page 29: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

PHP 5.3 …Late Static Binding

The ORM problem

Page 30: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

class Model { static public function getMe() { return __CLASS__; } }

class Article extends Model {}

echo Article::getMe();

Page 31: PHP 5.3 in practice

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

Page 32: PHP 5.3 in practice

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);

Page 33: PHP 5.3 in practice

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');

Page 34: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

PHP 5.3 …interlude

Anonymous functions Lambdas

Page 35: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

An anonymous function is a function

defined on the fly (no name)

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

Page 36: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Can be stored in a variable

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

Page 37: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

… to be used later on

$hello();

call_user_func($hello);

Page 38: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

… or can be passed as a function argument

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

foo($hello);

Page 39: PHP 5.3 in practice

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

Page 40: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Fonctions anonymes

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

foo($hello, 'Fabien');

Page 41: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

When is it useful?

Page 42: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

array_*

Greatly simplify usage of some array_* functions

array_map()

array_reduce()

array_filter()

Page 43: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

class Article { public function __construct($title) { $this->title = $title; }

public function getTitle() { return $this->title; } }

Page 44: PHP 5.3 in practice

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!'), );

Page 45: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

$titles = array(); foreach ($articles as $article) { $titles[] = $article->getTitle(); }

Page 46: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

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

Page 47: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

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

Page 48: PHP 5.3 in practice

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

Page 49: PHP 5.3 in practice

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);

Page 50: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

A closure is a lambda that remembers the context

of its creation…

Page 51: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

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

Page 52: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

$method = 'getTitle';

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

$method = 'getAuthor';

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

Page 53: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

$titleMapper = $mapper('getTitle'); $titles = array_map($titleMapper, $articles);

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

Page 54: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

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

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

Page 55: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Dependency Injector

Page 56: PHP 5.3 in practice

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

Page 57: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Dependency Injection

A real world « web » example

Page 58: PHP 5.3 in practice

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 – …

Page 59: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

This can be done with a User object

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

Page 60: PHP 5.3 in practice

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

Page 61: PHP 5.3 in practice

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; }

// ... }

Page 62: PHP 5.3 in practice

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

Page 63: PHP 5.3 in practice

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

Page 64: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

That’s Dependency Injection

Nothing more

Page 65: PHP 5.3 in practice

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

Page 66: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

A Dependency Injector

Describes objects and their dependencies

Instantiates and configures objects on-demand

Page 67: PHP 5.3 in practice

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

Page 68: PHP 5.3 in practice

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

Page 69: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Let’s build a simple injector with PHP 5.3

Page 70: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Dependency Injector

Managing parameters

Page 71: PHP 5.3 in practice

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]; } }

Page 72: PHP 5.3 in practice

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

Page 73: PHP 5.3 in practice

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

Page 74: PHP 5.3 in practice

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

Page 75: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Dependency Injector

Managing objects

Page 76: PHP 5.3 in practice

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!

Page 77: PHP 5.3 in practice

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

Page 78: PHP 5.3 in practice

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

Page 79: PHP 5.3 in practice

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

Page 80: PHP 5.3 in practice

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

Page 81: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Dependency Injector

Scope

Page 82: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

For some objects, like the user, the injector must always return the same instance

Page 83: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

spl_object_hash($injector->user)

!== spl_object_hash($injector->user)

Page 84: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

$injector->user = function ($c) { static $user;

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

return $user; };

Page 85: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

spl_object_hash($injector->user)

=== spl_object_hash($injector->user)

Page 86: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

$injector->user = $injector->asShared(function ($c) { return new User($c->storage); });

Page 87: PHP 5.3 in practice

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; }; }

Page 88: PHP 5.3 in practice

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

Page 89: PHP 5.3 in practice

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

Page 90: PHP 5.3 in practice

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

Page 91: PHP 5.3 in practice

PHP 5.3 in practice – Fabien Potencier

Questions?

Page 92: PHP 5.3 in practice

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/