Resolução de conflitos de métodos de traits na OOP em PHP

Como uma classe pode usar várias traits, podemos enfrentar um problema que surge quando duas traits possuem métodos com o mesmo nome.

Nesse caso, o PHP emitirá um erro fatal. Para corrigir a situação, será necessário resolver o conflito de nomes explicitamente. Como isso é feito - veremos na prática.

Suponha que temos duas traits com o mesmo método method:

php<?php
  trait Trait1
  {
    private function method()
    {
      return 1;
    }
  }
  
  trait Trait2
  {
    private function method()
    {
      return 2;
    }
  }
?>

Suponha que também tenhamos uma classe Test, usando ambas as nossas traits. Se simplesmente conectarmos ambas as traits à nossa classe, o PHP emitirá um erro, pois as traits possuem métodos conflitantes:

php<?php
  // Este código emitirá um erro!
  class Test
  {
    use Trait1, Trait2; // conectando as traits
  }
?>

Vamos resolver o conflito de nomes de nossas traits. Para isso, existe um operador especial insteadof. Usando este operador, utilizaremos o método method da trait Trait1 em vez do método homônimo da trait Trait2:

php<?php
  class Test
  {
    use Trait1, Trait2 {
      Trait1::method insteadof Trait2;
    }
  }
  
  new Test;
?>

Como você pode ver, a sintaxe é a seguinte: primeiro o nome da trait, depois dois pontos, depois o nome do método, depois nosso operador insteadof e o nome da segunda trait.

Vamos testar:

php<?php
  class Test
  {
    use Trait1, Trait2 {
      Trait1::method insteadof Trait2;
    }
    
    public function __construct()
    {
      echo $this->method(); // exibirá 1, pois é o método da primeira trait
    }
  }
  
  new Test;
?>

Então, em nossa classe, dissemos que se o método method for usado, ele deve ser obtido da primeira trait. Também podemos fazer o contrário - usar o método da segunda trait:

php<?php
  class Test
  {
    use Trait1, Trait2 {
      Trait2::method insteadof Trait1;
    }
    
    public function __construct()
    {
      echo $this->method(); // exibirá 2, pois é o método da segunda trait
    }
  }
  
  new Test;
?>

De qualquer forma, se especificarmos o uso do método de uma trait, o método da segunda trait torna-se inacessível. Podemos usar o método da segunda trait renomeando-o usando a palavra-chave as, assim:

php<?php
  class Test
  {
    use Trait1, Trait2 {
      Trait1::method insteadof Trait2;
      Trait2::method as method2;
    }
    
    public function __construct()
    {
      echo $this->method() + $this->method2(); // exibirá 3
    }
  }
  
  new Test;
?>

Se desejar, também pode renomear o método da primeira trait:

php<?php
  class Test
  {
    use Trait1, Trait2 {
      Trait1::method insteadof Trait2;
      Trait1::method as method1;
      Trait2::method as method2;
    }
    
    public function __construct()
    {
      echo $this->method1() + $this->method2(); // exibirá 3
    }
  }
  
  new Test;
?>

Não é possível usar a palavra-chave as sem definir o método principal via insteadof, isso emitirá um erro:

php<?php
  // Esta classe emitirá um erro:
  class Test
  {
    use Trait1, Trait2 {
      Trait1::method as method1;
      Trait2::method as method2;
    }
    
    public function __construct()
    {
      echo $this->method1() + $this->method2();
    }
  }
  
  new Test;
?>
Задание 1

Crie 3 traits com os nomes Trait1, Trait2 e Trait3. Deixe no primeiro trait um método method retornando 1, no segundo trait - um método homônimo retornando 2, e no terceiro trait - um método homônimo retornando 3.

Задание 2

Crie uma classe Test usando todas as três traits que criamos. Crie neste classe um método getSum retornando a soma dos resultados dos métodos das traits conectadas.