PHP: типи й помилки
20 питань · ~20 хв · Версія v3.0
Увійдіть, щоб продовжити
Строгі типи й приведення, порівняння й оператори, union і перетинні типи, винятки, finally і обробники помилок - питання всіх рівнів, від junior до lead.
- За спробу
- 20
- У пулі
- 62
- Проходжень
- 0
- Середній бал
- -
- Пройшли на 70%+
- -
Питання для підготовки
20 питаньPHP дозволяє оголошувати типи аргументів, повернення та властивостей - і перевіряє їх у рантаймі.
class VacancyService
{
public function __construct(
private VacancyRepository $repository,
) {
}
public function publish(int $id, ?string $comment = null): Vacancy
{
// ...
}
}
Що дає: помилка ловиться в момент виклику, а не через три шари; редактор підказує методи; статичний аналіз бачить невідповідності до запуску.
declare(strict_types=1) змінює поведінку перевірки. Без нього PHP приводить типи мовчки:
function repeat(int $times): string { /* ... */ }
repeat('5'); // без strict_types: '5' стане 5, викликається нормально
repeat('5'); // зі strict_types: TypeError
Саме мовчазне приведення небезпечне: 'abc' перетвориться на 0, а '5 котів' - на 5, і помилка проявиться далеко від місця, де виникла.
Важлива деталь: директива діє на файл, де вона написана, і стосується викликів з нього, а не в нього. Тому її ставлять у кожен файл, першим рядком після <?php.
Корисні типи PHP 8:
?string- рядок абоnull.int|string- обʼєднання типів.never- функція не повертає керування (кидає виняток чи завершує процес).
У Laravel-проєктах declare(strict_types=1) зазвичай вимагається стилем коду й перевіряється Pint.
Обидва об'єднують масиви, але по-різному поводяться з однаковими ключами:
$a + $b- бере все з$a, а з$bдодає лише ключі, яких у$aнемає. Перемагає лівий.array_merge($a, $b)- для рядкових ключів перемагає правий, а числові ключі перенумеровує з нуля й дописує в кінець.
$defaults = ['color' => 'red', 'size' => 'M'];
$options = ['color' => 'blue'];
$options + $defaults; // ['color' => 'blue', 'size' => 'M']
array_merge($defaults, $options); // ['color' => 'blue', 'size' => 'M']
[0 => 'a', 1 => 'b'] + [0 => 'c', 1 => 'd', 2 => 'e']; // ['a', 'b', 'e']
array_merge(['a', 'b'], ['c', 'd', 'e']); // ['a', 'b', 'c', 'd', 'e']
Коли що:
+- накласти значення за замовчуванням: «мої опції, а чого бракує - з дефолтів».array_mergeабо spread[...$a, ...$b]- склеїти списки.
Пастка: числові ключі, які насправді ідентифікатори ([15 => 'Київ']), array_merge перенумерує й загубить. Для таких масивів потрібен + або array_replace.
Через usort і оператор <=> («космічний корабель»), який повертає -1, 0 або 1. Масиви теж можна порівнювати через <=> - поелементно, зліва направо, і це дає сортування за кількома полями одним рядком:
usort($users, fn (array $a, array $b): int =>
[$b['score'], $a['name']] <=> [$a['score'], $b['name']]
);
Тут спершу за score за спаданням (тому $b і $a поміняні місцями), а за однакового рахунку - за name за зростанням.
Що ще варто знати:
usortскидає ключі. Щоб зберегти їх, потрібенuasort.- З PHP 8.0 сортування стабільне: рівні за критерієм елементи лишаються в початковому порядку. На цьому можна будувати сортування в кілька проходів.
- Рядки
<=>порівнює побайтово, тож «Ярослав» і «Андрій» стануть не за абеткою. Для тексту людською мовою -Collatorз розширення intl.
У Laravel колекції роблять те саме читабельніше: collect($users)->sortBy([['score', 'desc'], ['name', 'asc']]).
=== (тотожність) - 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 з'явився саме для того, щоб їх можна було перехопити.
Спершу почитати
Прочитати - ще не значить знати
20 питань, по одному на екран, ~20 хв. Після завершення - розбір кожної помилки з посиланням на питання.