PHP 8.1: Главные нововведения
Путь обновления с PHP 8.0.x: новые возможности языка, ужесточение рантайма и чеклисты для staging.
Содержание (Оглавление)
- Enums
- Readonly properties
- First-class callable syntax
- Fibers
- Intersection types
- never return type
- new in initializers
- Синтаксические и языковые улучшения (0o, unpack, named args после unpack)
- Обратно несовместимые изменения (миграционные заметки)
- Deprecated (что лучше исправить заранее)
- Extensions: важные изменения
Если 8.0 «перезапустил платформу», то 8.1 наполняет язык смыслом в коде: Enums и readonly становятся повседневностью, Fibers дают библиотекам примитив кооперативной конкурентности, intersection и never дополняют типовую картину. Трение появится там, где рантайм стал строже: переприсваивание $GLOBALS, MySQLi по умолчанию с исключениями и длинный хвост deprecations, которые в следующих 8.x превратятся в ошибки.
Enums
Enums — это типобезопасный способ представлять “закрытый” набор значений.
- Backed enums хранят скалярное значение (
string|int), удобное для хранения/передачи по API. - Pure enums — только идентичность, хорошо подходят для доменных состояний и исчерпывающей обработки.
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
case Archived = 'archived';
}
function canEdit(Status $status): bool
{
return $status !== Status::Archived;
}
$status = Status::from('draft'); // Status::Draft
$value = $status->value; // 'draft'
Практические рекомендации
- Используйте enums вместо “строковых флагов” (
'draft'|'published') в сигнатурах и DTO. - Для БД и публичных API обычно удобнее backed enums; для внутренних workflow — pure enums.
- Комбинируйте с
matchдля исчерпывающей обработки:
function label(Status $status): string
{
return match ($status) {
Status::Draft => 'Draft',
Status::Published => 'Published',
Status::Archived => 'Archived',
};
}
Readonly properties
Readonly properties можно присвоить только один раз — обычно в конструкторе. Это большой шаг для иммутабельных DTO, value objects и более безопасных доменных моделей.
final class UserProfile
{
public function __construct(
public readonly int $id,
public readonly string $email,
) {}
}
Что меняется архитектурно
- Подталкивает к объектам “сконструировали полностью — дальше не мутируем”.
- Для DTO-подобных сущностей уменьшает необходимость в private + getter’ах.
- Отлично сочетается с constructor property promotion.
First-class callable syntax
PHP 8.1 добавляет удобный синтаксис для создания closures из callables:
function normalize(string $s): string
{
return strtolower(trim($s));
}
$fn = normalize(...); // Closure
$out = array_map($fn, [' A ', 'B ']); // ['a', 'b']
Это эквивалент Closure::fromCallable('normalize'), но короче и лучше читается в пайплайнах.
Fibers
Fibers — низкоуровневый примитив для кооперативной конкуррентности. Он сам по себе не делает код async, но позволяет библиотекам и фреймворкам строить async-рантаймы, планировщики и “structured concurrency”.
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('paused');
echo "resumed with: {$value}\n";
});
echo $fiber->start() . "\n"; // paused
$fiber->resume('payload'); // resumed with: payload
Когда это важно
- Если вы используете async-фреймворк/event loop: Fibers могут существенно упростить интеграции.
- В классических request/response приложениях Fibers чаще остаются “инфраструктурой для библиотек”.
Intersection types
Intersection types выражают “должно удовлетворять всем этим интерфейсам/классам одновременно”.
function export(iterable&Countable $items): array
{
if (count($items) === 0) {
return [];
}
return iterator_to_array($items, preserve_keys: true);
}
Важно: intersection types нельзя комбинировать с union types.
never return type
Return type never означает, что функция никогда не возвращает управление: она всегда бросает исключение или вызывает exit.
function fail(string $message): never
{
throw new RuntimeException($message);
}
Это улучшает статический анализ и делает control flow явным.
new in initializers
PHP 8.1 разрешает new выражения в большем числе мест: defaults параметров, static variables, инициализация констант и аргументы атрибутов.
final class Clock
{
public function now(): DateTimeImmutable
{
return new DateTimeImmutable('now');
}
}
function handler(Clock $clock = new Clock()): DateTimeImmutable
{
return $clock->now();
}
Это убирает много “factory boilerplate” в простых случаях. (В DI-контейнере аккуратно с временем жизни сервисов.)
Синтаксические и языковые улучшения (0o, unpack, named args после unpack)
Octal integer literal prefix: 0o / 0O
$mask = 0o755;
Array unpacking with string keys
$a = [1, 'a' => 'b'];
$b = [...$a, 'c' => 'd']; // [1, 'a' => 'b', 'c' => 'd']
Named argument after argument unpacking
foo(...$args, named: $arg);
full_path for file uploads
В $_FILES теперь появляется ключ full_path (в первую очередь для загрузок webkitdirectory).
Практические рецепты
Backed enum из ненадёжного ввода (tryFrom)
$status = Status::tryFrom($row['status']) ?? Status::Draft;
Пайплайны с first-class callable
$normalize = static fn(string $s): string => strtolower(trim($s));
$mapper = $normalize(...);
$lines = array_map($mapper, $raw);
Обратно несовместимые изменения (миграционные заметки)
Этот раздел намеренно практический: он фокусируется на вещах, которые чаще всего реально ломают апгрейд.
PHP core / language
- Ограничения на запись в $GLOBALS: запись в “целый”
$GLOBALSбольше не поддерживается (например,array_pop($GLOBALS)— ошибка). Доступ к элементам$GLOBALS['x']по‑прежнему работает. - static variables в унаследованных методах: унаследованные (не переопределенные) методы теперь разделяют static variables с родительским методом.
- Optional перед required параметрами: optional параметры, объявленные до required, считаются required; в PHP 8.1 это может приводить к
ArgumentCountErrorпри вызове (даже с named arguments). - Return type compatibility с internal classes: переопределение внутренних методов без совместимого return type вызывает deprecation; для кросс-версии используйте
#[ReturnTypeWillChange]. - Новые ключевые слова:
readonlyтеперь keyword (но все еще можно использовать как имя функции).neverтеперь reserved word (нельзя использовать для имен class/interface/trait и запрещено в namespaces).
Resource → object migration (продолжается)
Несколько расширений перевели ресурсы в объекты (проверки is_resource() нужно заменить на проверки на false или на object types):
- FileInfo:
finfoobjects вместоfileinforesources. - FTP:
FTP\Connectionobjects. - IMAP:
IMAP\Connectionobjects. - LDAP:
LDAP\Connection,LDAP\Result,LDAP\ResultEntryobjects. - PgSQL:
PgSql\Connection,PgSql\Result,PgSql\Lobobjects. - PSpell:
PSpell\DictionaryиPSpell\Configobjects.
PDO / MySQLi behavior changes
- MySQLi: default error reporting mode изменился с “silent” на “exceptions”. Чтобы вернуть старое поведение:
mysqli_report(MYSQLI_REPORT_OFF); - PDO: изменилось поведение
PDO::ATTR_STRINGIFY_FETCHESдля bool; драйверы SQLite/MySQL стали стабильнее возвращать ints/floats как нативные PHP-типы.
Изменения в стандартной библиотеке
version_compare()больше не принимает недокументированные сокращения операторов.- HTML escaping функции теперь по умолчанию используют
ENT_QUOTES | ENT_SUBSTITUTE(кавычки экранируются; некорректный UTF-8 заменяется).
Deprecated (что лучше исправить заранее)
Deprecations в 8.1 — это “будущие ошибки”. Исправив их сейчас, вы сильно упростите переходы на PHP 8.2+.
Core deprecations
- Передача
nullв non-nullable параметры встроенных функций — deprecated. - Неявные “потерянные” float → int конверсии — deprecated (ключи массивов, int type declarations в coercive mode, int-операторы).
- Вызов static элемента на trait — deprecated.
- Autovivification из
false— deprecated ($x = false; $x[] = 1;).
Важные deprecations расширений
- Date:
strftime()/gmstrftime()иstrptime()deprecated (предпочитайтеdate()/date_parse_from_format()/IntlDateFormatter). - Filter:
FILTER_SANITIZE_STRINGиFILTER_SANITIZE_STRIPPEDdeprecated. - Hash:
mhash*()deprecated (используйтеhash_*()). - PDO:
PDO::FETCH_SERIALIZEdeprecated.
Extensions: важные изменения
Здесь собраны “полезно знать” изменения, которые не всегда ломают код напрямую, но влияют на поведение и эксплуатацию.
- Reflection:
ReflectionProperty::setAccessible()/ReflectionMethod::setAccessible()больше не имеют эффекта (все считается доступным через Reflection). - Phar: SHA256 используется по умолчанию для подписей; поддержаны OpenSSL_SHA256/OpenSSL_SHA512.
- OpenSSL: поддержан OpenSSL 3.0; доступность шифров и валидация параметров стали строже.
- CLI:
php -aбез readline теперь приводит к ошибке. - PHPDBG: удален remote функционал.