📑 Содержание
Введение
Релиз PHP 8.0, состоявшийся в ноябре 2020 года, стал не просто очередным обновлением, а настоящей вехой в истории языка. Если версия 7.4 была «работой над ошибками» и добавлением долгожданных типизированных свойств, то 8.0 принесла концептуальные изменения в синтаксис, систему типов и архитектуру.
В этой статье мы подробно разберем, что именно появилось в PHP 8.0 по сравнению с 7.4, как это повлияло на Developer Experience (DX) и какие подводные камни ждут при миграции легаси-кода.
Синтаксический сахар и новый уровень DX
PHP 8.0 избавил разработчиков от тонн шаблонного кода (boilerplate), который годами приходилось писать вручную.
1. Продвижение свойств конструктора (Constructor Property Promotion)
До версии 8.0 объявление DTO (Data Transfer Object) или сущности требовало тройного дублирования: объявить свойство, добавить его в параметры конструктора и присвоить внутри.
Как было в 7.4:
class User {
public string $name;
public int $age;
public function __construct(string $name, int $age) {
$this->name = $name;
$this->age = $age;
}
}
Как стало в 8.0:
class User {
public function __construct(
public string $name,
public int $age
) {}
}
Это изменение колоссально сократило объем кода в моделях Laravel и сущностях Doctrine.
2. Именованные аргументы (Named Arguments)
Теперь при вызове функции можно указывать имена параметров. Это позволяет передавать аргументы в любом порядке и пропускать значения по умолчанию.
// PHP 7.4: чтобы изменить только последний параметр, приходилось передавать все предыдущие
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
// PHP 8.0: передаем только то, что нужно
htmlspecialchars($string, double_encode: false);
3. Оператор безопасного перехода (Nullsafe Operator)
Забудьте о вложенных конструкциях if или громоздких проверках на null. Оператор ?-> работает как ->, но если левая часть равна null, выполнение цепочки прерывается и возвращается null.
// PHP 7.4
$city = $user !== null ? ($user->getAddress() !== null ? $user->getAddress()->getCity() : null) : null;
// PHP 8.0
$city = $user?->getAddress()?->getCity();
4. Выражение Match (Match Expression)
match — это современный, строгий аналог switch. Во-первых, он использует строгое сравнение (===) вместо нестрогого (==). Во-вторых, он возвращает значение, что позволяет использовать его прямо в присваиваниях. В-третьих, не требует break и выбрасывает UnhandledMatchError, если ни одно условие не подошло.
$result = match($statusCode) {
200, 201 => 'OK',
404 => 'Not Found',
default => 'Unknown',
};
Эволюция системы типов
PHP продолжил путь от языка со слабой динамической типизацией к языку со строгой статической типизацией.
Объединенные типы (Union Types)
В 7.4 можно было типизировать свойства, но только одним типом. В 8.0 появилась возможность указывать несколько допустимых типов через вертикальную черту.
public function processId(int|string $id): void { ... }
Примечание: null нельзя было использовать как самостоятельный тип в union (нужно было писать ?int), но это исправили уже в PHP 8.2.
Псевдотип mixed
В PHP 8.0 появился тип mixed, который эквивалентен объединению всех возможных типов (array|bool|callable|int|float|null|object|string). Это идеальное решение для функций, которые действительно могут принимать что угодно.
Возвращаемый тип static
Теперь методы могут возвращать тип static, что означает «экземпляр того же класса, в котором был вызван метод». Это критически важно для паттерна Fluent Interface (цепочки вызовов) и наследования.
class Builder {
public function addCondition(): static {
// ...
return $this;
}
}
Архитектура и метапрограммирование
Атрибуты (Attributes)
Это, пожалуй, самое важное изменение для создателей фреймворков (Symfony, Laravel) и библиотек. До 8.0 метаданные для классов и методов писались в DocBlock-комментариях (/** @ORM\Column */), и фреймворкам приходилось парсить эти строки через Reflection, что было медленно и ненадежно.
В PHP 8.0 появились нативные атрибуты — структурированные метаданные, которые становятся частью AST (абстрактного синтаксического дерева) и легко читаются через Reflection API.
#[Route('/api/users', methods: ['GET'])]
public function getUsers() { ... }
Стандартная библиотека и мелкие, но важные улучшения
Новые функции для работы со строками
Наконец-то ушли в прошлое конструкции вроде strpos($haystack, $needle) !== false. В ядро добавили три интуитивно понятные функции:
str_contains($haystack, $needle)— возвращаетtrue/false.str_starts_with($haystack, $needle)str_ends_with($haystack, $needle)
WeakMap (Слабые карты)
Класс WeakMap позволяет хранить ссылки на объекты в качестве ключей. Главное отличие от обычного массива: если на объект больше нет ссылок в программе, сборщик мусора уничтожит объект и автоматически удалит его из WeakMap. Это спасает от утечек памяти при реализации кэшей и реестров объектов.
throw как выражение
Теперь throw можно использовать там, где ожидаются выражения: в стрелочных функциях, тернарном операторе или при слиянии (??).
$callable = fn() => throw new Exception('Error');
$value = $nullableValue ?? throw new InvalidArgumentException();
Производительность: JIT-компилятор
В PHP 8.0 был внедрен JIT (Just-In-Time) компилятор. Ожидания были заоблачными: многие думали, что WordPress и Битрикс станут работать в 3 раза быстрее.
Обратная совместимость: Что сломалось при переходе с 7.4?
Переход на 8.0 был болезненным для многих легаси-проектов из-за ужесточения правил. Вот главные breaking changes:
Warning) из 7.4 стали ошибками (Error) в 8.0.
1. Сравнение чисел и нечисловых строк
В PHP 7.4 выражение 0 == "foo" возвращало true (строка приводилась к числу 0). В PHP 8.0 логика изменилась: число приводится к строке. Теперь "0" == "foo" возвращает false. Это устранило огромный пласт скрытых багов, но сломало старый код.
2. Строгий порядок аргументов встроенных функций
В 7.4 можно было передавать аргументы в implode() в любом порядке. В 8.0 это вызывает Deprecated, а в 8.0+ строго требует порядка implode(separator, array).
3. Ошибки вместо предупреждений
Многие ситуации, которые в 7.4 выдавали Notice или Warning (например, попытка прочитать несуществующее свойство объекта или деление на ноль), в 8.0 были повышены до Error или ValueError, что сразу роняет приложение, если не обернуто в try-catch.
4. Изменения в match и зарезервированные слова
Слово match стало зарезервированным. Если в вашем коде 7.4 были классы или методы с именем match, их пришлось переименовывать.
Заключение
PHP 8.0 стал мостом между «старым» PHP, который прощал разработчику любые вольности, и «современным» PHP, требующим дисциплины и строгой архитектуры. Внедрение именованных аргументов, атрибутов и продвижения свойств конструктора навсегда изменило то, как пишутся классы в экосистеме. Если вы до сих пор используете паттерны 7.4 в версии 8.0+, вы лишаете себя главных преимуществ современного языка.