---
title: "PHP 8.7: які RFC обговорюють у жовтні і за що вже голосують"
url: https://laravelukraine.com/blog/php-87-iaki-rfc-obgovoriuiut-u-zovtni-i-za-shho-vze-golosuiut
date: 2026-10-28
---

# PHP 8.7: які RFC обговорюють у жовтні і за що вже голосують

PHP 8.6 виходить у листопаді, і що в ньому буде, ми [вже розбирали](https://laravelukraine.com/blog/shho-novogo-u-php-86). Тим часом на [сторінці RFC](https://wiki.php.net/rfc) з'являються пропозиції для наступної версії, PHP 8.7. Нижче - те, що потрапило туди в жовтні 2026 року і може торкнутися щоденного коду. Важливо: усе описане - пропозиції. Два RFC уже на голосуванні, решта ще обговорюється, і будь-яку з них можуть змінити або відхилити. Стан голосувань наведено на 28 жовтня.

### На голосуванні: винятки для hexdec() і base_convert()

Функції `bindec()`, `octdec()`, `hexdec()` і `base_convert()` досі мовчки ігнорують символи, яких немає в системі числення. `hexdec('z')` повертає `0`, а з PHP 7.4 ще й показує `E_DEPRECATED`. Тоді ж планували в PHP 8 перейти до винятку, але цей крок так і не зробили. [RFC Сьорда Лангкемпера](https://wiki.php.net/rfc/throw_error_for_invalid_characters_for_number_base) його завершує:

```php
var_dump(hexdec('z'));
// PHP 7.4 - 8.6: Deprecated + int(0)
// PHP 8.7 (пропозиція): Uncaught Exception: Invalid characters passed for attempted conversion

base_convert('f', 16, 10); // гаразд, 'f' є в шістнадцятковій системі
base_convert('f', 15, 10); // Exception
```

Зверніть увагу на тип: це звичайний `Exception`, а не `ValueError`. Автор пояснює, що невалідний символ на вході не обов'язково означає баг у програмі, тож `Error` тут не пасує. Більш специфічний виняток лишили на майбутнє.

Це справжній BC break: код, що покладається на мовчазне відкидання символів (наприклад, `hexdec('1234-5678')`), почне падати. Голосування триває до 1 листопада, потрібна більшість у 2/3. На 28 жовтня рахунок 24 «за» і 1 «проти», тож прийняття виглядає майже певним.

### На голосуванні: енуми Filter\Filter і Filter\Flag

[Другий RFC](https://wiki.php.net/rfc/invalid_filter_options) пропонує типізовану альтернативу цілочисловим константам `FILTER_*`: backed-енуми `Filter\Filter` для фільтрів і `Filter\Flag` для прапорців. Функції `filter_var()`, `filter_var_array()`, `filter_input()` і `filter_input_array()` прийматимуть і їх, і старі константи:

```php
filter_var('123', Filter\Filter::VALIDATE_INT, Filter\Flag::ALLOW_OCTAL);

// кілька прапорців - через їхні значення
filter_var(
  '123',
  Filter\Filter::VALIDATE_INT,
  Filter\Flag::ALLOW_OCTAL->value | Filter\Flag::ALLOW_HEX->value
);
```

BC break немає: константи лишаються. Але шанси в RFC слабкі. Голосування триває до 4 листопада, і на 28 жовтня рахунок 2 «за» і 11 «проти» при потрібних 2/3.

### Коментарі й висячі коми в JSON

[RFC Отара Чекурішвілі](https://wiki.php.net/rfc/json_comments_trailing_commas) додає два незалежні прапорці для `json_decode()` і `json_validate()`: `JSON_ALLOW_COMMENTS` (коментарі `//` і `/* */`, але не `#`) і `JSON_ALLOW_TRAILING_COMMAS` (одна кома після останнього елемента масиву чи об'єкта):

```php
$config = <<<'JSON'
{
  // підключення для локальної розробки
  "host": "localhost",
  "port": 5432, /* стандартний порт PostgreSQL */
  "options": ["persistent", "utf8",],
}
JSON;

$data = json_decode($config, true, flags: JSON_ALLOW_COMMENTS | JSON_ALLOW_TRAILING_COMMAS);
```

Без прапорців поведінка не змінюється, а `json_encode()` коментарів і ком ніколи не пише. Це зручно для конфігів, які редагують люди: зараз для цього тягнуть окрему бібліотеку або вирізають коментарі регулярками. Голосування буде окремим для кожного прапорця.

### ext/csv на заміну SplFileObject

PHP 8.6 задепрекейтив CSV-методи `SplFileObject`, але заміни не запропонував. [RFC про ext/csv](https://wiki.php.net/rfc/csv_extension) на основі розширення `girgias/csv` Джини Пітер Баньярд додає простір імен `Csv\`: функції `array_to_row()`, `row_to_array()`, `collection_to_buffer()`, `buffer_to_collection()`, `buffer_to_collection_lax()`, `collection_to_file()` і клас `LazyLaxCollection`:

```php
Csv\collection_to_file($file, $rows);

foreach (Csv\LazyLaxCollection::createFromFile($file) as $row) {
  echo implode(' | ', $row), "\n";
}
```

На відміну від `fgetcsv()`, тут немає власного механізму екранування, розбір не залежить від локалі, а роздільники можуть бути багатобайтовими. Парсер строго дотримується RFC 4180 і на некоректному вході кидає `ValueError` замість здогадок. Одна деталь для імпорту файлів: за замовчуванням кінець рядка - `"\r\n"`, і файл лише з LF треба читати з явним `"\n"`, інакше буде помилка.

### array_partition() і PASSWORD_BCRYPT_SHA256

[`array_partition()`](https://wiki.php.net/rfc/array_partition) ділить масив на дві частини за колбеком - те, що Laravel-розробники давно роблять через `Collection::partition()`. Колбек отримує значення і ключ, ключі зберігаються, першою йде частина, де колбек повернув істину:

```php
[$adults, $minors] = array_partition($users, fn($u) => $u->age >= 18);
```

Якщо у вашому коді є власна глобальна `array_partition()` без перевірки `function_exists()`, після прийняття RFC буде помилка «Cannot redeclare function».

[`PASSWORD_BCRYPT_SHA256`](https://wiki.php.net/rfc/bcrypt_sha256) - новий алгоритм для `password_hash()`. Звичайний bcrypt мовчки обрізає пароль на 72 байтах, а пропозиція спершу пропускає його через HMAC-SHA256. Формат узято з Python-бібліотеки Passlib, тож хеші сумісні в обидва боки. `PASSWORD_DEFAULT` і `PASSWORD_BCRYPT` не змінюються, алгоритм вмикається явно:

```php
$hash = password_hash($password, PASSWORD_BCRYPT_SHA256);
password_verify($password, $hash); // true
```

Laravel за замовчуванням хешує паролі через bcrypt, тож тема знайома. Щоб скористатися новим алгоритмом через `Hash`, знадобиться підтримка з боку фреймворку - про неї поки нічого не відомо.

### Єдиний бінарник для CLI і FPM та клас Scope

Два RFC стосуються не так коду, як того, де він працює. [Пропозиція Матьє Наполі](https://wiki.php.net/rfc/single-binary-cli-fpm), автора Bref, вбудовує CLI у виконуваний файл `php-fpm`: викликаний під ім'ям `php` (наприклад, через симлінк), він працює як CLI. За його вимірами на AWS Lambda розпакований шар зменшується на 28%, а холодний старт із FPM пришвидшується на 70 мс. Для Docker-образів, де CLI запускає міграції перед стартом FPM, це теж може бути відчутно.

[RFC `Scope`](https://wiki.php.net/rfc/scope) Фроде Бьорлі пропонує фінальний клас, який позначає одиницю роботи: HTTP-запит, завдання з черги, CLI-команду. Сервер запускає кожен запит у власному scope, а код будь-де всередині може спитати, до якого запиту він належить:

```php
(new Scope())->run($handler, $request);

$this->connections ??= new WeakMap();
$db = $this->connections[Scope::getCurrent()] ??= new PDO($dsn);
```

Файбери й генератори успадковують scope, у якому їх створено. Два додаткові голосування пропонують прив'язати до scope суперглобальні змінні й сесію, а також локаль і часовий пояс за замовчуванням. Для Octane і FrankenPHP, де один процес обслуговує багато запитів, це саме та проблема, яку зараз розв'язують скиданням стану між запитами. Глобальний клас `Scope` більше не можна буде оголосити, але скопи Eloquent (`Illuminate\Database\Eloquent\Scope`) живуть у своєму просторі імен і не постраждають. Обговорення почалося лише 26 жовтня, тож RFC ще точно змінюватиметься.

### Що робити вже зараз

Реально підготуватися варто лише до першого RFC. Пошукайте в логах `E_DEPRECATED` з текстом «Invalid characters passed for attempted conversion» і подивіться, чи не передаєте ви у `hexdec()` чи `base_convert()` дані користувача: кольори, ідентифікатори, рядки з роздільниками. Такі місця краще валідувати до виклику, ніж ловити `Exception`.

Решта - пропозиції в обговоренні: дати голосувань ще не призначені, а тексти змінюються мало не щотижня. Якщо одна з них вам болить (CSV-імпорт, довгі паролі, стан у довгоживучих воркерах), найкраще місце для відгуку - [розсилка internals](https://news-web.php.net/php.internals), поки RFC не дійшли до голосування.
