Разрешаване на конфликти в методите на трейтове в ООП в PHP

Тъй като един клас може да използва няколко трейта, може да срещнем проблем, който възниква, когато два трейта имат методи с едно и също име.

В този случай PHP ще изведе фатална грешка. За да коригираме ситуацията, ще трябва да разрешим конфликта на имената изрично. Как се прави това - ще видим на практика.

Да предположим, че имаме два трейта с еднакъв метод method:

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

Да предположим, че имаме и клас Test, който използва и двата ни трейта. Ако просто свържем и двата трейта към нашия клас, то PHP ще изведе грешка, тъй като трейтовете имат съвпадащи методи:

php<?php
  // Този код ще изведе грешка!
  class Test
  {
    use Trait1, Trait2; // свързваме трейтовете
  }
?>

Нека разрешим конфликта на имената на нашите трейтове. За това съществува специален оператор insteadof. С помощта на този оператор ще използваме метода method на трейта Trait1 вместо същия метод на трейта Trait2:

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

Както виждате, синтаксисът е следният: първо името на трейта, после две двоеточия, после името на метода, после нашият оператор insteadof и името на втория трейт.

Нека проверим:

php<?php
  class Test
  {
    use Trait1, Trait2 {
      Trait1::method insteadof Trait2;
    }
    
    public function __construct()
    {
      echo $this->method(); // ще изведе 1, тъй като това е метод на първия трейт
    }
  }
  
  new Test;
?>

И така, в нашия клас казахме, че ако се използва метод method, трябва да се взема от първия трейт. Може и обратното - да вземем метода на втория трейт:

php<?php
  class Test
  {
    use Trait1, Trait2 {
      Trait2::method insteadof Trait1;
    }
    
    public function __construct()
    {
      echo $this->method(); // ще изведе 2, тъй като това е метод на втория трейт
    }
  }
  
  new Test;
?>

Във всеки случай, ако укажем да използваме метода на един трейт, то методът на втория трейт става недостъпен. Може да използваме и метода на втория трейт, като го преименуваме чрез ключовата дума as, ето така:

php<?php
  class Test
  {
    use Trait1, Trait2 {
      Trait1::method insteadof Trait2;
      Trait2::method as method2;
    }
    
    public function __construct()
    {
      echo $this->method() + $this->method2(); // ще изведе 3
    }
  }
  
  new Test;
?>

При желание може да преименувате и метода на първия трейт:

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(); // ще изведе 3
    }
  }
  
  new Test;
?>

Използването на ключовата дума as без дефиниране на главния метод чрез insteadof не е позволено, това ще изведе грешка:

php<?php
  // Този клас ще изведе грешка:
  class Test
  {
    use Trait1, Trait2 {
      Trait1::method as method1;
      Trait2::method as method2;
    }
    
    public function __construct()
    {
      echo $this->method1() + $this->method2();
    }
  }
  
  new Test;
?>
Задание 1

Направете 3 трейта с имена Trait1, Trait2 и Trait3. Нека в първия трейт има метод method, връщащ 1, във втория трейт - едноименен метод, връщащ 2, а в третия трейт - едноименен метод, връщащ 3.

Задание 2

Направете клас Test, използващ и трите създадени от нас трейта. Направете в този клас метод getSum, връщащ сумата от резултатите на методите на свързаните трейтове.