Middle: питання на співбесіді з теми «Масиви й рядки»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
4 питання
Обидва об'єднують масиви, але по-різному поводяться з однаковими ключами:
$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']]).
PHP використовує бібліотеку PCRE: функції preg_* приймають шаблон з роздільниками (/.../, ~...~, #...#) і модифікаторами після них.
preg_match('/^\d{4}-\d{2}-\d{2}$/', $date); // 1, 0 або false при помилці
preg_match('/(?<year>\d{4})-(?<month>\d{2})/', $s, $m); // $m['year'], $m['month']
preg_match_all('/#(\w+)/u', $text, $matches); // усі хештеги
preg_replace('/\s+/', ' ', $text); // стиснути пробіли
preg_replace_callback('/\d+/', fn ($m) => $m[0] * 2, $s);
preg_split('/[,;]\s*/', $list);
Модифікатор u вмикає режим UTF-8:
- шаблон і рядок обробляються як UTF-8, а не як байти;
.відповідає одному символу, а не байту;- працюють Unicode-класи:
\p{L}(будь-яка літера),\p{Lu}(велика),\p{Cyrillic}.
Без u шаблон /^.{3}$/ для «кіт» не спрацює (там 6 байтів), а \w не вважає кирилицю літерами.
preg_match('/^[\p{L}\s\'-]+$/u', "Мар'яна Іваненко"); // 1
Інші корисні модифікатори: i - без урахування регістру, m - ^/$ для кожного рядка, s - . охоплює й переведення рядка, x - пробіли й коментарі в шаблоні для читабельності.
Пастки:
- Помилка ≠ збіг:
preg_matchповертаєfalseпри помилці (зламаний UTF-8 у рядку зu, перевищення ліміту backtracking). Причина - уpreg_last_error_msg(). - Катастрофічний backtracking: шаблони на кшталт
(a+)+$на зловмисному введенні виконуються експоненційно довго (ReDoS). Уникайте вкладених квантифікаторів. - Екранування введення користувача в шаблоні - через
preg_quote($input, '/'). - Для простих випадків швидші й зрозуміліші
str_contains,str_starts_with,str_replace.
Деструктуризація - розпакування масиву в окремі змінні (короткий синтаксис замість list()):
[$first, $second] = [10, 20];
[, $second] = [10, 20]; // пропустити елемент
['id' => $id, 'name' => $name] = $user; // за ключами
[$a, [$b, $c]] = [1, [2, 3]]; // вкладене
[$a, $b] = [$b, $a]; // обмін значеннями
foreach ($rows as ['id' => $id, 'email' => $email]) {
// ...
}
Якщо ключа немає, змінна отримає null з попередженням.
Spread-оператор ... розпаковує масив у місці виклику чи в літералі масиву:
$all = [...$defaults, ...$overrides]; // склеїти масиви
$merged = [...['a' => 1], ...['a' => 2, 'b' => 3]]; // ['a' => 2, 'b' => 3] (PHP 8.1+)
function sum(int ...$numbers): int // збір аргументів (variadic)
{
return array_sum($numbers);
}
sum(...[1, 2, 3]); // передача масиву як аргументів
Що варто знати:
- Рядкові ключі в spread дозволені з PHP 8.1 і поводяться як
array_merge: пізніший ключ перемагає. Числові ключі перенумеровуються. - Spread ітератора:
[...$generator]працює з будь-якимTraversable. - Іменовані аргументи зі spread:
create(...['name' => 'Оля', 'email' => '...'])(PHP 8.1+) - ключі масиву стають іменами параметрів. - Не для великих масивів у циклі:
$result = [...$result, $item]у циклі щоразу копіює весь масив - квадратична складність. Для накопичення -$result[] = $item.