0

Конфликты с другими методами трейтов

12

Как мне справиться с трейты, у которых методы имеют одинаковые имена?

Я столкнулся с проблемой при использовании нескольких трейтов с методами, которые имеют одинаковые названия. Вот пример кода:

trait FooTrait {
    public function fooMethod() {
        return 'foo method';
    }

    public function getRow() {
        return 'foo row';
    }
}

trait TooTrait {
    public function tooMethod() {
        return 'too method';
    }

    public function getRow() {
        return 'too row';
    }
}

class Boo {
    use FooTrait;
    use TooTrait;

    public function booMethod() {
        return $this->fooMethod();
    }
}

При попытке вызвать метод getRow() в классе Boo, я получаю ошибку:

Fatal error: Trait method getRow has not been applied, because there are collisions with other trait methods on Boo in...

Как мне с этим справиться?

Кроме того, как мне получить метод из trait FooTrait?

$a = new Boo;
var_dump($a->getRow()); // Fatal error: Call to undefined method Boo::getRow() in...

Редактировать:

Теперь я изменил класс Boo, чтобы явно указать, откуда брать метод getRow:

class Boo {
    use FooTrait, TooTrait {
        FooTrait::getRow insteadof TooTrait;
    }

    public function booMethod() {
        return $this->fooMethod();
    }
}

Но если я также хочу получить метод getRow из TooTrait в классе Boo, возможно ли это?

3 ответ(ов)

1

При возникновении конфликта между методами в Trait в PHP, возникает фатальная ошибка, если конфликт не разрешён явно. Чтобы разрешить конфликты имен в Trait, используемых в одном классе, необходимо использовать оператор insteadof, который позволяет выбрать один из конфликтующих методов.

Поскольку этот оператор позволяет исключить только один метод, для включения одного из конфликтующих методов под другим именем можно использовать оператор as.

Например, приведённый код демонстрирует, как разрешить конфликты:

<?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;
    }
}

В этом примере класс Talker использует методы smallTalk из Trait B и bigTalk из Trait A. Класс Aliased_Talker, в свою очередь, делает это же, но в дополнение использует реализацию bigTalk из Trait B под другим псевдонимом talk.

Пример кода, аналогичный вашему случаю, может выглядеть так:

class Boo {
    use FooTrait, TooTrait {
        FooTrait::getRow insteadof TooTrait;
    }

    public function booMethod() {
        return $this->fooMethod();
    }
}

Этот подход работает даже если вы разделяете операторы use, но, на мой взгляд, более очевиден. Также можно использовать оператор as для объявления алиаса.

0

Да, это возможно. Вы можете использовать что-то подобное:

class Boo {
    use FooTrait, TooTrait {
        FooTrait::getRow as getFooRow;
        TooTrait::getRow as getTooRow;
    }

    public function getRow(...$arguments)
    {
        return ['foo' => $this->getFooRow(...$arguments), 'too' => $this->getTooRow(...$arguments)];
    }

    public function booMethod(...$arguments)
    {
        return $this->getFooRow(...$arguments);
    }
}

В данном примере класс Boo использует два трейта FooTrait и TooTrait. Методы getRow обоих трейтов переименовываются в getFooRow и getTooRow соответственно, чтобы избежать конфликтов. Метод getRow класса Boo затем объединяет результаты обоих методов и возвращает их в виде массива. Метод booMethod позволяет получить доступ только к значению из getFooRow.

0

Если у вас простая ситуация, как в моем случае, вы можете создать трейт, который будет использовать другой трейт, что приведет к конфликту.

trait FooTrait {
  // ...

  public function fooMethod() {
        return 'foo method';
  }

  // ...
}

trait MyFooTrait {
    use FooTrait;

    public function fooMethod() {
        return 'my foo method';
    }
}

class Boo
{
    use MyFooTrait;
}

В вашем случае FooTrait приходит из стороннего PHP-пакета, и вы хотите использовать его с улучшенными методами в нескольких классах вашего приложения. Таким образом, вы просто "перекрываете" метод fooMethod в своем трейте MyFooTrait, чтобы добавить свою реализацию. Этот подход позволит вам использовать функциональность из FooTrait, а также адаптировать поведение для ваших нужд.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь