Diogo Besson :: tecnologia

PHP Traits

PHP Traits é uma forma de burlar a limitação da herança simples no PHP. Com ele você pode reutilizar vários métodos nas classes que “usam” determinados Traits

Benditas Traits

Instigado pelo Kinn Julião, decidi dar uma fuçada nos posts sobre PHP Traits.

Pra começar, um fato: Estará disponível na versão final do PHP 5.4.

O Trait é solicitado pela programação como uma forma de burlar a limitação da herança simples no PHP. Com ele você pode, por exemplo, reutilizar vários métodos nas classes que “usam” determinados Traits, mesmo estando em hierarquias de classes diferentes.Parece herança, mas não é!

O Trait não pode ser instanciado, funcionando então como se fosse uma classe abstrata. Parece abstração, mas não é!

Me perdoem pelo exemplo idiota. Prometo pensar em alguma coisa mais concreta em outro momento, mas por enquanto, vamos imaginar que temos as seguintes classes:

 

<?php

class Veiculo {

}

class Aeronaves {

}

class Brinquedo {

}

class Carrinho {

}

class Aviaozinho {

}

class Helicoptero {

}

class Automovel {

}

A classe Veículo tem características próprias que a diferem de Brinquedo, mas existem objetos que poderiam se enquadrar em uma dessas categorias e, ao mesmo tempo possuir características próprias de um brinquedo. Uma aeronave de brinquedo ou um helicóptero, por exemplo.

Se a gente tivesse herança multipla dava pra resolver, né? A resposta é não temos, por isso criaremos Traits.

Então a gente pode fazer assim:

<?php

trait Veiculo {

public function rodar(){
/* x */
}

}

trait Aeronave {

public function voar(){
/* x */
}

}

trait Brinquedo {

private $pedagogico;

public function isPedagogico(){
return $this->pedagogico;
}

}

class Carrinho {

use Veiculo, Brinquedo;

}

class Aviaozinho {

use Aeronave, Brinquedo;

}

class Helicoptero {

use Aeronave;

}

class TanqueDeGuerra {

use Veiculo;

}

Uma instância de carrinho pode utilizar o método rodar() para andar sobre a terra e também chamar o método isPedagogico() para recuperar a informação sobre sua utilidade educativa, pois ele tem características de um veículo de brinquedo. Já uma instância de tanque de guerra não poderia ser considerada um brinquedo, estando fadada a não ter características pedagógicas em sua composição.

Por outro lado, um aviãozinho poderia voar() e ser pedagógico enquanto que um helicóptero somente voaria…

Exemplo + simples de junção

O exemplo dado no PHP Masters foi ótimo, pois combina de forma muito simples essa junção de funções:

<?php
trait Alou {
function printAlou() {
echo "Alou";
}
}

trait Mulecada {
function printMulecada() {
echo "Mulecada";
}
}

class imprimeCoisas {
use Alou, Mulecada;
}

$objeto = new imprimeCoisas();
echo $objeto->printAlou() . " " . $objeto->printMulecada(); // a saída será "Alou Mulecada"

Outras observações importantes (sobrescritas e combinações com heranças):

  • Métodos de um Trait sobrescrevem os métodos herdados de uma classe pai, mas os métodos reescritos na classe atual sobrescrevem os métodos de um Trait.

como assim?

<?php
trait Oi {
function dizOi() {
return "Oi";
}

function dizJose() {
return "Jose";
}

function dizOiJose() {
echo $this->dizOi() . " " . $this->dizJose(); // "Oi Jose"
}

function dizPaiJose() {
echo $this->dizOi() . " " . parent::dizJose();
}
}

class Pai {
function dizJose(){
return "Pai Jose";
}
}

class OiJose extends Pai {
use Oi;
function dizJose() {
return "Jose";
}
}

$h = new OiJose();
$h->dizOiJose(); // Oi Jose
$h->dizPaiJose(); // Oi Pai Jose

Acho que agora deu pra esclarecer melhor.

Consertando Conflitos

Se você criar 2 ( ou + ) Traits com métodos de mesmo nome, utiliza-las em uma mesma classe e chamar na execução, o PHP surta.

Pra resolver isso, entra em cena o tal do insteadof, que em português moderno quer dizer “ao invés de”. 😀

veja o exemplo do manual:

<?php
trait A {
public function minusculo() {
echo 'a';
}
public function maiusculo() {
echo 'A';
}
}

trait B {
public function minusculo() {
echo 'b';
}
public function maiusculo() {
echo 'B';
}
}

class Impressor {
use A, B {
B::minusculo insteadof A;
A::maiusculo insteadof B;
}
}

class Apelidado_Impressor {
use A, B {
B::minusculo insteadof A;
A::maiusculo insteadof B;
B::maiusculo as bezao;
}
}

$impressor = new Impressor();
$impressor->minusculo(); // b
$impressor->maiusculo(); // A

$novo_impressor = new Apelidado_Impressor();
$novo_impressor->minusculo(); // b
$novo_impressor->maiusculo(); // A
$novo_impressor->bezao(); // B

?>

Esse apelidamento  (conhecido como Aliasing) pode ser usado para modificar a visibilidade de um método. Imaginando que declaramos um Trait chamado HelloWorld, que possui um método público imprimir(), podemos trata-lo como privado na classe que “usa” o Trait da seguinte forma:

class MyClass2 {
use HelloWorld {
sayHello as private myPrivateHello;
}
}

Papai, posso fazer Traits e usá-los dentro de outro Trait?

Claro que pode meu filho. (Tem gente que complica tudo… Por que, não fazê-lo?)

de acordo com o manual:

trait Hello {
public function sayHello() {
echo 'Hello ';
}
}

trait World {
public function sayWorld() {
echo 'World!';
}
}

trait HelloWorld {
use Hello, World;
}

class MyHelloWorld {
use HelloWorld;
} 

Pelamor... Só em último caso, hem!?

Pra complicar só mais um pouquinho, sugiro que vocês dêem uma olhada no manual pra aprender mais sobre a constante mágica __TRAIT__ (que retorna o nome do Trait utilizado), membros estáticos e abstratos em Traits. -> http://php.net/manual/en/language.oop5.traits.php

Agradeço também ao grande ShameerC e Chris que contribuiram fornecendo na web as informações revisitadas nessa postagem.

Grande abraço!

6 Comments

  1. Marcos PL

    Muito bom o post. Claro, objetivo. Me fez entender com muita clareza o conceito de traits.

  2. eminetto

    Muito bom o post.
    Vou usar como referência em uma palestra, como exemplo de traits, se não se importa

  3. Diogo Besson

    Nossa, eminetto! Desculpe a demora pra responder, cara. Estive ausente na semana passada.
    Pode usar sim. Fique à vontade.
    obrigado pela visita!
    abraço.

  4. Diogo Besson

    Obrigado pela visita, Marcos!
    abraço.

  5. Andreus Timmm

    Parabéns pelo POST, ficou excelente!

  6. Diogo Besson

    Obrigado, Andreus!
    Volte sempre.
    abraço

Leave a Comment

O seu endereço de e-mail não será publicado.