PHP 7.4: Головні нововведення

PHP 7.4 — останній мінор 7.x перед гілкою 8.0: у мові з’являються типізовані властивості, arrow functions, обмежена коваріантність/контраваріантність і зручний синтаксис (??=, spread у масивах, роздільники в числах). Паралельно додаються FFI, OPcache preloading і сучасна пара __serialize / __unserialize. Під час міграції тримайте в полі зору: ініціалізацію typed properties, рядкові константи алгоритмів паролів, збірку та розширення (SQLite/Zip більше не в бандлі, перехід на pkg-config).

Зміст


Типізовані властивості

У властивостей класу можна оголошувати типи. Неініціалізовану typed property не можна читати до присвоєння — буде Typed property ... must not be accessed before initialization.

final class User
{
    public int $id;
    public string $name;
    public ?string $nickname = null;
}

З практики:

  • Якщо «немає значення» допустимо — ?Type або значення за замовчуванням.
  • Тип callable для властивостей не підтримується.
  • Об’єктні typed properties мають обмеження на оголошенні — див. мануал.

Arrow functions

fn захоплює змінні з оточення за значенням і повертає один вираз — зручно для коротких колбеків.

$factor = 10;
$nums = array_map(fn($n) => $n * $factor, [1, 2, 3]);

fn — зарезервоване слово: не можна назвати функцію чи клас fn (методи/константи — дозволено).

Коваріантність і контраваріантність

Типи повернення можна звужувати, типи параметрів — розширювати при успадкуванні, з обмеженнями. Повна варіантність краще працює з autoload; в одному файлі дозволені лише нециклічні посилання на класи.

Присвоєння з null coalescing (??=)

Присвоєння виконується, якщо зліва unset або null:

$config['debug'] ??= false;

Оператор spread у масивах

Розпакування ітерованих значень у літералі масиву:

$base = [1, 2];
$all = [0, ...$base, 3]; // [0, 1, 2, 3]

Добре поєднується з array_merge(...$arrays); array_merge() без аргументів повертає [].

Роздільник у числових літералах

Підкреслення покращують читання і не змінюють значення:

$million = 1_000_000;
$hex = 0xFF_FF_FF;

Слабкі посилання (WeakReference)

WeakReference тримає посилання, яке не заважає збирати об’єкт сміттєзбирачем — корисно для кешів.

Винятки з __toString()

Кидати винятки з __toString() дозволено (раніше — fatal). Частина старих recoverable fatals стала Error — перевірте приведення до рядка.

Серіалізація: __serialize / __unserialize

Новий механізм, що поступово витісняє Serializable:

final class Point
{
    public function __serialize(): array
    {
        return ['x' => $this->x, 'y' => $this->y];
    }

    public function __unserialize(array $data): void
    {
        $this->x = $data['x'];
        $this->y = $data['y'];
    }
}

Типи SPL на кшталт ArrayObject можуть записувати нові серіалізовані представлення, читані на 7.4+, але не на старіших PHP.

Розширення і стандартна бібліотека

  • FFI: виклик нативних бібліотек (потрібна модель загроз).
  • OPcache preloading: opcache.preload, часто opcache.preload_user.
  • mb_str_split(): як str_split(), але по кодпойнтах.
  • proc_open(): команда масивом (без shell), дескриптори redirect / null.
  • strip_tags(): дозволені теги масивом імен.
  • PDO: логін/пароль у DSN для більшої кількості драйверів; ?? у SQL екранує літеральний ? (наприклад для JSON-операторів у PostgreSQL).
  • PCRE: preg_replace_callback* приймає прапорці — PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL тощо.
  • Password: Argon2 через sodium без libargon.

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

Безпечні значення за замовчуванням з ??=

$options['timeout'] ??= 30;

Arrow function у сортуванні

usort($rows, fn($a, $b) => $a['score'] <=> $b['score']);

Spread-merge динамічних списків

$merged = array_merge(...$chunks);

Зворотно несумісні зміни (міграційні нотатки)

Ядро

  • Доступ як до масиву до не-масивів: $null["key"] та подібне для null, bool, int, float, resourcenotice.
  • get_declared_classes(): більше не повертає анонімні класи, доки їх не інстанційовано.
  • fn: зарезервоване ключове слово для імен функцій/класів.
  • <?php в кінці файлу без нового рядка: тепер трактується як відкриваючий PHP-тег (раніше залежало від short_open_tag).
  • Stream wrappers: під час include/require по stream може викликатися streamWrapper::stream_set_option(STREAM_OPTION_READ_BUFFER) — реалізуйте метод або повертайте false, щоб не було warning.
  • Серіалізація: формат o прибрано (актуально лише для вручну складених рядків).
  • Константи алгоритмів паролів: PASSWORD_* — це рядки ('2y', 'argon2id', …), не цілі — код з порівнянням з 1/2/3 ламається.
  • htmlentities(): notice, коли кодування зводиться до поведінки як у htmlspecialchars.
  • fread/fwrite: при невдачі повертають false (не ''/0); можливий notice.
  • BCMath: попередження для некоректних числових рядків (раніше часто трактувалися як нуль).
  • CURL: серіалізація CURLFile кидає раніше; нестандартний аргумент curl_version() — попередження/ігнорування.
  • Date: var_dump для DateInterval/DateTime* без «зайвих» властивостей; порівняння DateInterval попереджає і завжди дає false.
  • Intl: за замовчуванням idn_to_ascii / idn_to_utf8 використовують INTL_IDNA_VARIANT_UTS46.
  • MySQLi: прибрано embedded server; прибрано недокументоване $mysqli->stat — користуйтеся mysqli::stat().
  • OpenSSL: openssl_random_pseudo_bytes кидає як random_bytes при помилці; $crypto_strongtrue, якщо винятку немає.
  • PCRE: з PREG_UNMATCHED_AS_NULL хвостові неспівпадіння груп — null (стабільний розмір $matches).
  • PDO: серіалізація PDO/PDOStatement кидає Exception (не PDOException).
  • Reflection: серіалізація reflection-об’єктів кидає; змінено числові значення деяких констант модифікаторів.
  • SPL / ArrayObject: змінена поведінка get_object_vars без STD_PROP_LIST; SplPriorityQueue::setExtractFlags(0) кидає одразу.
  • Tokenizer: несподівані байти стають T_BAD_CHARACTER замість «пробілів» у потоці.
  • Cookie (з 7.4.11): імена вхідних cookie не URL-декодуються.

Deprecated (виправити завчасно)

Акценти:

  • Вкладені тернарні оператори без явних дужок (окрім однозначної форми з вкладенням у середній операнд).
  • Фігурні дужки для offset $str{0} / $arr{0} → використовуйте [].
  • (real) і is_real()(float) / is_float().
  • array_key_exists() для об’єктівisset() / property_exists().
  • implode($parts, $glue) — зворотний порядок → implode($glue, $parts).
  • ReflectionType::__toString() і Reflection*::export() → API на кшталт ReflectionNamedType::getName() і string cast reflection-об’єктів.
  • allow_url_include, FILTER_SANITIZE_MAGIC_QUOTES, застарілі LDAP paged-функції, money_format(), hebrevc() тощо — повний список: migration74 deprecated.

Інші зміни та експлуатація

  • zend.exception_ignore_args: новий INI (за замовчуванням може приховувати аргументи у stack traces — перевірте error reporting).
  • opcache.preload_user: користувач для preload, коли не root.
  • Міграція на pkg-config: багато ./configure-прапорів більше не приймають =DIR; використовуйте PKG_CONFIG_PATH або FOO_CFLAGS/FOO_LIBS.
  • Прибрані бандли: SQLite3, Zip, onig (mbregex) — потрібні системні бібліотеки; hash завжди вбудований.
  • CSV: порожній рядок для $escape вимикає PHP escape-механізм; str_getcsv() узгоджено.
  • GD: imagecropauto() узгоджено з системною libgd; змінено режим за замовчуванням; imagescale() з -1 зберігає пропорції.
  • PEAR: не встановлюється за замовчуванням (--with-pear опційно і deprecated).
  • Продуктивність: opcode для array_key_exists() при статичному розв’язанні; UTF-8 preg_match валідує рядок один раз при повторних викликах.

Підсумок

PHP 7.4 — міст до 8.x: впровадьте typed properties і __serialize/__unserialize заздалегідь; шукайте цілі PASSWORD_*, ім’я fn, синтаксис $var{idx} і крихкий код з ArrayObject; тестуйте streams, OpenSSL random і серіалізацію PDO/Reflection.