Apresentação feita no Latinoware 2010 apresentando o conceito de Dependency Injection no PHP 5.3.
Esta palestra é uma tradução e simplificação da palestra Dependency Injection in PHP 5.3 de Fabien Potencier.
2. 2
Guilherme Blanco
● 10+ anos como desenvolvedor
● Evangelista Open Source
● Trabalha para Yahoo!
● Contribui para Doctrine, Zend Framework, Symfony e
PHP
● No Twitter: @guilhermeblanco
● No GitHub: http://github.com/guilhermeblanco
3. 3
Dependency Injection no PHP 5.3
Um exemplo real na web
Esta palestra é uma tradução de: Dependency Injection in PHP 5.3
Concedida pelo autor Fabien Potencier
4. 4
Dependency Injection no PHP 5.3
● Na maioria das aplicações web, você precisa
gerenciar as preferências do usuário:
● A linguagem do usuário
● Se o usuário está autenticado ou não
● As credenciais do usuário
● ...
5. 5
Dependency Injection no PHP 5.3
● Isto pode ser feito no objeto User (Usuário):
● setLanguage(), getLanguage()
● setAuthenticated(), getAuthenticated()
● addCredential(), hasCredential()
● ...
6. 6
Dependency Injection no PHP 5.3
● As informações do Usuário precisam ser persistidas
entre requisições HTTP
● Nós usamos sessões em PHP para Storage
(Armazenagem)
7. 7
Dependency Injection no PHP 5.3
<?php
class SessionStorage
{
function __construct($cookieName = 'PHP_SESS_ID')
{
session_name($cookieName);
session_start();
}
function set($key, $value)
{
$_SESSION[$key] = $value;
}
// ...
}
8. 8
Dependency Injection no PHP 5.3
Muito difícil
de
customizar
Muito fácil
de
usar
<?php
class User
{
protected $storage;
function __construct()
{
$this>storage = new SessionStorage();
}
function setLanguage($language)
{
$this>storage>set('language', $language);
}
// ...
}
$user = new User();
9. 9
Dependency Injection no PHP 5.3
Muito fácil
de
customizar
Um pouco
mais difícil
de usar
<?php
class User
{
protected $storage;
function __construct($storage)
{
$this>storage = $storage;
}
}
$storage = new SessionStorage();
$user = new User($storage);
10. 10
Dependency Injection no PHP 5.3
● Isto é Dependency Injection (Injeção de Dependência)
● Nada além disso...
11. 11
Dependency Injection no PHP 5.3
● Vamos entender porque o primeiro exemplo não é
uma boa idéia...
● Quero mudar o nome do cookie da sessão do usuário
12. 12
Dependency Injection no PHP 5.3
Hardcode
na classe
do Usuário
<?php
class User
{
protected $storage;
function __construct()
{
$this>storage = new SessionStorage('SESSION_ID');
}
function setLanguage($language)
{
$this>storage>set('language', $language);
}
// ...
}
$user = new User();
13. 13
Dependency Injection no PHP 5.3
Adicionar
uma variável
global?
<?php
class User
{
protected $storage;
function __construct()
{
$this>storage = new SessionStorage(STORAGE_SESSION_NAME);
}
}
define('STORAGE_SESSION_NAME', 'SESSION_ID');
$user = new User();
14. 14
Dependency Injection no PHP 5.3
Configurar
via
Usuário?
<?php
class User
{
protected $storage;
function __construct($sessionName)
{
$this>storage = new SessionStorage($sessionName);
}
}
$user = new User('SESSION_ID');
15. 15
Dependency Injection no PHP 5.3
Configurar
com um
Array?
<?php
class User
{
protected $storage;
function __construct($storageOptions)
{
$this>storage = new SessionStorage($storageOptions['session_name']);
}
}
$user = new User(array('session_name' => 'SESSION_ID'));
16. 16
Dependency Injection no PHP 5.3
● Eu quero mudar a implementação de armazenamento
da sessão
● Sistema de Arquivos
● MySQL
● Memcached
● etc
17. 17
Usar uma
instância no
Registro global?
<?php
class User
{
protected $storage;
function __construct()
{
$this>storage = Registry::get('session_storage');
}
}
$storage = new SessionStorage();
Registry::set('session_storage', $storage);
$user = new User();
Dependency Injection no PHP 5.3
18. 18
Agora, o Usuário (User) depende do Registro
Dependency Injection no PHP 5.3
19. 19
Dependency Injection no PHP 5.3
● Ao invés da dependência do Storage estar
“hardcoded” no construtor da classe Usuário
● Injete a dependência de Storage no objeto Usuário
20. 20
Dependency Injection no PHP 5.3
<?php
class User
{
protected $storage;
function __construct($storage)
{
$this>storage = $storage;
}
}
$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
23. 23
Uso de uma
estratégia diferente
de Storage
Dependency Injection no PHP 5.3
<?php
class User
{
protected $storage;
function __construct($storage)
{
$this>storage = $storage;
}
}
$storage = new MySQLSessionStorage('SESSION_ID');
$user = new User($storage);
27. 27
Adiciona
uma interface
Dependency Injection no PHP 5.3
<?php
class User
{
protected $storage;
function __construct(SessionStorageInterface $storage)
{
$this>storage = $storage;
}
}
interface SessionStorageInterface
{
function get($key);
function set($key, $value);
}
29. 29
Dependency Injection no PHP 5.3
Mock de uma
Session
class User
{
protected $storage;
function __construct(SessionStorageInterface $storage)
{
$this>storage = $storage;
}
}
class SessionStorageForTests implements SessionStorageInterface
{
protected $data = array();
public function set($key, $value)
{
$this>data[$key] = $value;
}
public function get($key)
{
return $this>data[$key];
}
}
30. 30
Dependency Injection no PHP 5.3
● Uso de diferentes estratégias de Storage
● Configuração fica natural
● Engloba classes de terceiros (Adapter/Interface)
● Mock do objeto Storage (para testes)
● Fácil sem alterar a classe Usuário (User)
31. 31
Dependency Injection no PHP 5.3
“Dependency Injection é onde os objetos obtém
suas dependências através do construtor,
métodos ou diretamente nas propriedades”
http://www.picocontainer.org/injection.html
32. 32
Dependency Injection no PHP 5.3
<?php
$storage = new SessionStorage();
// constructor injection
$user = new User($storage);
// setter injection
$user = new User();
$user>setStorage($storage);
// property injection
$user = new User();
$user>storage = $storage;
34. 34
Dependency Injection no PHP 5.3
<?php
$request = new Request();
$response = new Response();
$storage = new FileSessionStorage('SESSION_ID');
$user = new User($storage);
$cache = new FileCache(
array('dir' => dirname(__FILE__) . '/cache')
);
$routing = new Routing($cache);
35. 35
Dependency Injection no PHP 5.3
<?php
class Application
{
function __construct()
{
$this>request = new WebRequest();
$this>response = new WebResponse();
$storage = new FileSessionStorage('SESSION_ID');
$this>user = new User($storage);
$cache = new FileCache(
array('dir' => dirname(__FILE__).'/cache')
);
$this>routing = new Routing($cache);
}
}
$application = new Application();
37. 37
Dependency Injection no PHP 5.3
<?php
class Application
{
function __construct()
{
$this>request = new WebRequest();
$this>response = new WebResponse();
$storage = new FileSessionStorage('SESSION_ID');
$this>user = new User($storage);
$cache = new FileCache(
array('dir' => dirname(__FILE__).'/cache')
);
$this>routing = new Routing($cache);
}
}
$application = new Application();
38. 38
Dependency Injection no PHP 5.3
● Nós precisamos de um Container
● Descreve objetos e suas dependências
● Instancia e configura objetos sob demanda
39. 39
Dependency Injection no PHP 5.3
● Um Container DEVE ser capaz de gerenciar
QUALQUER objeto PHP (POPO)
● Os objetos NÃO DEVEM saber que são gerenciados
por um Container
40. 40
Dependency Injection no PHP 5.3
● Parâmetros
● A implementação de SessionStorageInterface que
queremos usar (o nome da classe)
● O nome da sessão
● Objetos
● SessionStorage
● User
● Dependências
● Usuário depende de uma implementação de
SessionStorageInterface
45. 45
Dependency Injection no PHP 5.3
<?php
class Container
{
protected $parameters = array();
public function __set($key, $value)
{
$this>parameters[$key] = $value;
}
public function __get($key)
{
return $this>parameters[$key];
}
}
46. 46
Dependency Injection no PHP 5.3
<?php
$container = new Container();
$container>session_name = 'SESSION_ID';
$container>storage_class = 'SessionStorage';
$sessionStorage = new $container>storage_class($container>session_name);
$user = new User($sessionStorage);
Interface é
mais limpa
48. 48
Dependency Injection no PHP 5.3
● Precisamos de uma forma para descrever como criar
objetos, sem na realidade instanciar qualquer coisa!
● Funções anônimas existem para nos salvar!
49. 49
Funções anônimas / Lambdas
● Função anônima é uma função definida diretamente e
sem um nome
<?php
function () { echo 'Hello World'; };
50. 50
Funções anônimas / Lambdas
● Uma lambda pode ser armazenado em uma variável
<?php
$hello = function () { echo 'Hello World'; };
51. 51
Funções anônimas / Lambdas
● E depois podem ser usadas como qualquer
executável PHP
<?php
$hello();
call_user_func($hello);
52. 52
Funções anônimas / Lambdas
● Você também pode passar uma lambda como um
argumento de uma função ou método
<?php
function foo(Closure $func)
{
$func();
}
foo($hello);
55. 55
Armazene um lambda
capaz de criar objetos
sob demanda
Dependency Injection no PHP 5.3
Notifique a closure
para criar um objeto
passando um Container
class Container
{
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);
}
}
56. 56
Dependency Injection no PHP 5.3
<?php
$container = new Container();
$container>session_name = 'SESSION_ID';
$container>storage_class = 'SessionStorage';
$container>setService('user', function ($c)
{
return new User($c>getService('storage'));
});
$container>setService('storage', function ($c)
{
return new $c>storage_class($c>session_name);
});
<?php
$user = $container>getService('user');
61. 61
Dependency Injection no PHP 5.3
● Para alguns tipos de objetos, como o Usuário, o
Container sempre deve retornar a mesma instância
<?php
spl_object_hash($container>user) !== spl_object_hash($container>user);
62. 62
Dependency Injection no PHP 5.3
<?php
$container>user = function ($c)
{
static $user;
if (is_null($user))
{
$user = new User($c>storage);
}
return $user;
};
66. 66
Dependency Injection no PHP 5.3
<?php
class Article
{
public function __construct($title)
{
$this>title = $title;
}
public function getTitle()
{
return $this>title;
}
}
$articles = array(
new Article('Title 1'),
new Article('Title 2'),
);
67. 67
Dependency Injection no PHP 5.3
<?php
$mapper = function ($article)
{
return $article>getTitle();
};
$titles = array_map($mapper, $articles);
71. 71
Dependency Injection no PHP 5.3
<?php
$container>user = $container>asShared(function ($c)
{
return new User($c>storage);
});
72. 72
Dependency Injection no PHP 5.3
<?php
function asShared(Closure $lambda)
{
return function ($container) use ($lambda)
{
static $object;
if (is_null($object))
{
$object = $lambda($container);
}
return $object;
};
}
73. 73
Dependency Injection no PHP 5.3
Gerenciamento
de erros
class Container
{
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;
};
}
}
74. 74
Dependency Injection no PHP 5.3
NÃO estou afirmando para usarem lambdas em
todos os lugares
Esta apresentação tem o intuito de mostrar como
elas funcionam em exemplos práticos
83. 83
Dependency Injection no PHP 5.3
● Symfony Components DependencyInjection
● Usado no core do Symfony 2 (uma das mais rápidas
frameworks)
● Flexível (YAML, XML, INI and PHP)