Programação Orientada a Objetos em PHP: Entendendo Traits
Aprenda traits em PHP: declare e use-as, combine múltiplas traits, resolva conflitos de métodos com insteadof e as, e use membros abstratos e estáticos.
Uma trait é um bloco reutilizável de métodos (e propriedades) que você pode misturar em qualquer classe. As traits resolvem um problema específico em PHP: uma classe só pode estender um pai, portanto, quando duas classes não relacionadas precisam compartilhar o mesmo comportamento, a herança simples não é suficiente. As traits permitem compartilhar esse comportamento horizontalmente — entre classes que não têm relacionamento pai-filho.
Este capítulo aborda o que são traits, como declará-las e usá-las, como combinar várias traits, como o PHP resolve conflitos de nomes de métodos e as armadilhas (métodos abstratos, membros estáticos, propriedades) que costumam confundir as pessoas.
O que é uma trait?
Uma trait é quase idêntica a uma classe, mas é declarada com a palavra-chave trait em vez de class. As principais diferenças:
- Uma trait não pode ser instanciada por conta própria — não existe
new MyTrait(). - Uma trait é usada dentro de uma classe com a palavra-chave
use. Seus métodos são então copiados para a classe como se você os tivesse escrito ali. - Uma classe pode usar qualquer número de traits, e uma trait pode usar outras traits.
Pense em uma trait como um copiar-e-colar assistido pelo compilador: o código da trait é literalmente incorporado em cada classe que a usa.
Declarando e usando uma trait
Declare uma trait com trait, depois incorpore-a em uma classe com use:
<?php
trait Greetable
{
public function greet(): string
{
return "Hello, my name is {$this->name}.";
}
}
class User
{
public function __construct(public string $name) {}
use Greetable;
}
$user = new User("Ada");
echo $user->greet();
// Output: Hello, my name is Ada.Observe que a trait referencia $this->name mesmo sem defini-lo. Uma trait é executada no contexto da classe que a usa, portanto pode depender de propriedades e métodos fornecidos por essa classe.
Usando múltiplas traits
Uma classe pode usar várias traits ao mesmo tempo. Separe-as com vírgulas ou liste cada uma com sua própria instrução use:
<?php
trait Loggable
{
public function log(string $message): string
{
return "[LOG] " . $message;
}
}
trait Jsonable
{
public function toJson(): string
{
return json_encode(get_object_vars($this));
}
}
class Order
{
use Loggable, Jsonable;
public function __construct(public int $id, public float $total) {}
}
$order = new Order(7, 49.99);
echo $order->log("created") . PHP_EOL;
echo $order->toJson();
// Output:
// [LOG] created
// {"id":7,"total":49.99}Resolvendo conflitos com insteadof e as
Se duas traits definem um método com o mesmo nome, o PHP gera um erro fatal, a menos que você indique qual manter. Use insteadof para escolher o vencedor e as para manter o perdedor com um alias:
<?php
trait FileStorage
{
public function save(): string
{
return "Saved to a file.";
}
}
trait DatabaseStorage
{
public function save(): string
{
return "Saved to the database.";
}
}
class Report
{
use FileStorage, DatabaseStorage {
DatabaseStorage::save insteadof FileStorage;
FileStorage::save as saveToFile;
}
}
$report = new Report();
echo $report->save() . PHP_EOL; // DatabaseStorage wins
echo $report->saveToFile(); // still reachable via the alias
// Output:
// Saved to the database.
// Saved to a file.A palavra-chave as também pode alterar a visibilidade de um método, por exemplo protected reset as private — útil quando você quer que um método de trait esteja disponível internamente, mas não como parte da API pública.
Membros abstratos e estáticos
As traits podem fazer mais do que conter métodos de instância concretos.
- Métodos abstratos permitem que uma trait exija que a classe que a usa implemente algo. É assim que uma trait declara uma dependência de sua hospedeira.
- Métodos e propriedades estáticos se comportam como membros estáticos normais, mas cada classe que usa a trait obtém sua própria cópia de qualquer propriedade estática.
<?php
trait Counter
{
private static int $count = 0;
public static function increment(): int
{
return ++self::$count;
}
// The using class MUST provide this:
abstract public function label(): string;
}
class PageView
{
use Counter;
public function label(): string
{
return "views";
}
}
echo PageView::increment() . PHP_EOL; // 1
echo PageView::increment() . PHP_EOL; // 2
echo (new PageView())->label(); // views
// Output:
// 1
// 2
// viewsTraits vs. herança e interfaces
Escolha a ferramenta certa:
- Herança (
extends) modela um relacionamento "é-um" e fornece um único pai. Use quando as classes genuinamente compartilham uma hierarquia de tipos. Veja PHP Herança. - Interfaces definem um contrato — quais métodos existem — mas não contêm implementação. Veja PHP Interfaces.
- Traits fornecem implementação que você pode misturar em classes não relacionadas. Um padrão comum é uma interface + uma trait: a interface anuncia a capacidade, a trait fornece o código padrão.
Se você é novo nesses conceitos, comece com PHP Classes e Objetos e PHP Classes Abstratas.
Armadilhas comuns
- Colisões de nomes são silenciosas até que não sejam. Combinar traits que definem o mesmo método é um erro fatal; sempre resolva com
insteadof/as. - Traits são incorporadas, não herdadas. Um método definido diretamente na classe substitui a versão da trait, e a versão da trait substitui qualquer coisa herdada de uma classe pai.
- Propriedades estáticas são por classe. Duas classes que usam a mesma trait não compartilham seu estado estático — cada uma recebe uma cópia separada.
- Não abuse delas. Uma trait que precisa de muitas propriedades de sua hospedeira frequentemente é um sinal de que composição (um objeto real) ou herança se encaixa melhor.
Conclusão
As traits oferecem ao PHP uma forma limpa de compartilhar implementações de métodos entre classes não relacionadas, contornando a limitação de herança única. Declare-as com trait, incorpore-as com use, resolva conflitos com insteadof e as, e lembre-se de que o código da trait é incorporado em cada classe que a usa. Usadas com moderação, as traits mantêm comportamentos transversais — logging, serialização, contadores — em um único lugar, em vez de cópias espalhadas.