---
title: "Спільна мова для PHP: навіщо стандартній бібліотеці SortDirection і Time\\Duration"
url: https://laravelukraine.com/blog/spilna-mova-dlia-php-navishho-standartnii-biblioteci-sortdirection-i-timeduration
date: 2026-11-04
source: https://thephp.foundation/blog/2026/11/03/stdlib-investment/
---

# Спільна мова для PHP: навіщо стандартній бібліотеці SortDirection і Time\Duration

Це переказ колонки [Тіма Дюстерхуса](https://github.com/timwolla) (Tim Düsterhus, Tideways) у блозі PHP Foundation від 3 листопада 2026 року. Структура й технічні аргументи - авторові, скорочення - наші. Про самі нововведення PHP 8.6 ми вже писали в огляді [«Що нового у PHP 8.6»](https://laravelukraine.com/blog/shho-novogo-u-php-86), тут ідеться про мотивацію, дизайн-рішення і про те, що буде далі.

Усі PHP-проєкти, хоч на повноцінному фреймворку, хоч на мікросервісному з ретельно дібраними бібліотеками, хоч у вигляді розширення для CMS, мають одну спільну річ - стандартну бібліотеку PHP. `str_replace()`, `array_map()`, `DateTimeImmutable` чи `Uri\Rfc3986\Uri` з PHP 8.5 доступні завжди, працюють однаково і означають одне й те саме. Тому стандартна бібліотека - це lingua franca екосистеми: завдяки їй окремі бібліотеки вбудовуються у фреймворки, а розробник переносить досвід з одного проєкту в інший.

Після розширення Random у PHP 8.2, нового DOM API у 8.4 і розширення URI у 8.5 PHP 8.6 поповнює цей словник enum [`SortDirection`](https://wiki.php.net/rfc/sort_direction_enum) і класом [`Time\Duration`](https://wiki.php.net/rfc/duration_class). Обидва маленькі, і це навмисно. Автор пояснює, чому їхня цінність набагато більша за розмір.

### Кожен винаходить колесо

Коли для поняття на кшталт «скільки минуло часу» чи «в який бік сортувати» немає типу в стандартній бібліотеці, кожен проєкт вигадує власний. Окремо кожне таке рішення має сенс. Але коли їх збирають в одному застосунку, на кожній межі між двома бібліотеками з'являється клей-код, а розробникові доводиться вивчати, яким «діалектом» говорить кожна з них.

Сам PHP тут не виняток. Щоб передати «проміжок часу», стандартна бібліотека використовує:

- секунди (`sleep()`);
- мілісекунди (`CURLOPT_TIMEOUT_MS`);
- мікросекунди (`usleep()`);
- наносекунди (`hrtime()`);
- пару цілих чисел - секунди плюс дробова частина (`stream_set_timeout()`);
- числа з рухомою комою (`microtime(true)`).

З напрямком сортування схожа картина. Є цілочисельні константи `SORT_ASC` і `SCANDIR_SORT_ASCENDING`, які працюють лише з певними функціями і ніяк не позначені на рівні типів. Для масивів є пари функцій, що відрізняються лише напрямком. Бібліотеки використовують рядки на кшталт `'ASC'` або параметри типу `bool`, а ті, що орієнтуються на PHP 8.1, створили власні enum. Пошук `SortDirection` на GitHub дав понад 150 різних, взаємно несумісних збігів - і це без enum з іншими назвами, як-от `Order` у Doctrine.

### Два нові слова у словнику

`SortDirection` - enum рівно з двома варіантами (`Ascending` і `Descending`) без жодної поведінки. Він повністю описується чотирма рядками:

```php
enum SortDirection {
  case Ascending;
  case Descending;
}
```

Написати такий у своїй бібліотеці - дрібниця. Але щоб поєднати кілька бібліотек, кожна з яких має свій `SortDirection`, потрібні перехідники між однаковими за змістом enum. Оскільки в PHP 8.6 `SortDirection` доступний завжди, бібліотеки можуть сміливо використовувати його в публічних API, а в інше подання перетворювати лише тоді, коли треба серіалізувати значення для SQL-запиту чи HTTP API:

```php
$query->orderBy('created_at', SortDirection::Descending);
```

`Time\Duration` - це проміжок часу, який показав би секундомір: від однієї наносекунди до понад ста років, з наносекундною точністю на всьому діапазоні. Іменовані конструктори прямо називають одиницю, тож більше не треба гадати, у чому параметр `$timeout` чекає значення - у секундах, мілісекундах чи фазах Місяця. Для читання є властивості `->seconds` і `->nanoseconds` та окремий прапорець `->negative`, без «магічних чисел».

Першим API, яке приймає `Time\Duration`, став новий polling API з [проєкту Streams](https://thephp.foundation/blog/2025/10/30/php-streams-evolution/), що теж входить у PHP 8.6:

```php
$events = $pollContext->wait(Time\Duration::fromMilliseconds(250));
```

### Фундамент для наступних кроків

`Time\Duration` корисний і сам по собі, але задуманий як перша цеглина нового API для дати й часу, побудованого на можливостях сучасного PHP. Наступна частина вже [обговорюється](https://news-web.php.net/php.internals/132588) у PHP Internals: [RFC Time\Instant і Time\Clock](https://wiki.php.net/rfc/time_instant_class) пропонує клас `Time\Instant` для моментів у часі та інтерфейс `Time\Clock`.

Саме інтерфейс `Clock` найцікавіший для тих, хто пише тести. Код, який сам питає системний час, важко тестувати: результат залежить від того, коли запущено тест. Якщо ж такий код отримує годинник ззовні через інтерфейс, у тесті можна підставити свою реалізацію з потрібним моментом часу. Стандартний інтерфейс означає, що бібліотеки й фреймворки зможуть домовитися про одну точку підміни замість кожен свою.

Дві частини API складаються, як шестерні: відстань між двома `Time\Instant` - це `Time\Duration`, а щоб зсунути момент у часі, передають `Time\Duration`.

### Чому це варте інвестиції

Enum на чотири рядки і клас із трьома властивостями та кількома методами можна написати самому за пів дня, ні з ким не домовляючись. Навіщо довге обговорення RFC? Автор вважає, що саме така робота окупається найбільше: вона дає екосистемі спільну опору, яка замінює власні реалізації, а не просто ще один спосіб написати те, що вже існує.

Попит був і раніше: понад 150 варіантів enum, кілька бібліотек і розширень із класом-секундоміром. Усі вони з добрих намірів розв'язують ту саму задачу - і всі несумісні між собою. Одне обговорення дизайну в PHP Internals, хоч і дорожче на старті, замінює всі ці зусилля і дає екосистемі те, до чого можна зійтися.

### Планування на десятиліття вперед

Для стандартної бібліотеки правильний дизайн важить більше, ніж для будь-якого пакета. Через жорстку обіцянку зворотної сумісності PHP другої спроби майже не буває: щойно щось потрапило у стабільний випуск, навіть найменша несумісна зміна зачіпає тисячі користувачів.

Простори імен дають рідкісну можливість для другої спроби, бо значна частина стандартної бібліотеки з'явилася раніше за них. Новий API може оселитися в порожньому просторі імен, не чіпаючи поведінки старого, а користувачі переходять на нього у власному темпі. Так уже зробили з Random, DOM і URI (`Random\*`, `Dom\*` і `Uri\*` замість `mt_rand()`, `DOM*` і `parse_url()`).

Тому RFC свідомо роблять маленькі кроки з чітко описаним майбутнім обсягом робіт. Кожне сьогоднішнє рішення обмежує наступні, а деякі шляхи може закрити зовсім. Маленький правильний API, спроєктований з думкою про розвиток, легко розширити. Великий, але трохи хибний API муляє десятиліттями.

На практиці це виглядало так. Тім Дюстерхус і Дерік Ретанс (Derick Rethans), мейнтейнер ext/date і підрядник PHP Foundation, хотіли, щоб новий polling API приймав тайм-аут як `Time\Duration`, а не парою `$timeoutSeconds` і `$timeoutMicroseconds`, і встигнути до feature freeze PHP 8.6, після якого сигнатура стала б остаточною. Від першої пропозиції RFC лишалося близько двох місяців.

Невдалий API в одному методі - локальна проблема. А невдалий будівельний блок, який використовуватимуть усюди, розв'язав би малу проблему ціною великої. Тож пропозиція обмежилася двома видами функціональності:

- тим, у чому автори були впевнені, з перестраховкою;
- тим, на що спиратимуться майбутні доповнення і що не можна додати пізніше, не зламавши наявних очікувань.

Підтримка від'ємних тривалостей належить саме до другого виду. Якби перша версія `Time\Duration` вважала тривалість завжди невід'ємною, код почав би на це покладатися, і додати від'ємні значення згодом без поломки вже не вдалося б. Тому прапорець `->negative` закладено одразу, а все зручне, але необов'язкове, автори прямо попросили спільноту PHP Internals не обговорювати і залишили на майбутнє. У результаті вийшов клас для загального поняття, корисний уже сьогодні, навіть якщо наступні частини так і не з'являться, і з місцем для `Time\Instant` та всього, що буде далі.

### Як зробити це сталим

Така робота не безкоштовна. Авторам RFC треба дослідити тему, запропонувати дизайн і вести обговорення. Рецензентам - читати RFC, ділитися досвідом і, можливо, втретє сперечатися про назви, поки не дійдуть згоди. Реалізацію треба написати, перевірити і підтримувати ще довго. Автор відкрито пише, що працює в Tideways - компанії-засновниці та срібному спонсорі PHP Foundation, яка оплатила частину його часу на RFC `SortDirection`, `Time\Duration` і `Time\Instant`. А PHP Foundation фінансує розробників, як-от Деріка Ретанса, і це значною мірою пояснює, чому стандартна бібліотека PHP останніми роками стає дедалі кращою.

### Що може зробити кожен

- **Спробувати.** [Тестуйте PHP у період пре-релізів](https://thephp.foundation/blog/2026/08/17/how-to-contribute-to-php/#1.-test-prereleases), зосереджуючись на нових можливостях на кшталт `Time\Duration`, і повідомляйте про знайдені проблеми, щоб нові функції добре себе показали після стабільного випуску.
- **Прийняти.** Якщо ви підтримуєте бібліотеку, розширте її API, щоб воно приймало нові типи. Union type у параметрі дозволяє зробити це без несумісних змін - користувачі отримають зрозумілішу термінологію і лишаться на звичних бібліотеках.
- **Вплинути.** Долучайтеся до обговорення RFC, наприклад [того, що про Time\Instant](https://news-web.php.net/php.internals/132588). Цінна будь-яка думка, велика чи мала.
- **Профінансувати.** Якщо на перші три пункти бракує часу, можна оплатити час інших - наприклад, переконати роботодавця стати [спонсором PHP Foundation](https://thephp.foundation/sponsor/). Кожне покращення самого PHP багаторазово окупається зручнішою розробкою.

Для Laravel-розробників це теж не абстракція: щойно `SortDirection` і `Time\Duration` з'являться в API фреймворків і пакетів, на межах між ними поменшає перетворень рядків і чисел туди й назад.
