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);
Porque isso funciona?

     <?php

     $d1 = new DateTime();
     $d2 = new DateTime('1991-10-21');

     var_dump($d1 > $d2);
<?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


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



• Só funciona no 5.2
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

<?php
require_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;
    }
}
runkit
https://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('/@dependencys*(([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)
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);
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 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???

PHP ao Extremo

  • 1.
  • 2.
    Quem sou eu??? •github.com/thiagophx • @thiagophx • thiagorigo.com • phpml.org
  • 3.
    Agenda • pecl/operator • pecl/runkit* • SplTypes • php5.3.99-dev
  • 4.
    <?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;
  • 5.
    <?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...
  • 6.
    <?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();
  • 7.
    <?php class CarrinhoCompras implementsCountable { 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);
  • 8.
    Porque isso funciona? <?php $d1 = new DateTime(); $d2 = new DateTime('1991-10-21'); var_dump($d1 > $d2);
  • 9.
    <?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);
  • 10.
  • 11.
    sudo pecl install-f operator
  • 12.
    <?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;
  • 13.
    <?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;
  • 14.
    <?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); // ???
  • 15.
    <?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...
  • 16.
    <?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));
  • 17.
  • 18.
    <?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);
  • 19.
    Operadores disponíveis +, -, *, /, %, <<, >>, ., |, &, ^, ~, !, ++, --, +=, -=, *=, /=, %=, <<=, >>=, .=, |=, &=, ^=, ~=, ==, !=, ===, !==, <, <=
  • 20.
    Nada é perfeito... •Só funciona no 5.2
  • 21.
  • 22.
    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);
  • 23.
    Container de Injeçãode Dependência
  • 24.
    Symfony <?xml version="1.0" ?>   <containerxmlns="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>
  • 25.
    Symfony <?php require_once '/PATH/TO/sfServiceContainerAutoloader.php'; sfServiceContainerAutoloader::register();   $sc =new sfServiceContainerBuilder();   $loader = new sfServiceContainerLoaderFileXml($sc); $loader->load('/somewhere/container.xml'); $sc->mailer;
  • 26.
  • 27.
    Você não chama,você é chamado!
  • 28.
    <?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; } }
  • 29.
    <?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; } }
  • 30.
  • 31.
    <?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(); } }
  • 32.
    <?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);'); } } }
  • 33.
    <?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; } }
  • 34.
    <?php class Controller { protected $mailer; public function get() { var_dump($this->mailer); } }
  • 35.
    <?php $w = newWatcher('path/to/my/folder'); $w->watch(); $c = new Controller(); $c->get(); // string(6) "mailer"
  • 36.
    <?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; } }
  • 37.
    <?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('/@dependencys*(([a-zA-Z0-9_ ]*))/i', $prop->getDocComment(), $matches); } }
  • 38.
    <?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'])); } } } }
  • 39.
    <?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(); } }
  • 40.
    <?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());
  • 41.
    Melhorias • Verificar existênciade __construct • Pegar os parametros do __construct • Observar classes dentro de namespaces(recursivo)
  • 42.
    Recursos • function_add, *_remove,*_copy, *_redefine, *_rename • method_add, *_remove, *_copy, *_redefine, *_rename
  • 43.
    E agora??? • Tokenizer •Mutagenesis (https://github.com/ padraic/mutagenesis)
  • 44.
  • 45.
  • 46.
    <?php $id = $_GET['id'];// int $valor = $_GET['valor']; // float
  • 47.
    <?php $id = (int)$_GET['id']; // int $valor = (float) $_GET['valor']; // float
  • 48.
    <?php $id = $_GET['id'];// int $valor = $_GET['valor']; // float function processa($id, $valor) { // ... }
  • 49.
    <?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);
  • 50.
    <?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);
  • 51.
    <?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));
  • 52.
  • 53.
    <?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));
  • 54.
    <?php $int = newSplInt('10'); // ?
  • 55.
    <?php $int = newSplInt('10'); // UnexpectedValueException: Value not an integer
  • 56.
    <?php $int = newSplInt('10', false); // Ok
  • 57.
    <?php $int = newSplInt('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;
  • 58.
    <?php $int = newSplInt(10); $int = 10; $int1 = new SplInt('10', false); $int1 = '10';
  • 59.
    <?php $int = newSplInt(10); $float = new SplFloat(10.7); echo $float + $int; echo $float - $int; echo $float / $int; echo $float * $int;
  • 60.
    <?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
  • 61.
    <?php class Month extendsSplEnum { 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; }
  • 62.
    <?php class Month extendsSplEnum { 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));
  • 63.
    <?php class Month extendsSplEnum { 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
  • 64.
    <?php class Month extendsSplEnum { 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());
  • 65.
  • 66.
  • 67.
    <?php function testInt(integer $value) { var_dump($value); } testInt(1);
  • 68.
  • 69.
  • 70.
  • 71.
    <?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
  • 72.
    <?php function testNumeric(numeric $value) { var_dump($value); } testNumeric('10');
  • 73.
    <?php function testCast((int) $value) { var_dump($value); } testCast('10');
  • 74.
    <?php function testScalar(scalar $value) { var_dump($value); } testScalar('10');
  • 75.
    <?php function testBool(bool $value) { // ... } function testString(string $value) { // ... } function testFloat(float $value) { // ... }
  • 76.