Trait (трейт) в PHP — это механизм повторного использования кода, предназначенный для преодоления ограничений одиночного наследования. Он позволяет включать наборы методов в несколько независимых классов, обеспечивая «горизонтальную» композицию поведения, без необходимости создания сложной иерархии наследования.
Пример использования в плагине
Проблема
В плагине есть два абсолютно разных класса:
abstract class Options { ... } // Страница настроек в админке
abstract class Metabox { ... } // Поля в редакторе поста
Они про разные вещи. Наследовать друг от друга — бессмысленно. Но обоим нужна одна и та же логика: работа с секциями и полями.
Оба хранят массив секций. Оба умеют получить все поля, найти поле по имени, добавить секцию. Копировать этот код в два класса — дублирование.
Решение: trait HasSections
trait HasSections
{
protected array $sections = [];
public function sections(): array
{
return $this->sections;
}
public function allFields(): array
{
return array_merge(...array_column($this->sections(), 'fields'));
}
public function fieldByName(string $name): ?Field
{
foreach ($this->allFields() as $field) {
if ($field->name === $name) {
return $field;
}
}
return null;
}
public function defaultValue(string $name): mixed { ... }
protected function appendSection(array $section): void { ... }
}
Как подключается — одна строка use
abstract class Options
{
use HasSections; // ← Options получил sections(), allFields(), fieldByName()...
// свои свойства и методы
}
abstract class Metabox
{
use HasSections; // ← Metabox получил те же самые методы
// свои свойства и методы
}
use HasSections — PHP буквально вставляет код трейта внутрь класса. Как если бы ты скопировал все методы руками, но без дублирования.
Как это используется дальше
// PluginOptions.php (наследник Options)
protected function __construct()
{
$all_options = array_map(fn($path) => include $path, $options_files);
$this->setSections($all_options);
// ↑ метод из HasSections, доступен через Options
}
// PostMeta.php (наследник Metabox)
public function __construct(array $sections)
{
array_walk($sections, [$this, 'appendSection']);
// ↑ метод из HasSections, доступен через Metabox
}
Оба вызывают appendSection() — но ни Options, ни Metabox этот метод не определяют. Он приходит из трейта.