Traits
O PHP implementa uma maneira de reúso de código chamada Traits.
Traits (Traços, Carecterísticas) são um mecanismo para reúso de código em linguagens de herança simples como o PHP. A intenção de uma Trait é reduzir algumas limitações de herança simples permitindo que um desenvolvedor reutilize livremente conjuntos de métodos em várias classes independentes habitando em diferentes hierarquias de classe. A semântica da combinação de Traits e classes é definida de uma maneira que reduz a complexidade, e evita os problemas típicos associados com herança múltipla e Mixins.
Uma Trait é similar a uma classe, mas destina-se apenas a agrupar funcionalidade de uma forma refinada e consistente. Não é possível instanciar uma Trait por si só. Ela é uma adição à herança tradicional e permite composição e comportamento horizontais; isto é, a aplicação de membros de classe sem exigir herança.
Example #1 Exemplo de Trait
<?php
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>
Precedência
Um membro herdado de uma classe base é substituído por um membro inserido por uma Trait. Na ordem de precedência, os membros da classe atual substituem os métodos da Trait, que por sua vez substituem os métodos herdados.
Example #2 Exemplo de Ordem de Precedência
Um membro herdado de uma classe base é substituído pelo método inserido em MyHelloWorld da Trait SayWorld. O comportamento é o mesmo para métodos definidos na classe MyHelloWorld. Na ordem de precedência, os métodos da classe atual sobrescrevem os métodos da Trait, que por sua vez substituem os métodos da classe base.
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
O exemplo acima produzirá:
Hello World!
Example #3 Exemplo de Ordem de Precedência Alternativa
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
}
$o = new TheWorldIsNotEnough();
$o->sayHello();
?>
O exemplo acima produzirá:
Hello Universe!
Traits Múltiplas
Traits múltiplas podem ser inseridas em uma classe, listando-as na declaração
use
, separadas por vírgulas.
Example #4 Uso de Traits Múltiplas
<?php
trait Hello {
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayWorld() {
echo 'World';
}
}
class MyHelloWorld {
use Hello, World;
public function sayExclamationMark() {
echo '!';
}
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>
O exemplo acima produzirá:
Hello World!
Resolução de Conflitos
Se duas Trais inserem dois métodos com o mesmo nome, um erro fatal é gerado, se o conflito não for explicitamente resolvido.
Para resolver conflitos de nomes entre Traits usados na mesma classe,
o operador insteadof
deve ser usado para escolher exatamente
um dos métodos conflitantes.
Como isto permite apenas excluir métodos, o operador
as
pode ser usado para adicionar um apelido a um dos métodos. Note que
o operador as
não renomeia o método e também não
afeta nenhum outro método.
Example #5 Exemplo de Resolução de Conflito
Neste exemplo, Talker usa as Traits A e B. Como A e B possuem métodos conflitantes, a classe define a variante de smallTalk da Trait B, e a variante bigTalk da Trait A.
A classe Aliased_Talker usa o operador as
para perrmitir o uso da implementação bigTalk da Trait B sob um apelido adicional
talk
.
<?php
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}
class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
}
class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk;
}
}
?>
Mudando a Visibilidade de Método
Ao utilizar a sintaxe as
, pode-se também ajustar a visibilidade
de métodos na classe.
Example #6 Exemplo de Mudança de Visibilidade de Método
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
// Modifica a visibilidade de sayHello
class MyClass1 {
use HelloWorld { sayHello as protected; }
}
// Apelido de método com visibilidade modificada
// Visibilidade de sayHello não é modificada
class MyClass2 {
use HelloWorld { sayHello as private myPrivateHello; }
}
?>
Traits Compostas de Traits
Assim como as classes podem usar Trais, outros Traits tamém podem. Ao utilizar uma ou mais Traits em uma definição de Trait, ela pode ser comporta parcialmente ou integralmente dos membros definidos nessas outras Traits.
Example #7 Exemplo de Traits Compostas de Traits
<?php
trait Hello {
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayWorld() {
echo 'World!';
}
}
trait HelloWorld {
use Hello, World;
}
class MyHelloWorld {
use HelloWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
?>
O exemplo acima produzirá:
Hello World!
Membros abstratos de Traits
Traits suportam o uso de métodos abstratos para impor requisitos sobre uma classe expositora. Métodos públicos, protegidos e privados são suportados. Antes do PHP 8.0.0, apenas métodos abstratos públicos e protegidos eram suportados.
A partir do PHP 8.0.0, a assinatura de um método concreto deverá seguir as regras de compatibilidade de assinaturas. Anteriormente, sua assinatura poderia ser diferente.
Example #8 Requisitos Expressos por Métodos Abstratos
<?php
trait Hello {
public function sayHelloWorld() {
echo 'Hello'.$this->getWorld();
}
abstract public function getWorld();
}
class MyHelloWorld {
private $world;
use Hello;
public function getWorld() {
return $this->world;
}
public function setWorld($val) {
$this->world = $val;
}
}
?>
Membros Estáticos de Traits
Traits podem definir variáveis estáticas, métodos estáticos e propriedades estáticas.
Note:
A partir do PHP 8.1.0, chamar um método estático, ou acessar uma propriedade estática diretamente em uma Trait foi descontinuado. Métodos estáticos e propriedades somente devem ser acessados em uma classe utilizando a Trait.
Example #9 Variáveis Estáticas
<?php
trait Counter {
public function inc() {
static $c = 0;
$c = $c + 1;
echo "$c\n";
}
}
class C1 {
use Counter;
}
class C2 {
use Counter;
}
$o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>
Example #10 Métodos Estáticos
<?php
trait StaticExample {
public static function doSomething() {
return 'Doing something';
}
}
class Example {
use StaticExample;
}
Example::doSomething();
?>
Example #11 Propriedades Estáticas
<?php
trait StaticExample {
public static $static = 'foo';
}
class Example {
use StaticExample;
}
echo Example::$static;
?>
Propriedades
Traits podem também definir propriedades.
Example #12 Definindo Propriedades
<?php
trait PropertiesTrait {
public $x = 1;
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
?>
Se uma Trait define uma propriedade, uma classe não pode definir uma propriedade com o mesmo nome a menos que seja compatível (mesma visibilidade, tipo, modificador de somente-leitura e valor inicial), caso contrário um erro fatal é emitido.
Example #13 Resolução de Conflito
<?php
trait PropertiesTrait {
public $same = true;
public $different1 = false;
public bool $different2;
public bool $different3;
}
class PropertiesExample {
use PropertiesTrait;
public $same = true;
public $different1 = true; // Fatal error
public string $different2; // Fatal error
readonly protected bool $different3; // Fatal error
}
?>
Constantes
Traits podem, a partir do PHP 8.2.0, também definir constantes.
Example #14 Definindo Constantes
<?php
trait ConstantsTrait {
public const FLAG_MUTABLE = 1;
final public const FLAG_IMMUTABLE = 5;
}
class ConstantsExample {
use ConstantsTrait;
}
$example = new ConstantsExample;
echo $example::FLAG_MUTABLE; // 1
?>
Se uma Trait define uma constante, uma classe não pode definir uma constante com o mesmo nome a não ser que seja compatível (mesma visibilidade, valor inicial, e finalidade), caso contrário um erro fatal é emitido.
Example #15 Resolução de Conflito
<?php
trait ConstantsTrait {
public const FLAG_MUTABLE = 1;
final public const FLAG_IMMUTABLE = 5;
}
class ConstantsExample {
use ConstantsTrait;
public const FLAG_IMMUTABLE = 5; // Fatal error
}
?>