Post on 18-Jan-2015
description
<?php
class CarrinhoCompras{ protected $produtos; public function __construct() { $this->produtos = array(); } public function addProduto($produto) { $this->produtos[] = $produto; }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());$obj2->addProduto(new StdClass());
echo $obj1 + $obj2;
<?php
class CarrinhoCompras{ protected $produtos; public function __construct() { $this->produtos = array(); } public function addProduto($produto) { $this->produtos[] = $produto; }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());$obj2->addProduto(new StdClass());
echo $obj1 + $obj2;
// Notice: Object of class CarrinhoCompras could not be converted to int...
<?php
class CarrinhoCompras{ protected $produtos; public function __construct() { $this->produtos = array(); } public function addProduto($produto) { $this->produtos[] = $produto; } public function count() { return count($this->produtos); }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());$obj2->addProduto(new StdClass());
echo $obj1->count() + $obj2->count();
<?php
class CarrinhoCompras implements Countable{ protected $produtos; public function __construct() { $this->produtos = array(); } public function addProduto($produto) { $this->produtos[] = $produto; } public function count() { return count($this->produtos); }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());$obj2->addProduto(new StdClass());
echo count($obj1) + count($obj2);
<?php
$d1 = new DateTime();$d2 = new DateTime('1991-10-21');
var_dump($d1 > $d2);
Porque isso funciona?
<?php
class CarrinhoCompras{ protected $produtos; public function __construct() { $this->produtos = array(); } public function addProduto($produto) { $this->produtos[] = $produto; }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());$obj2->addProduto(new StdClass());$obj2->addProduto(new StdClass());
var_dump($obj1 > $obj2);
<?php
class CarrinhoCompras{ protected $produtos;
public function __construct() { $this->produtos = array(); }
public function addProduto($produto) { $this->produtos[] = $produto; }
public function __add(CarrinhoCompras $carrinho = null) { return count($this->produtos) + ($carrinho ? $carrinho->__add() : 0); }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());$obj2->addProduto(new StdClass());
echo $obj1 + $obj2;
<?php
interface Summable{ public function __add(Summable $value = null);}
class CarrinhoCompras implements Summable{ protected $produtos;
public function __construct() { $this->produtos = array(); }
public function addProduto($produto) { $this->produtos[] = $produto; }
public function __add(Summable $value = null) { return count($this->produtos) + ($value ? $value->__add() : 0); }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());$obj2->addProduto(new StdClass());
echo $obj1 + $obj2;
<?php
class CarrinhoCompras{ protected $produtos;
public function __construct() { $this->produtos = array(); }
public function addProduto($produto) { $this->produtos[] = $produto; }
public function __add(CarrinhoCompras $carrinho = null) { return count($this->produtos) + ($carrinho ? $carrinho->__add() : 0); }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());
$obj3 = new CarrinhoCompras();$obj3->addProduto(new StdClass());
var_dump($obj1 + $obj2 + $obj3);
// ???
<?php
class CarrinhoCompras{ protected $produtos;
public function __construct() { $this->produtos = array(); }
public function addProduto($produto) { $this->produtos[] = $produto; }
public function __add(CarrinhoCompras $carrinho = null) { return count($this->produtos) + ($carrinho ? $carrinho->__add() : 0); }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());
$obj3 = new CarrinhoCompras();$obj3->addProduto(new StdClass());
var_dump($obj1 + $obj2 + $obj3);
// Notice: Object of class CarrinhoCompras could not be converted to int...
<?php
class CarrinhoCompras{ protected $produtos;
public function __construct() { $this->produtos = array(); }
public function addProduto($produto) { $this->produtos[] = $produto; }
public function __add($value = null) { if (is_int($value)) return count($this->produtos) + $value;
return count($this->produtos) + ($value ? $value->__add() : 0); }}
$obj1 = new CarrinhoCompras();$obj1->addProduto(new StdClass());
$obj2 = new CarrinhoCompras();$obj2->addProduto(new StdClass());
$obj3 = new CarrinhoCompras();$obj3->addProduto(new StdClass());
var_dump($obj1 + ($obj2 + $obj3));
<?php
class ContaCorrente // Entity{ public function depositar(Dinheiro $valor) { $this->setSaldo($this->getSaldo() + $valor); }}
class Dinheiro // ValueObject{ const BRL = 1; const AUD = 2; protected $tipoMoeda; public function getTipoMoeda() { ... } public function converte($tipoMoeda) { return $this->getValor() * $tipoMoeda; } public function __add($dinheiro) { return $this->getValor() + $dinheiro->convert($this->getTipoMoeda()); }}
$conta = new ContaCorrente(/* id */);$valor = new Dinheiro(100);$conta->depositar($valor);
Operadores disponíveis
+, -, *, /, %, <<, >>, ., |, &, ^, ~, !, ++, --, +=, -=, *=,/=, %=, <<=, >>=, .=, |=, &=, ^=, ~=, ==, !=, ===, !==, <, <=
Injeção de Dependência<?php
// Zend Framework: A setter injection example$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array( 'auth' => 'login', 'username' => 'foo', 'password' => 'bar', 'ssl' => 'ssl', 'port' => 465,)); $mailer = new Zend_Mail();$mailer->setDefaultTransport($transport);
Symfony<?xml version="1.0" ?> <container xmlns="http://symfony-project.org/2.0/container"> <parameters> <parameter key="mailer.username">foo</parameter> <parameter key="mailer.password">bar</parameter> <parameter key="mailer.class">Zend_Mail</parameter> </parameters> <services> <service id="mail.transport" class="Zend_Mail_Transport_Smtp" shared="false"> <argument>smtp.gmail.com</argument> <argument type="collection"> <argument key="auth">login</argument> <argument key="username">%mailer.username%</argument> <argument key="password">%mailer.password%</argument> <argument key="ssl">ssl</argument> <argument key="port">465</argument> </argument> </service> <service id="mailer" class="%mailer.class%"> <call method="setDefaultTransport"> <argument type="service" id="mail.transport" /> </call> </service> </services></container>
Symfony
<?phprequire_once '/PATH/TO/sfServiceContainerAutoloader.php';sfServiceContainerAutoloader::register(); $sc = new sfServiceContainerBuilder(); $loader = new sfServiceContainerLoaderFileXml($sc);$loader->load('/somewhere/container.xml');$sc->mailer;
<?php
class UserService{ protected $serviceLocator; public function __construct($serviceLocator) { $this->serviceLocator = $serviceLocator; } public function saveUser(array $data) { $validator = $this->serviceLocator->getService('validator'); try { // Valida os dados $data = $validator->validate($data);
$repository = new UserRepository(); $user = new User(); // Persiste os dados return $repository->save($data, $user); } catch (Exception $e) { $this->serviceLocator->getService('logger')->log($e); } return null; }}
<?php
class UserService{ /** * @Dependency(validator) */ protected $validator; /** * @Dependency(logger) */ protected $logger; public function saveUser(array $data) { try { // Valida os dados $data = $this->validator->validate($data);
$repository = new UserRepository(); $user = new User(); // Persiste os dados return $repository->save($data, $user); } catch (Exception $e) { $this->logger->log($e); } return null; }}
runkithttps://github.com/zenovich/runkit/
<?php
class Container{ protected static $data = array('mailer' => 'mailer'); public static function get($key) { if (!self::exists($key)) throw new InvalidArgumentException('Invalid key!'); return self::$data[$key]; } public static function exists($key) { return array_key_exists($key, self::$data); }
public static function notify($object) { $injector = new Injector($object, self::$data); return $injector->inject(); }}
<?php
class Watcher{ protected $dir; public function __construct($dir) { $this->dir = $dir; } public function watch() { foreach (glob($this->dir . DIRECTORY_SEPARATOR . '*.php') as $class) { require $class; runkit_method_add(pathinfo($class, PATHINFO_FILENAME),
'__construct', '', 'Container::notify($this);');
} }}
<?php
class Injector{ protected $object; public function __construct($object) { $this->object = $object; } public function inject() { $reflection = new ReflectionObject($this->object); $prop = $reflection->getProperty('mailer'); if (!Container::exists('mailer')) return null; $prop->setAccessible(true); $prop->setValue($this->object, Container::get('mailer')); return true; }}
<?php
$w = new Watcher('path/to/my/folder');$w->watch();
$c = new Controller();$c->get();// string(6) "mailer"
<?php
class UserService{ /** * @Dependency(validator) */ protected $validator; /** * @Dependency(logger) */ protected $logger; public function saveUser(array $data) { try { // Valida os dados $data = $this->validator->validate($data);
$repository = new UserRepository(); $user = new User(); // Persiste os dados return $repository->save($data, $user); } catch (Exception $e) { $this->logger->log($e); } return null; }}
<?php
class AnnotationParser{ protected $class; public function __construct(ReflectionClass $class) { $this->class = $class; } public function parseDependencies() { $dependencies = array(); foreach ($this->class->getProperties() as $prop) if ($this->matchDependency($prop, $matches)) $dependencies[] = array('property' => $prop, 'dependency' => $matches[1]); return $dependencies; } protected function matchDependency($prop, &$matches) { return (bool) preg_match('/@dependency\s*\(([a-zA-Z0-9_ ]*)\)/i',
$prop->getDocComment(), $matches); }}
<?php
class Injector{ protected $object; public function __construct($object) { $this->object = $object; } public function inject() { $annotationParser = new AnnotationParser(new ReflectionObject($this->object)); $props = $annotationParser->parseDependencies();
foreach ($props as $prop) { if (Container::exists($prop['dependency'])) { $prop['property']->setAccessible(true); $prop['property']->setValue($this->object,
Container::get($prop['dependency'])); }
} }}
<?php
class Container{ protected static $data = array(); public static function set($key, $value) { self::$data[$key] = $value; }
public static function get($key) { if (!self::exists($key)) throw new InvalidArgumentException('Invalid key!'); return self::$data[$key]; } public static function exists($key) { return array_key_exists($key, self::$data); }
public static function notify($object) { $injector = new Injector($object, self::$data); return $injector->inject(); }}
<?php
class Validator{ public function validate() { // ... }}
class Logger{ public function log() { // ... }}
$w = new Watcher('path/to/my/folder');$w->watch();
Container::set('validator', new Validator());Container::set('logger', new Logger());
$c = new UserService();$c->saveUser(array());
Melhorias
• Verificar existência de __construct
• Pegar os parametros do __construct
• Observar classes dentro de namespaces(recursivo)
Recursos
• function_add, *_remove, *_copy, *_redefine, *_rename
• method_add, *_remove, *_copy, *_redefine, *_rename
E agora???
• Tokenizer
• Mutagenesis (https://github.com/padraic/mutagenesis)
<?php
$id = $_GET['id']; // int$valor = $_GET['valor']; // float
function processa($id, $valor){ // ...}
<?php
$id = $_GET['id']; // int$valor = $_GET['valor']; // float
function processa($id, $valor){ $id = (int) $id; $valor = (float) $valor; echo $id, $valor;}
processa($id, $valor);
<?php
$id = $_GET['id']; // int$valor = $_GET['valor']; // float
function processa($id, $valor){ if (!is_int($id)) throw new InvalidArgumentException('Tipo inválido'); if (!is_float($valor)) throw new InvalidArgumentException('Tipo inválido'); echo $id, $valor;}
processa($id, $valor);
<?php
$id = $_GET['id']; // int$valor = $_GET['valor']; // float
function processa(Integer $id, Float $valor){ echo $id, $valor;}
processa(new Integer($id), new Float($valor));
<?php
$id = $_GET['id']; // int$valor = $_GET['valor']; // float
function processa(SplInt $id, SplFloat $valor){ echo $id, $valor;}
processa(new SplInt($id), new SplFloat($valor));
<?php
$int = new SplInt('10');
if (!is_int('10')) throw new InvalidArgumentException('Tipo inválido');
$int1 = new SplInt('10', false);$int2 = '10';
if (!is_int($int2)) $int2 = (int) $int2;
<?php
$int = new SplInt(10);$float = new SplFloat(10.7);
echo $float + $int;echo $float - $int;echo $float / $int;echo $float * $int;
<?php
$id = $_GET['id']; // int$valor = $_GET['valor']; // float
function processa(SplInt $id, SplFloat $valor){ echo $id, $valor;}
processa($id, $valor);// Catchable fatal error: Argument 1 passed to processa() must be an instance of SplInt// Catchable fatal error: Argument 2 passed to processa() must be an instance of SplFloat
<?php
class Month extends SplEnum{ const __default = self::January; const January = 1; const February = 2; const March = 3; const April = 4; const May = 5; const June = 6; const July = 7; const August = 8; const September = 9; const October = 10; const November = 11; const December = 12;}
<?php
class Month extends SplEnum{ const __default = self::January; const January = 1; const February = 2; const March = 3; const April = 4; const May = 5; const June = 6; const July = 7; const August = 8; const September = 9; const October = 10; const November = 11; const December = 12;}
function getMonth(Month $month){ echo $month;}
getMonth(new Month(Month::October));
<?php
class Month extends SplEnum{ const __default = self::January; const January = 1; const February = 2; const March = 3; const April = 4; const May = 5; const June = 6; const July = 7; const August = 8; const September = 9; const October = 10; const November = 11; const December = 12;}
function getMonth(Month $month){ echo $month;}
getMonth(new Month(13));// UnexpectedValueException: Value not a const in enum Month
<?php
class Month extends SplEnum{ const __default = self::January; const January = 1; const February = 2; const March = 3; const April = 4; const May = 5; const June = 6; const July = 7; const August = 8; const September = 9; const October = 10; const November = 11; const December = 12;}
$month = new Month();var_dump($month->getConstList());
http://svn.php.net/viewvc?view=revision&revision=299534
https://svn.php.net/repository/php/php-src/branches/WITH_SCALAR_TYPES/
http://ilia.ws/archives/207-Type-Hinting-Conclusion.html
<?php
function testInt(integer $value){ var_dump($value);}
testInt('PHPubSP');// Catchable fatal error: Argument 1 passed to testInt() must be of the type integer, string given
<?php
function testBool(bool $value){ // ...}
function testString(string $value){ // ...}
function testFloat(float $value){ // ...}