Все методы, объявленные в интерфейсе, фактически являются абстрактными: все их необходимо явно определить в дочернем классе. То есть, описать какую-то универсальную логику с помощью интерфейса не получится.
При этом
Дочерний класс должен реализовать все методы, описанные в интерфейсе, иначе произойдёт фатальная ошибка.
Кроме того, в интерфейсах нельзя объявлять свойства. Можно только методы и константы.
По сравнению с абстрактным классом, содержащим только абстрактные методы, интерфейс имеет единственную дополнительную возможность: классы можно создать на основе нескольких интерфейсов, но нельзя на основе нескольких родительских классов.
От интерфейсов отличаются возможностью объявления неабстрактных методов. Дочерний класс должен реализовывать только абстрактные, как и в случае с обычным наследованием.
В трейтах нельзя объявлять свойства, т.к. они не имеют состояния.
Фактически нельзя переопределять константы в дочерних классах:
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 '
}
Это делает бесполезной такую схему использования констант, а вместе с этим - и использование здесь интерфейсов.
Абстрактные методы можно подставить в код, поставив в фокус объявление класса (конструкцию 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
];
}
}
Рекомендуется называть свойство для декларации структуры аналогично методу. В противном случае придется изучать код трейта, ,чтобы выяснить имя свойства.