Что делает ключевое слово `final` в PHP?

«Что делает ключевое слово `final` в PHP?» — вопрос из категории ООП, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ключевое слово final в PHP используется для наложения ограничений на наследование в ООП. Оно может применяться к классам и методам и имеет два основных назначения:

  1. final класс: Запрещает наследование от этого класса.
  2. final метод: Запрещает переопределение (override) этого метода в дочерних классах.

Зачем это нужно?

  • Защита проектных решений (Design Enforcement): Когда вы проектируете класс, который является критически важным компонентом (например, класс, отвечающий за подключение к базе данных или шифрование), вы можете объявить его final, чтобы предотвратить случайное или намеренное изменение его поведения через наследование, которое могло бы нарушить контракты или безопасность системы.
  • Оптимизация и стабильность: Объявление методов final позволяет движку PHP (например, JIT в OPcache) применять более агрессивные оптимизации, так как он уверен, что реализация метода не изменится в runtime. Это также защищает от ошибок, когда переопределение метода в наследнике ломает ожидаемую логику родителя.
  • Принцип композиции над наследованием: Использование final поощряет проектирование через композицию (включение объектов), что часто является более гибким и предсказуемым подходом, чем глубокие иерархии наследования.

Примеры:

// 1. Final класс: нельзя унаследовать.
final class DatabaseConnection {
    // ... логика подключения
}
// Это вызовет фатальную ошибку:
// class MyConnection extends DatabaseConnection {}

// 2. Final метод: можно унаследовать класс, но нельзя переопределить метод.
class PaymentProcessor {
    final public function validateTransaction(Transaction $tx): bool {
        // Критичная логика проверки, которую нельзя менять.
        return $tx->amount > 0 && $tx->isValidSignature();
    }

    public function process(Transaction $tx) {
        if ($this->validateTransaction($tx)) {
            // ... обработка
        }
    }
}

class PayPalProcessor extends PaymentProcessor {
    // Можно добавить новые методы...
    public function processRefund() {}

    // НО НЕЛЬЗЯ переопределить validateTransaction()
    // public function validateTransaction(Transaction $tx) {} // Фатальная ошибка
}