<? phpukraine СПІВБЕСІДИ
⌕Пошук по платформі

Питання на співбесіду Senior PHP

Архітектура, памʼять у довгих процесах, ідемпотентність, компроміси й обґрунтування рішень. Питають не факти, а спосіб мислення.

Тема
Рівень
38 питань
LR
Laravel·Senior ·Laravel 11 ·Laravel 12 ·Laravel 13

Laravel 11 прибрав з нового скелета `Http/Kernel`, `Console/Kernel`, `Exceptions/Handler`, половину конфігів і теку `lang`, перенісши налаштування в `bootstrap/app.php` і в дефолти фреймворка; 12 і 13 структуру не чіпали, тож оновлення між ними - це бамп версій у `composer.json` плюс вичитаний `UPGRADE.md`, а не переписування застосунку.

Завантажую відповідь…
Сторінка питання →
OPS
DevOps·Senior ·деплой ·zero-downtime ·OPcache

Новий реліз збирається і прогрівається в окремій теці, трафік перемикається однією атомарною заміною симлінка (або поступовою заміною контейнерів за readiness-пробою), після чого скидаються realpath cache й OPcache і перезапускаються воркери; відкат - та сама операція у зворотний бік.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·design patterns ·Command ·command bus

Command пакує намір разом з усіма даними в обʼєкт, щоб виконати його зараз, пізніше, повторно або з іншої точки входу. Command bus додає одне місце, через яке проходять усі команди: він знаходить обробник за класом команди й загортає виконання в middleware для транзакцій, логування й валідації. Queued job у Laravel - це команда й обробник в одному класі з доставкою через чергу, а Symfony Messenger тримає повідомлення й обробник окремо і вирішує, синхронно чи через транспорт, у конфігу `routing`.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·design patterns ·Decorator ·Proxy

За кодом їх не відрізнити: обидва реалізують той самий інтерфейс і тримають посилання на справжній обʼєкт. Декоратор додає поведінку (кеш, лог, повтори) і складається шарами там, де збирають обʼєкт, а проксі керує доступом до справжнього обʼєкта: відкладає його створення, перевіряє права або пересилає виклик кудись далі. Кешувальна обгортка репозиторію працює, лише якщо інвалідація живе в тій самій обгортці й спрацьовує після коміту.

Завантажую відповідь…
Сторінка питання →
LR
Laravel·Senior ·Livewire ·SPA ·Inertia

Вибір визначають три речі, а не смак: чи є другий клієнт (мобілка, партнери) — тоді API все одно доведеться зробити, і SPA стає майже безкоштовним; яка зернистість взаємодій — Livewire платить мережевим round-trip за кожен крок, тому годиться для форм, таблиць і фільтрів, а не для перетягування, канви чи офлайну; і хто це супроводжуватиме. Livewire тримає стан на сервері й ганяє снапшот компонента туди-сюди, SPA тримає стан у браузері й вимагає версіонованого контракту; Inertia — середина, де є клієнтські компоненти, але немає публічного API.

Завантажую відповідь…
Сторінка питання →
SF
Symfony·Senior ·API Platform ·DTO ·state provider

API Platform виправданий, коли потрібен машиночитний контракт (OpenAPI/JSON-LD), фільтри, пагінація й HTTP-кеш «з коробки»; головна архітектурна зміна — контролер зникає, а його місце займають state provider і processor, тому ресурс має бути окремим DTO, а не Doctrine-сутністю.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·DDD ·bounded context ·компроміси

DDD має сенс там, де складність саме в домені: багато правил, які часто змінюються, і потрібна спільна мова з бізнесом; для CRUD-адмінки чи інтеграційного сервісу агрегати й репозиторії додають шарів без вигоди.

Завантажую відповідь…
Сторінка питання →
WP
WordPress·Senior ·headless ·REST API ·WPGraphQL

Headless виправданий, коли контент споживає більше ніж один клієнт або команда вже живе в JS-екосистемі; платити за це доводиться прев'ю, редиректами, формами, CSS блоків, меню, SEO-мета й власною інвалідацією кешу — усім, що раніше безкоштовно робила тема.

Завантажую відповідь…
Сторінка питання →
WP
WordPress·Senior ·multisite ·мережа ·switch_to_blog

Multisite виправданий, коли багато однотипних сайтів живуть на одному коді, одній базі й під одною командою; він дає спільні wp_users і спільний набір плагінів, але й спільну долю - оновлення, падіння, бекап і безпека стають мережевими, а не посайтовими.

