Разрешение конфликтов трейтов
Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 53 из 91.
Когда два трейта определяют методы с одинаковым именем, PHP выдает фатальную ошибку. Вы должны явно указать PHP, как разрешить этот конфликт, используя операторы insteadof и as.
<?php
trait FileLogger {
public function log(string $message): void {
echo "File: $message";
}
}
trait DatabaseLogger {
public function log(string $message): void {
echo "DB: $message";
}
}
class Application {
use FileLogger, DatabaseLogger {
FileLogger::log insteadof DatabaseLogger;
DatabaseLogger::log as logToDatabase;
}
}
$app = new Application();
$app->log("Error occurred");
$app->logToDatabase("Error occurred");
Вывод:
File: Error occurred
DB: Error occurredОператор insteadof выбирает, какой метод трейта использовать при возникновении конфликта. В этом примере FileLogger::log имеет приоритет и становится методом log() по умолчанию. Оператор as создает псевдоним, позволяя обращаться к методу другого трейта под другим именем.
Вы также можете использовать as для изменения видимости метода:
<?php
trait Greeting {
public function sayHello(): void {
echo "Hello!";
}
}
class Person {
use Greeting {
sayHello as private;
}
}
Это делает sayHello() приватным внутри класса Person, даже если он является публичным в трейте. Разрешение конфликтов дает вам детальный контроль над тем, как методы трейтов интегрируются в ваши классы.
Задание
ЛегкоДавайте создадим систему уведомлений, в которой различные каналы могут отправлять сообщения, но нам нужно разрешать конфликты, когда несколько трейтов предоставляют метод с одинаковым именем.
Вы организуете свой код в четырех файлах:
EmailNotifier.php— Создайте трейт с именемEmailNotifierс методомnotify(string $message), который возвращает"Email: [message]".SmsNotifier.php— Создайте трейт с именемSmsNotifierс методомnotify(string $message), который возвращает"SMS: [message]".AlertService.php— Создайте классAlertService, который использует оба трейта. Поскольку оба трейта имеют методnotify(), вам нужно будет разрешить этот конфликт:- Используйте
EmailNotifier::notifyв качестве методаnotify()по умолчанию - Создайте псевдоним
notifyBySmsдля методаSmsNotifier::notify
notifyAll(string $message), который вызывает оба метода уведомления и возвращает их объединенными на отдельных строках.- Используйте
main.php— Подключите все необходимые файлы и создайте экземплярAlertService. Вы получите один входной параметр: строку сообщения. Выведите три строки:- Результат вызова
notify()с сообщением - Результат вызова
notifyBySms()с сообщением - Результат вызова
notifyAll()с сообщением
- Результат вызова
Это демонстрирует, как insteadof определяет, какой метод трейта побеждает при конфликте имен, в то время как as позволяет сохранить доступ к методу другого трейта под другим именем.
Шпаргалка
Когда несколько трейтов определяют методы с одинаковым именем, PHP требует явного разрешения конфликтов с помощью операторов insteadof и as.
Оператор insteadof выбирает, метод какого трейта использовать по умолчанию:
<?php
trait FileLogger {
public function log(string $message): void {
echo "File: $message";
}
}
trait DatabaseLogger {
public function log(string $message): void {
echo "DB: $message";
}
}
class Application {
use FileLogger, DatabaseLogger {
FileLogger::log insteadof DatabaseLogger;
DatabaseLogger::log as logToDatabase;
}
}
$app = new Application();
$app->log("Error occurred"); // File: Error occurred
$app->logToDatabase("Error occurred"); // DB: Error occurred
Оператор as создает псевдоним для метода трейта, позволяя обращаться к нему под другим именем.
Вы также можете использовать as для изменения видимости метода:
<?php
trait Greeting {
public function sayHello(): void {
echo "Hello!";
}
}
class Person {
use Greeting {
sayHello as private;
}
}
Попробуйте сами
<?php
require_once 'AlertService.php';
// Чтение входных данных
$message = trim(fgets(STDIN));
// TODO: Создать экземпляр AlertService
// TODO: Вывести результат вызова notify() с сообщением
// TODO: Вывести результат вызова notifyBySms() с сообщением
// TODO: Вывести результат вызова notifyAll() с сообщением
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy