Skip to content

Instantly share code, notes, and snippets.

@1234ru
Last active June 5, 2024 22:27
Show Gist options
  • Save 1234ru/145d897aeafcf7828f520cc32160cc86 to your computer and use it in GitHub Desktop.
Save 1234ru/145d897aeafcf7828f520cc32160cc86 to your computer and use it in GitHub Desktop.
Про интерфейсы (interfaces) и трейты (traits) в PHP

Все методы, объявленные в интерфейсе, фактически являются абстрактными: все их необходимо явно определить в дочернем классе. То есть, описать какую-то универсальную логику с помощью интерфейса не получится.

При этом

Дочерний класс должен реализовать все методы, описанные в интерфейсе, иначе произойдёт фатальная ошибка.

Кроме того, в интерфейсах нельзя объявлять свойства. Можно только методы и константы.

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

От интерфейсов отличаются возможностью объявления неабстрактных методов. Дочерний класс должен реализовывать только абстрактные, как и в случае с обычным наследованием.

Недостатки

В трейтах нельзя объявлять свойства, т.к. они не имеют состояния.

Фактически нельзя переопределять константы в дочерних классах:

trait SomeTrait
{
    private const SOME_CONST = 5;
}

class SomeClass {
    use SomeTrait;
    // private const SOME_CONST = 10; // Нельзя
    private const SOME_CONST = 5; // Можно только так
}

Остается лишь использовать для подобных целей методы вместо свойств и констант:

trait SomeTrait
{
    public function someMethod()
    {
        return $this->prop();
        // вместо $this->prop или self::PROP
    }
    
    abstract private function prop();
}

class SomeClass {
    use SomeTrait;

    private function prop()
    {
        return "A prop";
    }
    // Вместо
    // private $prop = "A prop"
    // или
    // private const PROP = "A prop";
}

Можно назначить классу, наряду с трейтом, вышестоящий интерфейс, в котором определить константу. Однако, например, в PhpStorm не будет подсказок, относящихся к её переопределению:

interface SomeInterface
{
    const SOME_CONSTANT = null;
}

trait SomeTrait
{
    public function someMethod()
    {
        return self::SOME_CONSTANT;
    }
}

class SomeClass implements SomeInterface {
    use SomeTrait;
    const SOME_CONSTANT = 3; // No suggestions after typing 'const '
}

Это делает бесполезной такую схему использования констант, а вместе с этим - и использование здесь интерфейсов.

PhpStorm: автозаполнение в дочернем классе

Абстрактные методы можно подставить в код, поставив в фокус объявление класса (конструкцию class <имя>) и вызвав контекстное меню (Alt+Enter), где выбрать "Add method stubs".

Неабстрактные методы: нажатие Ctrl+Space внутри тела класса (между скобками - {...}) даст список подсказок. Свойства и методы будут расположены в конце, поэтому для перехода к ним нужно сразу нажать Up.

Подсказки deep-assoc-completion работают:

trait SomeTrait
{
    /** @return = self::$someMethodReturnDeclaration */
    abstract public function someMethod();
}

class SomeClass {
    use SomeTrait;

    /** @var = [ 'x' => int ] */
    private $someMethodReturnDeclaration;
    
    function someMethod()
    {
        return [ 
            '' // suggests 'x' on Ctrl+space 
        ];
    }
}

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment