Middle: питання на співбесіді з теми «PHP 8+»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
4 питання
Атрибути (PHP 8.0) - структуровані метадані, прикріплені до класів, методів, властивостей, параметрів, функцій і констант. Синтаксис #[...]. На відміну від анотацій у PHPDoc, атрибути - частина мови: перевіряються синтаксично, мають класи й автодоповнення.
Оголошення атрибута:
#[Attribute(Attribute::TARGET_METHOD)]
final class Route
{
public function __construct(
public string $path,
public string $method = 'GET',
) {}
}
Використання:
final class UserController
{
#[Route('/users', method: 'GET')]
public function index(): Response { /* ... */ }
}
Читання через Reflection:
foreach ((new ReflectionClass(UserController::class))->getMethods() as $method) {
foreach ($method->getAttributes(Route::class) as $attribute) {
$route = $attribute->newInstance(); // створює об'єкт Route з аргументами
$router->add($route->method, $route->path, [$method->class, $method->name]);
}
}
Важливо: атрибут сам нічого не робить. Це лише дані, які хтось має прочитати. Об'єкт атрибута створюється лише при newInstance().
Вбудовані атрибути PHP: #[\Override] (8.3) - перевірка, що метод справді перевизначає батьківський; #[\SensitiveParameter] (8.2) - приховати значення параметра в стек-трейсах (паролі); #[\Deprecated] (8.4); #[\NoDiscard] (8.5) - попередження, якщо результат функції проігноровано; #[\AllowDynamicProperties].
У Laravel: атрибути контейнера (#[Config('app.name')], #[CurrentUser], #[Storage('s3')]), атрибути моделей (#[ObservedBy], #[ScopedBy]), атрибути Livewire (#[Url], #[Locked], #[Computed]).
Продуктивність: читання через Reflection не безкоштовне - фреймворки кешують результат (кеш маршрутів, скомпільований контейнер).
PHP 8.4 (листопад 2024) - головні зміни:
Property hooks - логіка читання й запису властивості без геттерів і сеттерів:
public string $email {
set => mb_strtolower($value);
}
Асиметрична видимість - різні права на читання й запис: public private(set) string $status.
new без дужок у ланцюжку:
$name = new Profile($user)->displayName(); // раніше: (new Profile($user))->displayName()
Нові функції масивів: array_find(), array_find_key(), array_any(), array_all():
$admin = array_find($users, fn (User $u) => $u->isAdmin());
$allPaid = array_all($orders, fn (Order $o) => $o->isPaid());
Лінива ініціалізація об'єктів (lazy objects) - вбудована підтримка ghost- і proxy-об'єктів, що ініціалізуються при першому зверненні. Корисно для ORM і контейнерів.
#[\Deprecated] - позначати власні функції й методи застарілими, і PHP сам видаватиме E_USER_DEPRECATED при виклику.
Інше:
- Новий DOM API з підтримкою HTML5 (
Dom\HTMLDocument). mb_trim(),mb_ucfirst(),mb_lcfirst().- BCMath отримав об'єктний API (
BcMath\Number) з перевантаженням операторів. request_parse_body()- розбір тіла дляPUT/PATCH.
Застарілим стало: неявно nullable параметри - function f(Type $x = null) тепер видає попередження; правильно ?Type $x = null. Це одна з найчастіших змін при оновленні старого коду.
PHP 8.5 (листопад 2025) - головні нововведення:
Оператор конвеєра |> - передає значення ліворуч у callable праворуч, без проміжних змінних і вкладених викликів:
$slug = $title
|> trim(...)
|> mb_strtolower(...)
|> (fn (string $s) => preg_replace('/\s+/', '-', $s));
// замість
$slug = preg_replace('/\s+/', '-', mb_strtolower(trim($title)));
Кожен крок має приймати один параметр, тому функції з кількома аргументами загортають у стрілкову функцію.
clone з новими значеннями - зручні «withers» для незмінних об'єктів, зокрема з readonly-властивостями:
public function withAmount(int $amount): static
{
return clone($this, ['amount' => $amount]);
}
#[\NoDiscard] - PHP попередить, якщо результат функції проігноровано. Захищає від помилок на кшталт виклику методу незмінного об'єкта без присвоєння результату.
URI extension - вбудований розбір і нормалізація URL за RFC 3986 і стандартом WHATWG (Uri\Rfc3986\Uri, Uri\WhatWg\Url) замість неточного parse_url().
Інше:
array_first()іarray_last()- перший і останній елемент без маніпуляцій з внутрішнім вказівником масиву.- Замикання й first-class callables у константних виразах (наприклад, у значеннях атрибутів і параметрів за замовчуванням).
- Стек-трейси для фатальних помилок.
- Постійні cURL share handles - повторне використання з'єднань і DNS-кешу між запитами.
Як оновлюватися: спершу прогнати тести й статичний аналіз на новій версії в CI, переглянути список застарілого в посібнику з міграції, і лише потім оновлювати прод.
Типізовані константи класу (PHP 8.3) - тип можна вказати й для константи:
interface HasVersion
{
const string VERSION = '1.0';
}
class Api implements HasVersion
{
const string VERSION = '2.0'; // ок
// const VERSION = 2; // помилка: константа має бути string
}
Раніше нащадок чи клас, що реалізує інтерфейс, міг перевизначити константу значенням будь-якого типу, і код, що на неї покладався, отримував сюрприз.
#[\Override] - позначає метод, який має перевизначати метод батьківського класу чи інтерфейсу. Якщо такого методу в предка немає, PHP видасть помилку:
class BaseController
{
protected function authorizeRequest(): void {}
}
class UserController extends BaseController
{
#[\Override]
protected function authorizeRequest(): void {} // ок
#[\Override]
protected function authoriseRequest(): void {} // помилка: опечатка, нічого не перевизначено
}
Навіщо #[\Override]:
- Ловить опечатки в назві перевизначеного методу - без атрибута такий метод просто ніколи не викликається фреймворком.
- Ловить зміни в батьківському класі: якщо бібліотека перейменувала чи видалила метод, усі «перевизначення» в нащадках одразу дадуть помилку замість тихого мертвого коду.
- Документує намір для читача.
Ще з PHP 8.3: json_validate() (перевірка без розбору всього документа в пам'ять), динамічне звернення до констант класу (Foo::{$name}), повторна ініціалізація readonly-властивостей у __clone, нові методи Random\Randomizer, mb_str_pad().