W3docs

insteadof

Ключевое слово "insteadof" в PHP указывает, какой трейт использовать вместо другого при конфликте методов. Разбираем синтаксис и примеры применения.

Ключевое слово PHP insteadof

insteadof — это ключевое слово PHP, используемое внутри блока use для разрешения конфликта методов между двумя и более трейтами. Когда класс использует несколько трейтов, каждый из которых определяет метод с одним и тем же именем, PHP не может определить, какой из них применить, и выбрасывает фатальную ошибку. insteadof указывает PHP, метод какого трейта следует оставить, а метод какого трейта — отбросить.

На этой странице описана проблема, которую решает это ключевое слово, его точный синтаксис, то, как оно сочетается с ключевым словом as для сохранения «проигравшего» метода, а также важные тонкости.

Проблема, которую решает insteadof

Трейт — это повторно используемый блок методов, который копируется в класс во время компиляции. Если два трейта определяют метод с одним именем, а класс использует оба, PHP не может сделать выбор. Результатом является фатальная ошибка коллизии:

<?php

trait FileLogger {
  function log() { echo "Writing to a file."; }
}
trait DatabaseLogger {
  function log() { echo "Writing to the database."; }
}

class Service {
  use FileLogger, DatabaseLogger; // No conflict resolution
}

// PHP Fatal error: Trait method DatabaseLogger::log has not been
// applied as Service::log, because of collision with FileLogger::log

Класс вообще не создаётся — это ошибка времени компиляции, а не времени выполнения. insteadof позволяет указать PHP, какой метод применить.

Синтаксис

insteadof записывается внутри блока в фигурных скобках оператора use:

use TraitWeKeep, TraitWeDrop {
    TraitWeKeep::methodName insteadof TraitWeDrop;
}

Читается это так: «использовать methodName из TraitWeKeep вместо TraitWeDrop». У трейта, указанного после insteadof, его версия метода удаляется из класса.

<?php

trait FileLogger {
  function log() { echo "Writing to a file."; }
}
trait DatabaseLogger {
  function log() { echo "Writing to the database."; }
}

class Service {
  use FileLogger, DatabaseLogger {
    FileLogger::log insteadof DatabaseLogger;
  }
}

$service = new Service();
$service->log();
// Output: Writing to a file.

Разрешение конфликта между тремя и более трейтами

Если конфликтуют три и более трейта, перечислите все трейты, которые нужно исключить, после insteadof через запятую:

<?php

trait Json    { function format() { echo "JSON output"; } }
trait Xml      { function format() { echo "XML output"; } }
trait Csv      { function format() { echo "CSV output"; } }

class Report {
  use Json, Xml, Csv {
    Json::format insteadof Xml, Csv;
  }
}

$report = new Report();
$report->format();
// Output: JSON output

Сохранение другого метода с помощью as

insteadof удаляет метод — но зачастую вы не хотите его терять, а просто хотите использовать его под другим именем, чтобы оба метода были доступны. Именно это делает ключевое слово as: оно создаёт псевдоним для исключённого метода под новым именем, сохраняя его после разрешения конфликта.

<?php

trait FileLogger {
  function log($msg) { return "[file] $msg"; }
}
trait DatabaseLogger {
  function log($msg) { return "[db] $msg"; }
}

class Service {
  use FileLogger, DatabaseLogger {
    FileLogger::log insteadof DatabaseLogger;  // FileLogger::log becomes log()
    DatabaseLogger::log as logToDb;            // DatabaseLogger::log survives as logToDb()
  }
}

$service = new Service();
echo $service->log("started"), "\n";      // [file] started
echo $service->logToDb("started"), "\n";  // [db] started

insteadof и as почти всегда используются вместе: insteadof выбирает победителя, as сохраняет проигравшего под псевдонимом. Ключевое слово as также может изменить видимость метода (например, FileLogger::log as protected;).

Подводные камни

  • insteadof разрешает только коллизии имён. Если у двух методов разные имена, конфликта нет и insteadof не нужен.
  • Необходимо ссылаться на метод, который реально существует в указанном трейте, используя синтаксис Трейт::метод. Опечатка приводит к фатальной ошибке.
  • Поведение не объединяется. Исключённый метод просто не применяется; insteadof никогда не вызывает оба метода одновременно.
  • Метод дочернего класса всё равно имеет приоритет над трейтом. Порядок разрешения методов следующий: методы, определённые непосредственно в классе, переопределяют методы трейтов, а методы трейтов переопределяют унаследованные (от родительского класса) методы.

Когда использовать

Используйте insteadof каждый раз, когда вы создаёте класс из нескольких трейтов и два из них предоставляют метод с одним именем — это часто встречается при подключении сторонних трейтов, которые вы не контролируете. В сочетании с as это позволяет компоновать поведение из независимых трейтов без переименования их исходного кода, сохраняя классы модульными и свободными от конфликтов.

Практика

Практика
Что делает ключевое слово 'insteadof' в PHP?
Что делает ключевое слово 'insteadof' в PHP?
Was this page helpful?