Завантажую відповідь…
Сторінка питання →
SQL
SQL·Senior ·партиціонування ·PostgreSQL ·MySQL

Партиціонування потрібне тоді, коли дані природно ріжуться по ключу (найчастіше по часу) і старі шматки треба видаляти чи архівувати цілком: DROP/DETACH PARTITION — це операція над метаданими замість мільйонів DELETE. Прискорення читання — побічний ефект, і лише коли ключ партиціонування є в WHERE.

Завантажую відповідь…
Сторінка питання →
PHP
Core PHP·Senior ·WeakMap ·WeakReference ·памʼять

WeakReference (PHP 7.4+) і WeakMap (PHP 8.0+) тримають обʼєкт, не збільшуючи його refcount: як тільки обʼєкт знищується, посилання стає null, а запис у WeakMap зникає сам — це штатний спосіб робити привʼязані до обʼєктів кеші й реєстри в довгоживучих процесах.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·модульний моноліт ·мікросервіси ·межі модулів

Модулі й сервіси розділяє не код, а межа процесу: усередині моноліта виклик модуля — це синхронний метод у спільній транзакції, між сервісами — мережа з таймаутами, частковими збоями й eventual consistency. Тому за замовчуванням беруть модульний моноліт із твердими межами, а окремий сервіс виділяють лише там, де є конкретна причина — незалежний профіль навантаження, окремий цикл релізу під окрему команду або інший рантайм — і платять за неї розподіленими транзакціями та експлуатацією.

Завантажую відповідь…
Сторінка питання →
SEC
Безпека·Senior ·unserialize ·object injection ·gadget chain

`unserialize()` створює обʼєкти будь-якого класу, який може завантажити автозавантажувач, з властивостями, які задав автор рядка, і потім PHP сам викликає їхні магічні методи: `__wakeup()`, `__unserialize()`, `__destruct()`, `__toString()`. Ланцюжок таких методів із класів у `vendor/` (gadget chain) дає запис файлів чи виконання коду. Чужі дані розбирають через `json_decode()`, а якщо формат PHP неминучий, то з `allowed_classes => false`, `max_depth` і перевіркою HMAC до розбору.

Завантажую відповідь…
Сторінка питання →
PHP
Core PHP·Senior ·PHP 8.5 ·pipe operator ·clone with

PHP 8.5 (реліз 20 листопада 2025) додав оператор конвеєра `|>`, синтаксис `clone $obj with {...}`, атрибут `#[\NoDiscard]` з приведенням `(void)`, вбудоване розширення URI з класами `Uri\Rfc3986\Uri` та `Uri\WhatWg\Url`, а також дрібніші зручності — `array_first()`/`array_last()`, атрибути на константах, замикання в константних виразах і бектрейси для фатальних помилок.

Завантажую відповідь…
Сторінка питання →
SEC
Безпека·Senior ·SSRF ·вебхуки ·cloud metadata

SSRF (server-side request forgery) трапляється, коли сервер іде за адресою, яку задав користувач, і стукає туди, куди сам користувач дістатись не може: у метадані хмари на `169.254.169.254`, у Redis на localhost чи в адмінку у внутрішній мережі. Захист: дозволяти лише `http(s)`, самим резолвити хост і відкидати непублічні адреси, підключатися саме до перевіреної IP, не йти за редіректами без повторної перевірки, а надійніше пустити такі запити через окремий egress-проксі.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·outbox ·dual write ·повідомлення

Outbox pattern прибирає проблему подвійного запису: подію зберігають рядком у таблиці `outbox` тією ж транзакцією, що й зміну стану, а окремий процес-релей уже після коміту читає ці рядки й публікує в брокер. База і подія стають атомарними, ціною чого є доставка at-least-once — споживач зобовʼязаний бути ідемпотентним.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·DDD ·агрегат ·інваріанти

Агрегат - це найменша група об'єктів, яку доводиться читати й писати разом, щоб гарантувати бізнес-правило: межа проходить там, де є інваріант, який мусить виконуватися після кожного коміту. Корінь агрегату - єдина точка входу, одна транзакція змінює один екземпляр, а зв'язки з іншими агрегатами тримаються за ідентифікатором; що більший агрегат, то більше конкурентних записів він серіалізує.

Завантажую відповідь…
Сторінка питання →
OPS
DevOps·Senior ·масштабування ·stateless ·сесії

Треба винести назовні весь стан, який зараз лежить на локальному диску чи в памʼяті процесу: сесії — у Redis/БД, завантажені файли — в обʼєктне сховище, кеш і локи — у спільний Redis, черги й планувальник — в окремі процеси з дедуплікацією; липкі сесії це не рішення, а спосіб не помітити проблему.

Завантажую відповідь…
Сторінка питання →
OPS
DevOps·Senior ·міграції ·zero-downtime ·деплой

Схема змінюється у фази expand і contract: спершу зміни, сумісні зі старим кодом, потім деплой коду, який працює з обома варіантами, і лише після повного викочування прибирання старого.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·API ·версіонування ·backward compatibility

Версія - це ярлик на контракті, а сумісність визначається поведінкою: ламає не номер, а звужений тип, нове значення enum, суворіша валідація чи змінений статус-код. Робочий підхід - additive-зміни всередині версії, нова major лише на справжній злам, машиночитані заголовки Deprecation і Sunset плюс телеметрія по клієнтах перед вимкненням.

Завантажую відповідь…
Сторінка питання →
SF
Symfony·Senior ·Doctrine Migrations ·schema:validate ·CI

diff дає єдине джерело істини для схеми, але його результат треба перечитувати й перегенеровувати після rebase; розходження ловиться в CI прогоном міграцій на порожній базі плюс doctrine:schema:validate, а перенесення даних живе в окремих міграціях на сирому SQL, не в тих, що змінюють схему.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·bounded context ·DDD ·context map

Межа контексту проходить там, де слово змінює значення і де змінюється склад людей, які замовляють зміни, а не там, де закінчується таблиця. Спільні дані розвʼязуються правилом одного власника факту: решта контекстів читають через контракт або тримають власну проєкцію, спільний код лишається мікроскопічним ядром без бізнес-правил, а чужа модель заходить усередину тільки через антикорупційний шар.

Завантажую відповідь…
Сторінка питання →
SQL
SQL·Senior ·deadlock ·блокування ·InnoDB

Deadlock — це цикл очікувань блокувань: база сама його виявляє й відкочує одну транзакцію (MySQL 1213/40001, PostgreSQL 40P01), тому застосунок зобовʼязаний повторити її, а зменшують частоту єдиним порядком блокування, короткими транзакціями та індексами під UPDATE.

Завантажую відповідь…
Сторінка питання →
SQL
SQL·Senior ·блокування ·транзакції ·конкурентність

Три рівні захисту: песимістичний замок SELECT FOR UPDATE серіалізує доступ до рядка, колонка version із перевіркою кількості оновлених рядків ловить конфлікт без замка, а унікальне обмеження в базі тримає інваріант навіть тоді, коли обидва механізми обійшли.

Завантажую відповідь…
Сторінка питання →
LR
Laravel·Senior ·профілювання ·Telescope ·Debugbar

Спершу розкласти час: браузерний waterfall відділяє TTFB від фронтенду, а далі Debugbar/Clockwork/Telescope локально й Blackfire або Pulse у проді показують, куди йдуть мілісекунди всередині PHP. Оптимізують за спаданням внеску: кількість запитів (N+1) → важкі запити з `EXPLAIN` → синхронні зовнішні виклики в чергу → гігієна деплою (`opcache`, `php artisan optimize`, `composer -o`), і лише потім кеш і Octane.

Завантажую відповідь…
Сторінка питання →
OPS
DevOps·Senior ·observability ·OpenTelemetry ·трасування

Три сигнали налаштовують як один набір даних, зшитий спільним trace_id: OpenTelemetry дає trace через PECL-розширення й auto-instrumentation, той самий trace_id підмішується процесором у кожен запис структурованого лога, а метрики через shared-nothing модель PHP збирає не сам процес, а локальний Collector; алерти вішають лише на SLI з бюджетом помилок, решта живе на дашборді.

Завантажую відповідь…
Сторінка питання →
SF
Symfony·Senior ·Messenger ·retry ·failed transport

Транспорт налаштовується з retry_strategy: кількість спроб, початкова затримка й множник; вичерпані повідомлення йдуть у failed transport, а логічні помилки відкидаються одразу через UnrecoverableMessageHandlingException.

Завантажую відповідь…
Сторінка питання →
TST
Тестування·Senior ·легасі ·characterization tests ·golden master

Спершу зафіксувати те, що код робить зараз, а не те, що він мав би робити: characterization-тести (Майкл Фезерс) або golden master на рівні HTTP чи консольної команди. Далі знайти шви (seams), через які можна підставити залежності, мінімальними безпечними рефакторингами винести логіку в класи, що тестуються, і лише тоді міняти поведінку. Нову логіку пишуть поруч і з тестами (sprout method, wrap method), а не всередині старого моноліту.

Завантажую відповідь…
Сторінка питання →
PHP
Core PHP·Senior ·OPcache ·JIT ·preload

OPcache тримає в спільній памʼяті вже скомпільовані опкоди, прибираючи парсинг і компіляцію з кожного запиту — це і є основний виграш; preload (PHP 7.4+) додатково тримає звʼязані класи фреймворку, а JIT (PHP 8.0+, вимкнений за замовчуванням через `opcache.jit_buffer_size=0`) компілює гарячі опкоди в машинний код і майже нічого не дає типовому веб-застосунку, бо той упирається в I/O, а не в CPU.

Завантажую відповідь…
Сторінка питання →
YII
Yii·Senior ·міграція ·легасі ·strangler

Найбезпечніший підхід — strangler pattern: новий застосунок ставиться поруч, роутинг поступово перекидається на нього, спільною лишається база даних; переписувати все одразу означає роками тримати дві версії продукту.

Завантажую відповідь…
Сторінка питання →
ARC
Архітектура·Senior ·ідемпотентність ·платежі ·черги

Клієнт передає idempotency key, сервер зберігає його разом із результатом операції під унікальним індексом і на повторний запит повертає збережений результат замість другого платежу.

Завантажую відповідь…
Сторінка питання →
LR
Laravel·Senior ·архітектура ·actions ·DTO

Стандартної структури вистачає, доки логіка сценарію вміщається в контролер плюс модель; далі вузьке місце — не назви тек, а те, що бізнес-правило не має одного власника. Дія з одним публічним методом і незмінним DTO на вході дає цього власника без DDD, а тактичні патерни DDD додають лише там, де є інваріанти, які треба захищати, і мова, якою говорить бізнес.

Завантажую відповідь…
Сторінка питання →
TST
Тестування·Senior ·швидкість тестів ·parallel ·шардування

Спершу виміряти (`pest --profile`, час по джобах CI), потім прибрати дешеві втрати: хешування паролів, міграції на кожен тест, справжні `sleep()` і мережу, зайві дані з фабрик. Далі паралелити (`--parallel`, шардування на кілька машин), тримаючи тести ізольованими: своя база на процес, без спільних файлів і статики. Надійність тримається на правилі «flaky-тест це баг з власником», а не на автоматичних повторах.

Завантажую відповідь…
Сторінка питання →
PHP
Core PHP·Senior ·Octane ·FrankenPHP ·воркери

Під FPM памʼять прибирає смерть процесу наприкінці запиту, у worker mode цього прибирання немає: усе, що лишилось у статиці, контейнері чи реєстрі слухачів, накопичується. Робочий набір це гігієна стану між ітераціями, явний запуск збирача циклів за порогом і перезапуск воркера за лімітом памʼяті або кількості запитів.

Завантажую відповідь…
Сторінка питання →
LR
Laravel·Senior ·Multi-tenancy ·tenant_id ·global scope

Колонка `tenant_id` дає спільні міграції та один пул зʼєднань, але ізоляція живе в кожному `WHERE`, і будь-який забутий фільтр стає витоком. База (або схема) на тенанта переносить межу на рівень конекшна ціною N міграцій, N бекапів і пулу зʼєднань. Глобальний scope закриває лише читання через Eloquent: `DB::table()`, правила `unique`/`exists` і відновлення моделі в черзі його не бачать, а кеш, файли, сесії та scheduler треба розводити по тенантах окремо.

Завантажую відповідь…
Сторінка питання →
SQL
SQL·Senior ·транзакції ·ізоляція ·PostgreSQL

MySQL за замовчуванням REPEATABLE READ, PostgreSQL — READ COMMITTED; рівень визначає, які аномалії можливі, але втрачене оновлення закривають не рівнем, а атомарним UPDATE або SELECT FOR UPDATE.

Завантажую відповідь…
Сторінка питання →
Прогрес карток і тестів зберігається у профілі. Створити профіль·Увійти