Junior: питання на співбесіді з теми «Типи й помилки»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
4 питання
=== (тотожність) - true, лише якщо однакові і значення, і тип. == (рівність) спершу приводить операнди до спільного типу, а тоді порівнює.
1 === '1'; // false - int і string
1 == '1'; // true
0 == 'abc'; // false у PHP 8, true у PHP 7
null == false; // true
'1e3' == '1000'; // true - обидва числові рядки
Що змінив PHP 8: порівняння числа з нечисловим рядком тепер порівнює їх як рядки. Раніше 0 == 'abc' було true, бо 'abc' перетворювався на 0 - джерело реальних вразливостей у перевірках паролів і токенів.
Правило: за замовчуванням ===. == - лише коли приведення типів свідомо потрібне, і тоді краще привести явно: (int) $request->input('page') === 1.
Ще пастки ==:
in_array($value, $list)іarray_searchза замовчуванням порівнюють через==. Третій аргументtrueвмикає строге порівняння.switchтеж порівнює через==;match- через===.
У PHP дві гілки «кидабельних» об'єктів, і обидві реалізують інтерфейс Throwable:
Exception- очікувані ситуації, які код програми кидає сам і може обробити: немає файлу, сервіс не відповів, дані не пройшли перевірку.Error- помилки в самому коді чи середовищі, які кидає рушій:TypeError,ArgumentCountError,DivisionByZeroError, виклик методу наnull.
try {
$user->profile->avatar(); // $user->profile === null
} catch (Exception $e) {
// сюди не потрапить: це Error
} catch (Error $e) {
// Call to a member function avatar() on null
}
Що з цим робити:
- Ловіть конкретні класи винятків, які очікуєте:
catch (ConnectionException $e). catch (Throwable $e)- лише на самому верху: в обробнику помилок фреймворку, у воркері черги, щоб записати збій і не впасти цілком.Errorзазвичай не «обробляють», а виправляють: це баг, і його треба побачити в логах, а не проковтнути.
До PHP 7 більшість таких помилок були фатальними й не ловилися взагалі - Error з'явився саме для того, щоб їх можна було перехопити.
$a ?? $b(null coalescing) - повертає$a, якщо воно існує і неnull, інакше$b. Не кидає попереджень на неіснуючих змінних і ключах.$a ?: $b(скорочений тернарний, «elvis») - повертає$a, якщо воно істинне, інакше$b. Для0,'','0',[],falseтеж поверне$b.
$page = $_GET['page'] ?? 1; // ключа немає - 1, без попередження
$page = $_GET['page'] ?: 1; // ключа немає - попередження "Undefined array key"
$discount = 0;
$discount ?? 10; // 0 - нуль не null
$discount ?: 10; // 10 - нуль хибний
$title = '';
$title ?? 'Без назви'; // ''
$title ?: 'Без назви'; // 'Без назви'
Як обирати:
??- «значення за замовчуванням, якщо нічого немає»: ключі масиву, необов'язкові параметри, конфігурація. Найчастіший випадок.?:- коли порожній рядок, нуль чи порожній масив справді треба замінити: показати «Без назви» замість порожнього заголовка.
Присвоєння з ??= (PHP 7.4):
$options['timeout'] ??= 30; // присвоїти, лише якщо ключа немає чи там null
Ланцюжки: $a ?? $b ?? $c - перше значення, що не null. Разом із nullsafe-оператором: $user?->profile?->city ?? 'Невідомо'.
match (PHP 8.0) - вираз, що повертає значення залежно від умови:
$label = match ($status) {
'new', 'pending' => 'Очікує',
'paid' => 'Оплачено',
'cancelled' => 'Скасовано',
};
Відмінності від switch:
- Строге порівняння
===.switchпорівнює через==, тожswitch ('1')спрацює наcase 1, аswitch (0)у PHP 7 - навіть наcase 'abc'. - Повертає значення - його можна присвоїти чи повернути одразу.
switch- інструкція. - Немає провалювання (fall-through) і не потрібен
break. Забутийbreakуswitch- класичний баг. - Необроблене значення - виняток
UnhandledMatchError.switchбезdefaultпросто нічого не робить, і помилка залишається непомітною. - Кілька умов через кому в одній гілці.
match (true) - заміна ланцюжку if/elseif з довільними умовами:
$grade = match (true) {
$score >= 90 => 'A',
$score >= 75 => 'B',
$score >= 60 => 'C',
default => 'F',
};
Обмеження: у кожній гілці - один вираз. Якщо гілці потрібно кілька інструкцій, - викликати метод або лишити if/switch.
У поєднанні з enum match без default - спосіб гарантувати, що кожен варіант оброблено: новий case без гілки дасть UnhandledMatchError, а статичний аналізатор (PHPStan) попередить ще до запуску.