• Traits no PHP

    Numa entrevista recente, me perguntaram o que seria uma trait no PHP. Mesmo sabendo o que é e sabendo usar, fiquei naquela pergunta: como posso explicar de modo mais técnico? Pois bem, resolvi explicar de forma mais didática.

    Algumas situações do dia a dia parecem que só replicando um método específico em várias classes pode resolver a situação de ter aquele método, mas sem herdar de outra classe, porque em muitos casos já tem uma herança na classe. Para facilitar essa reutilização de código em várias classes sem precisar de herança, se cria a trait, desse modo:

     1trait Foo {
     2    public function bar(string $val)
     3    {
     4        echo $val;
     5    }
     6}
     7
     8class MyClass {
     9    use Foo;
    10
    11    public function foo()
    12    {
    13        $this->bar();
    14    }
    15}
    

    Esse é um exemplo básico, criando uma trait com um método bar() e utilizando essa trait com use Foo;, assim o método da trait pode ser utilizado como um método da classe $this->bar(). Mas ainda tem mais casos como o método da trait utilizar um método da classe herdada com o parent::method(), como o exemplo a seguir:

     1class Base {
     2    public function base()
     3    {
     4        echo 'base';
     5    }
     6}
     7
     8trait Foo {
     9    public function bar() {
    10        parent::base();
    11        echo 'Foo Bar';
    12    }
    13}
    14
    15class MyClass extends Base {
    16    use Foo;
    17}
    

    A classe MyClass vai ter disponível o método bar() e esse método bar() vai chamar o método base(), isso só funciona quando o código é pensado para a trait estar sempre uma classe que herda essa mesma classe, ou qualquer uma que tenha esse mesmo nome de método.

    Um ponto a ser considerado também é que é possível sobrescrever o método da trait, se criar um novo método com o mesmo nome dentro da classe, o método da trait, como neste exemplo:

     1trait HelloWorld {
     2    public function sayHello() {
     3        echo 'Hello World!';
     4    }
     5}
     6
     7class TheWorldIsNotEnough {
     8    use HelloWorld;
     9    public function sayHello() {
    10        echo 'Hello Universe!';
    11    }
    12}
    

    Na chamada de sayHello a saída vai ser 'Hello Universe!' pois haverá uma sobrescrita de método.

    Como a trait não é uma herança, a classe pode utilizar diversas traits, só ir incluindo os nomes das traits no use separando por vírgula:

     1class MyClass {
     2    use TraitA, TraitB, Foo, Bar;
     3}
    

    Pode ter o caso também de usar várias traits que possuem métodos com nomes iguais, para escolher qual usar na chamada do use pode definir de qual trait será utilizado os métodos com nomes iguais:

     1trait A {
     2    public function smallTalk() {
     3        echo 'a';
     4    }
     5    public function bigTalk() {
     6        echo 'A';
     7    }
     8}
     9
    10trait B {
    11    public function smallTalk() {
    12        echo 'b';
    13    }
    14    public function bigTalk() {
    15        echo 'B';
    16    }
    17}
    18
    19class Talker {
    20    use A, B {
    21        B::smallTalk insteadof A;
    22        A::bigTalk insteadof B;
    23    }
    24}
    25
    26class Aliased_Talker {
    27    use A, B {
    28        B::smallTalk insteadof A;
    29        A::bigTalk insteadof B;
    30        B::bigTalk as talk;
    31    }
    32}
    

    Outra característica da trait é no escopo dentro da classe que a utiliza, a classe tem o poder de alterar o escopo original dos métodos da trait:

     1trait HelloWorld {
     2    public function sayHello() {
     3        echo 'Hello World!';
     4    }
     5}
     6
     7class MyClass1 {
     8    use HelloWorld { sayHello as protected; }
     9}
    10
    11class MyClass2 {
    12    use HelloWorld { sayHello as private myPrivateHello; }
    13}
    

    E para melhor organização, se pode criar uma trait para usar várias outras traits sem a necessidade de sempre importar todas, a composição de traits:

     1trait Hello {
     2    public function sayHello() {
     3        echo 'Hello ';
     4    }
     5}
     6
     7trait World {
     8    public function sayWorld() {
     9        echo 'World!';
    10    }
    11}
    12
    13trait HelloWorld {
    14    use Hello, World;
    15}
    16
    17class MyHelloWorld {
    18    use HelloWorld;
    19}
    

    Traits também podem ter métodos abstrados, que obrigam a classe que a utiliza a implementálos:

     1trait Hello {
     2    public function sayHelloWorld() {
     3        echo 'Hello'.$this->getWorld();
     4    }
     5    abstract public function getWorld();
     6}
     7
     8class MyHelloWorld {
     9    private $world;
    10    use Hello;
    11    public function getWorld() {
    12        return $this->world;
    13    }
    14    public function setWorld($val) {
    15        $this->world = $val;
    16    }
    17}
    

    Traits também podem ter métodos estáticos:

     1trait StaticExample
     2{
     3    public static function doSomething()
     4    {
     5        return 'Doing something';
     6    }
     7}
     8
     9class Example
    10{
    11    use StaticExample;
    12}
    

    Agora vamos falar de propriedades, sim, trait além de métodos podem ter propriedades:

     1trait PropertiesTrait
     2{
     3    public $x = 1;
     4}
     5
     6class PropertiesExample
     7{
     8    use PropertiesTrait;
     9}
    

    A classe PropertiesExample vai ter disponível nela a propriedade $x para utilizar. E é claro que não poderiam faltar as propriedades estáticas também:

     1trait T
     2{
     3    public static $counter = 1;
     4}
     5
     6class A
     7{
     8    use T;
     9
    10    public static function incrementCounter()
    11    {
    12        static::$counter++;
    13    }
    14}
    15
    16class B extends A
    17{
    18    use T;
    19}
    

    No caso das propriedades, não existe sobrescrita, se criar uma outra na classe com o mesmo nome, vai causar erro.

    Traits também podem utilizar constants, também com o mesmo ponto de atenção das propriedades que não podem ser sobrescritas e vai resultar em erro caso tente criar uma nova com o mesmo nome. Mas, pra finalizar, se o método for final ele não pode ser sobrescrito:

     1trait CommonTrait
     2{
     3    public function method()
     4    {
     5        echo 'Hello';
     6    }
     7}
     8
     9class FinalExampleA
    10{
    11    use CommonTrait {
    12        CommonTrait::method as final;
    13    }
    14}
    15
    16class FinalExampleB extends FinalExampleA
    17{
    18    public function method() {} // esse trecho aqui ocasiona um erro na execução
    19}
    

    Traits são um tipo de classe especial feita para reaproveitar melhor o código sem utilizar heranças, possuem métodos, propriedades e conseguem acessar até a classe pai, mas precisa ter muito cuidado, pois essa flexibilidade toda pode ocasionar diversos erros no uso de muitas traits.

    profile image of Lucien Risso Coreia

    Lucien Risso Coreia

    Engenheiro de Software, entusiasta de tecnologia e criador de conteúdo.

    Mais posts de Lucien Risso Coreia