Наближається кінець 2025 року — чудовий час, щоб оновити мій вішліст для PHP. Приємно бачити, що деякі пункти з попередніх списків уже стали частиною мови:
Але попереду ще багато роботи. Цікаво, що за останній рік мої пріоритети змінилися. Деякі речі залишилися незмінними (так, я про generics), але дещо я викреслив, а дещо — додав. Давайте розберемося.
До речі, якщо у вас є власні побажання — діліться ними в коментарях.
PHP Editions
Через обіцянку зворотної сумісності (backwards compatibility) у PHP кожна нова функція має бути бездоганною. Якщо вона потрапляє в мову — це назавжди. Саме тому розробка триває так довго, а фінальні рішення часто стають результатом компромісів: internals намагаються передбачити всі можливі edge cases. Свіжий приклад — розширення URI extension:
На обговорення було витрачено майже рік, понад 150 імейлів у розсилці та безліч дискусій у чатах.
І це заради відносно невеликої фічі. До того ж ніхто не застрахований від помилок.
PHP виграв би від впровадження opt-in функцій — можливості вмикати певні фічі лише для конкретного namespace. Це дозволило б додавати зміни, що порушують зворотну сумісність, не впливаючи на існуючий код. Це також дало б змогу тестувати «експериментальні функції».
Іншими словами: ви оновлюєте PHP до останньої версії та вмикаєте breaking changes лише для своєї кодової бази (наприклад, у папці src), не чіпаючи код у vendor. Синтаксис міг би виглядати так:
namespace Tempest { declare(edition=8_5); } namespace App { declare(edition=experimental); }
Ця тема заслуговує на окремий пост, який я скоро напишу. До речі, ідея не нова: Микита Попов пропонував PHP editions ще кілька років тому, натхненний досвідом Rust Editions.
Interface default methods
На втілення цього пункту я сподіваюся найбільше. Раніше вже був відповідний RFC, і деякі розробники internals висловлювали зацікавленість у другій спробі.
Ідея в тому, щоб дозволити інтерфейсам мати дефолтну реалізацію методів безпосередньо в самому інтерфейсі:
interface Request { public array $query { get; } public function hasQuery(string $key): bool { return has_key($this->query, $key); } }
Класи можуть перевизначати ці методи, але не зобов’язані цього робити. Ви запитаєте: «Хіба не для цього існують abstract classes?». Почасти так, але є дві великі різниці:
- Ви можете успадкувати лише один abstract class, але імплементувати багато інтерфейсів.
- Інтерфейси зазвичай самостійні, тоді як глибокі ланцюжки успадкування абстрактних класів часто роблять код надто жорстким і неповоротким.
Класичний приклад проблеми з абстрактними класами — моделі в Laravel:
class BlogPost extends Model { /* … */ } class NewsBlogPost extends Model { /* … */ } class GuestBlogPost extends Model { /* … */ }
Я хотів би бачити NewsBlogPost як підтип BlogPost для повторного використання коду та поліморфізму. Але мій код жорстко прив'язаний до Model, бо інакше він не працюватиме з Laravel ORM. Насправді NewsBlogPost — це не «модель» у чистому розумінні, він просто має відповідати технічним вимогам фреймворку.
Інтерфейси справляються з цим краще:
class BlogPost implements Model { /* … */ } class NewsBlogPost extends BlogPost { /* … */ }
Але в PHP інтерфейс не може містити логіку, тому вам доведеться переписувати методи Model щоразу. Тимчасове рішення — використання traits, але це не завжди зручно. Ось чому я так чекаю на default methods в інтерфейсах.
Generics
Ох, Generics 🥹. Що я можу сказати такого, чого ще не було сказано?
Хороша новина: PHP Foundation продовжує досліджувати це питання. Погана новина: runtime generics все ще вважаються поганою ідеєю. Краща новина: концептуально generics — це інструмент часу компіляції, тому перевірки в рантаймі не обов'язкові. Найгірша новина: ідея ігнорування generics у рантаймі вимагає серйозних змін у філософії PHP, до яких спільнота поки не готова.
Але хто знає — можливо, колись це стане реальністю. На Різдво ж можна мріяти?
$query = new ModelQuery<Post>;
Structs
Останній пункт у моєму списку — structs. Після довгих роздумів над проблемами private(set) та readonly, я дійшов висновку, що більшість моїх болів зникли б, якби в PHP з’явилися прості структури даних.
struct Book { string $title; Author $author; ChapterCollection $chapters; Publisher $publisher; null|DateTimeImmutable $publishedAt = null; }
Зрештою, я просто хочу зручний спосіб представляти структуровані дані. Це поширений кейс, і додати таку можливість було б цілком логічно.
Що я викреслив
Були речі, від яких я відмовився. Наприклад, scalar objects:
$string = ' hello, world '; echo $string->trim()->explode(',');
Це було б круто, але масштаб фічі занадто великий для консервативних комітетів PHP. Існуючі userland-рішення цілком справляються з цим завданням, хоч і з трохи більшим обсягом коду.
Також я мріяв про єдині назви та сигнатури функцій, але цього не станеться ніколи. Можливо, PHP editions відкриють ці двері, але я сумніваюся. Я навчився приймати химерність PHP такою, яка вона є.
Нарешті, clone with. Ми отримаємо його в PHP 8.5, але не зовсім у тому вигляді, який би вирішив мої проблеми. Схоже, цей поїзд уже пішов.
Проте давайте фокусуватися на можливому. Я вірю, що PHP editions — це ключ до прогресу, і сподіваюся, що у 2026 році ми знову повернемося до цього обговорення!