PHP 8.1: Основни нововъведения

Път за обновяване от PHP 8.0.x: нови езикови възможности, по-строг runtime и чеклисти за staging.

Съдържание


Ако 8.0 нулира платформата, 8.1 я напълва с ежедневни абстракции: Enums и readonly, Fibers за библиотеки, intersection/never за типове. Триенето идва от по-строг runtime: презапис на $GLOBALS, MySQLi по подразбиране с изключения и deprecations, които в следващите 8.x стават hard errors.

Enums

Enums са първокласен, type-safe начин да представите “затворен” набор от стойности.

  • 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 вместо “string флагове” ('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 могат да бъдат присвоени само веднъж — най-често в конструктора. Това е голям плюс за immutable DTO, value objects и по-сигурни домейн модели.

final class UserProfile
{
    public function __construct(
        public readonly int $id,
        public readonly string $email,
    ) {}
}

Какво променя архитектурно

  • Насърчава обекти “конструираш напълно — после не мутираш”.
  • Намалява нуждата от private + getter-и за DTO-подобни типове.
  • Работи чудесно с 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'), но е по-кратко и се чете по-добре в pipeline-и.

Fibers

Fibers са ниско-нивов примитив за кооперативна конкурентност. Те сами по себе си не правят кода async, но позволяват на библиотеки/фреймворкове да изграждат async runtime-и, scheduler-и и 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 framework/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 означава, че функцията никога не връща управление: тя винаги хвърля exception или извиква 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 контейнер внимавайте с lifetime-а на услугите.)

Синтактични и езикови подобрения (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 uploads).

Практични рецепти

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) дава error). Достъпът до елементи $GLOBALS['x'] продължава да работи.
  • static variables в наследени методи: наследените (не override-нати) методи вече споделят static variables с родителския метод.
  • Optional преди required параметри: optional параметри, декларирани преди required, се третират като required; в PHP 8.1 това може да доведе до ArgumentCountError при извикване (дори с named arguments).
  • Return type compatibility с internal classes: override на вътрешни методи без съвместим return type води до deprecation; за cross-version използвайте #[ReturnTypeWillChange].
  • Нови ключови думи:
    • readonly вече е keyword (но все още може да се използва като име на функция).
    • never вече е reserved word (не може да се използва за имена на class/interface/trait и е забранено в namespaces).

Resource → object migration (още)

Няколко разширения мигрират ресурси към обекти (проверки с is_resource() заменете с проверки за false или с object types):

  • FileInfo: finfo objects вместо fileinfo resources.
  • FTP: FTP\Connection objects.
  • IMAP: IMAP\Connection objects.
  • LDAP: LDAP\Connection, LDAP\Result, LDAP\ResultEntry objects.
  • PgSQL: PgSql\Connection, PgSql\Result, PgSql\Lob objects.
  • PSpell: PSpell\Dictionary и PSpell\Config objects.

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 типове.

Standard library behavior changes

  • version_compare() вече не приема недокументирани съкращения за оператори.
  • HTML escaping функциите по подразбиране използват ENT_QUOTES | ENT_SUBSTITUTE (кавычките се escape-ват; невалиден UTF-8 се заменя).

Deprecated (какво е добре да оправите рано)

Deprecations в 8.1 са “бъдещи грешки”. Ако ги оправите сега, следващите ъпгрейди към PHP 8.2+ ще са много по-лесни.

Core deprecations

  • Подаване на null към non-nullable параметри на built-in функции е deprecated.
  • Неявни lossy float → int конверсии са deprecated (array keys, int type declarations в coercive mode, int оператори).
  • Извикване на static елемент върху trait е deprecated.
  • Autovivification от false е deprecated ($x = false; $x[] = 1;).

Notable extension deprecations

  • Date: strftime() / gmstrftime() и strptime() са deprecated (предпочитайте date() / date_parse_from_format() / IntlDateFormatter).
  • Filter: FILTER_SANITIZE_STRING и FILTER_SANITIZE_STRIPPED са deprecated.
  • Hash: mhash*() са deprecated (използвайте hash_*()).
  • PDO: PDO::FETCH_SERIALIZE е deprecated.

Extensions: важни промени

Тук са събрани “добре е да се знае” промени, които не винаги чупят кода директно, но влияят на поведение и операции.

  • Reflection: ReflectionProperty::setAccessible() / ReflectionMethod::setAccessible() вече нямат ефект (всичко е “достъпно” през Reflection).
  • Phar: SHA256 е default за подписи; поддържат се OpenSSL_SHA256/OpenSSL_SHA512 подписи.
  • OpenSSL: OpenSSL 3.0 е поддържан; наличността на cipher-и и валидирането на параметри са по-строги.
  • CLI: php -a без readline вече дава error.
  • PHPDBG: remote функционалността е премахната.