Download - PHP ao Extremo

Transcript

PHP ao Extremo

Quem sou eu???

• github.com/thiagophx

• @thiagophx

• thiagorigo.com

• phpml.org

Agenda

• pecl/operator

• pecl/runkit *

• SplTypes

• php5.3.99-dev

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

Sobrecarga de operador

sudo pecl install -f operator

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

Exemplo do mundo real

<?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

+, -, *, /, %, <<, >>, ., |, &, ^, ~, !, ++, --, +=, -=, *=,/=, %=, <<=, >>=, .=, |=, &=, ^=, ~=, ==, !=, ===, !==, <, <=

• Só funciona no 5.2

Nada é perfeito...

Injeção de Dependência

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

Container de Injeção de Dependência

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;

Inversão de Controle

Você não chama, você é chamado!

<?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

class Controller{ protected $mailer; public function get() { var_dump($this->mailer); }}

<?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

Variáveis Tipadas

PHP tem tipo???

<?php

$id = $_GET['id']; // int$valor = $_GET['valor']; // float

<?php

$id = (int) $_GET['id']; // int$valor = (float) $_GET['valor']; // float

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

sudo pecl install spl_types

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

<?php

$int = new SplInt('10');// UnexpectedValueException: Value not an integer

<?php

$int = new SplInt('10', false);// Ok

<?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);$int = 10;

$int1 = new SplInt('10', false);$int1 = '10';

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

Scalar Type Hinting

<?php

function foo(array $value){ // ...}

<?php

function testInt(integer $value){ var_dump($value);}

testInt(1);

<?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 testNumeric(numeric $value){ var_dump($value);}

testNumeric('10');

<?php

function testCast((int) $value){ var_dump($value);}

testCast('10');

<?php

function testScalar(scalar $value){ var_dump($value);}

testScalar('10');

<?php

function testBool(bool $value){ // ...}

function testString(string $value){ // ...}

function testFloat(float $value){ // ...}

Perguntas???


Top Related