Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

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() і дати моделей стають незмінними.

Докладніше в документації: DateTimeImmutable

Не всі генератори випадкових чисел однакові. Для безпеки потрібен криптографічно стійкий генератор (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', ...), бо токен і так довгий і випадковий).

Докладніше в документації: Розширення Random