Menu

Traits em PHP: use, várias traits e insteadof

Uma trait PHP é um bloco reutilizável de métodos e propriedades que você copia para uma classe com use TraitName;. Veja como usar várias traits, resolver conflitos de nome com insteadof e as, membros abstratos e estáticos em traits, qual método vence e trait, interface ou classe abstrata.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Uma trait PHP é um bloco reutilizável de métodos e propriedades. Você a declara com trait Name { ... } e a incorpora em qualquer classe com use Name; dentro do corpo da classe. O PHP copia os membros da trait para a classe, então eles funcionam exatamente como se você os tivesse escrito ali.

User e Robot não têm uma classe pai em comum, mas agora os dois têm hello(). As traits existem para esse caso: classes PHP só podem estender uma classe, e uma trait compartilha código de lado, entre classes que não têm relação.

Traits com propriedades

Uma trait pode trazer propriedades junto com os métodos. Os métodos usam $this como qualquer método da classe, porque, depois que a trait é copiada, eles são métodos da classe:

Uma classe não pode declarar uma propriedade com o mesmo nome de uma propriedade da trait, a menos que as duas declarações sejam idênticas (mesma visibilidade, tipo e valor padrão); caso contrário é um erro fatal.

Usar várias traits

Liste várias traits numa instrução use, separadas por vírgulas, ou escreva várias linhas use. A classe recebe os membros de todas:

Resolver conflitos com insteadof e as

Quando duas traits definem um método com o mesmo nome, o PHP se recusa a adivinhar e para com um erro fatal. Dentro de um bloco use, insteadof escolhe o vencedor, e as mantém o método perdedor com outro nome:

Com um simples use EnglishGreeting, FrenchGreeting; e sem bloco, o script para antes de imprimir qualquer coisa:

PHP Fatal error:  Trait method FrenchGreeting::greet has not been applied as Host::greet, because of collision with EnglishGreeting::greet in /home/index.php on line 12

as também pode mudar a visibilidade. use Loggable { log as protected; } torna o log() da trait protegido só nesta classe, e use Loggable { log as private writeLog; } adiciona um alias privado.

Membros abstratos e estáticos numa trait

Uma trait pode exigir algo da classe que a usa declarando um método abstrato. Ela também pode ter métodos e propriedades estáticos. Cada classe que usa a trait recebe a própria cópia de uma propriedade estática:

Car e Bike contam separadamente, porque a propriedade estática é copiada para cada classe. Isso é diferente de uma propriedade estática declarada numa classe pai compartilhada, que todas as filhas dividem; a página sobre static mostra esse caso.

Qual método vence: classe, trait ou classe pai

Se o mesmo nome de método existe na própria classe, numa trait que ela usa e na classe pai, o PHP escolhe nesta ordem: o método da própria classe, depois o da trait, depois o da classe pai. Dentro da trait, parent:: ainda alcança a versão da classe pai:

Uma trait não é um tipo

instanceof nunca bate com uma trait, e um parâmetro tipado com o nome de uma trait rejeita qualquer objeto com um TypeError. Para perguntar "esta classe tem o comportamento", combine a trait com uma interface, ou inspecione a classe com class_uses():

O padrão acima é o comum: a interface Exportable é o que as funções usam no type hint e o que o instanceof verifica, e a trait ExportsProperties é uma implementação pronta que as classes podem usar ou substituir. Uma trait sozinha é código compartilhado sem contrato; quando você se pegar verificando class_uses() na lógica de negócio, a trait precisava de uma interface ao lado. Para código compartilhado por uma família de classes relacionadas, uma classe abstrata pode ser a escolha mais simples.

Perguntas frequentes

O que é uma trait no PHP?

Uma trait é um grupo nomeado de métodos (e propriedades) declarado com trait Name { } que uma classe incorpora com use Name;. O PHP copia os membros da trait para a classe, então eles se comportam como se você os tivesse escrito ali. É um jeito de compartilhar código entre classes que não têm uma classe pai em comum.

Uma classe PHP pode usar várias traits?

Sim: use Timestamps, SoftDeletes; ou várias linhas use. Se duas traits definem um método com o mesmo nome, você precisa escolher um com insteadof, por exemplo use A, B { A::hello insteadof B; }, e pode manter o outro com um novo nome com B::hello as helloB;.

Qual a diferença entre uma trait e uma interface no PHP?

Uma interface é um tipo sem código: diz quais métodos uma classe precisa ter, e instanceof funciona com ela. Uma trait é código sem tipo: cola corpos de métodos numa classe, mas $obj instanceof SomeTrait é sempre falso. Elas costumam ser combinadas: uma interface para o tipo, uma trait com uma implementação padrão.

Uma trait pode ter construtor ou métodos abstratos?

Sim, as duas coisas. Uma trait pode declarar abstract public function name(): string; para exigir que a classe que a usa forneça esse método, e pode definir __construct, embora um construtor numa trait conflite facilmente com o da própria classe e raramente seja uma boa ideia.

Qual método vence se uma classe e uma trait definem o mesmo método?

O método da própria classe vence o da trait, e o método da trait vence um método herdado da classe pai. Então a ordem é: classe, depois trait, depois classe pai.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR