Senior: питання на співбесіді з теми «Основи мови»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
4 питання
PHP використовує підрахунок посилань + збирач циклічних посилань. У звичайному веб-запиті пам'ять звільняється наприкінці запиту, тож витоки малопомітні. Але у довготривалих процесах (черги, Octane) пам'ять накопичується.
Як уникати:
- Не зберігати стан у статичних властивостях/синглтонах між завданнями.
unset()великих структур, скидати накопичувачі (логи запитівDB::flushQueryLog()).- Обробляти дані порціями (
chunk,lazy), не тримати все в пам'яті. - Перезапускати воркери за лімітом:
queue:work --max-jobs=1000 --max-time=3600або при досягненні--memory.
Octane має gc_collect_cycles()-хуки; Horizon автоматично перезапускає воркери, що «розпухли».
Налаштування PHP задаються на кількох рівнях, і не кожне можна змінити будь-де. Для кожної директиви в документації вказано режим:
INI_SYSTEM- лише вphp.iniчи конфігурації сервера:opcache.memory_consumption,opcache.preload,disable_functions.INI_PERDIR- ще й у.user.ini(для FPM/CGI) чи.htaccess(Apache з mod_php):upload_max_filesize,post_max_size.INI_USER- ще й у коді черезini_set().INI_ALL- усюди:memory_limit,max_execution_time,display_errors,date.timezone.
ini_set('memory_limit', '512M'); // працює
ini_set('upload_max_filesize', '50M'); // не подіє: тіло запиту вже розібране
Звідки PHP бере налаштування:
php --ini # які файли завантажено
php -i | grep memory # поточні значення (CLI!)
CLI і FPM часто мають різні php.ini (/etc/php/8.4/cli/php.ini і /etc/php/8.4/fpm/php.ini). Класична пастка: в консолі memory_limit=-1, а веб-запити падають на 128 МБ. Значення для FPM дивляться через phpinfo() у веб-запиті чи статусну сторінку.
Пули PHP-FPM можуть перевизначати налаштування: php_admin_value[memory_limit] = 256M (не можна змінити з коду) і php_value[...] (можна).
У Docker налаштування кладуть окремим файлом у /usr/local/etc/php/conf.d/ - він перевизначає дефолти без редагування основного php.ini.
Рекомендації для проду: display_errors=Off, log_errors=On, expose_php=Off, налаштований date.timezone, OPcache з validate_timestamps=0 і скиданням при деплої. Зміни в php.ini вимагають перезапуску FPM.
DateTime змінюється на місці: modify(), add(), setTime() змінюють сам об'єкт і повертають його ж.
DateTimeImmutable ніколи не змінюється: ті самі методи повертають новий об'єкт, а оригінал лишається без змін.
Класичний баг з DateTime:
$start = new DateTime('2026-10-01');
$end = $start->modify('+7 days');
echo $start->format('Y-m-d'); // 2026-10-08 - початок теж зсунувся!
$start і $end - той самий об'єкт. Особливо підступно, коли дату передали у функцію чи вона - властивість моделі: виклик modify() усередині методу непомітно змінює стан десь в іншому місці.
З DateTimeImmutable:
$start = new DateTimeImmutable('2026-10-01');
$end = $start->modify('+7 days'); // $start лишився 2026-10-01
Що ще варто знати про дати в PHP:
- Часовий пояс - явно:
new DateTimeImmutable('now', new DateTimeZone('Europe/Kyiv')). Без нього беретьсяdate.timezone, яке на різних серверах різне. - Зберігати в UTC, показувати в поясі користувача -
->setTimezone(...)при виведенні. modify('+1 month')31 січня дає 3 березня (переповнення дня). Для «наступного місяця» -modify('last day of next month')чиfirst day of next month.- Порівняння - звичайними операторами:
$a < $bпрацює для дат. - Для інтервалів -
diff()повертаєDateInterval, для ітерації -DatePeriod.
У Laravel Carbon (на основі DateTime) змінюваний, а CarbonImmutable - ні. Laravel дозволяє зробити immutable за замовчуванням: Date::use(CarbonImmutable::class) у сервіс-провайдері - тоді now() і дати моделей стають незмінними.
Не всі генератори випадкових чисел однакові. Для безпеки потрібен криптографічно стійкий генератор (CSPRNG), результат якого неможливо передбачити.
Для токенів, паролів, кодів підтвердження - лише CSPRNG:
random_int(100000, 999999); // 6-значний код підтвердження
bin2hex(random_bytes(32)); // 64-символьний токен
Str::random(40); // у Laravel - теж на random_bytes
Чого не використовувати для безпеки:
rand(),mt_rand()- Mersenne Twister: швидкий, але передбачуваний. Знаючи кілька результатів, можна відновити внутрішній стан і передбачити наступні.uniqid()- це час з мікросекундами, а не випадковість.md5(time()),sha1(microtime())- хеш передбачуваного значення лишається передбачуваним.shuffle(),array_rand(),str_shuffle()- використовують нестійкий генератор.
Розширення Random (PHP 8.2) - об'єктний API з явним вибором рушія:
use Random\Randomizer;
use Random\Engine\Secure;
use Random\Engine\Mt19937;
$secure = new Randomizer(new Secure()); // CSPRNG за замовчуванням
$secure->getBytesFromString('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', 8); // код без схожих символів
$secure->shuffleArray($items); // безпечне перемішування
$seeded = new Randomizer(new Mt19937(42)); // відтворюваний: та сама послідовність з тим самим seed
$seeded->getInt(1, 100);
Відтворювана (seeded) випадковість корисна поза безпекою: тести, генерація даних, детерміновані вибірки («та сама випадкова добірка для того самого користувача сьогодні»).
Порівняння токенів - через hash_equals(), а зберігати токени доступу в базі краще хешованими (як паролі, але достатньо hash('sha256', ...), бо токен і так довгий і випадковий).