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

Питання на співбесіді: Конфігурація

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

7 питань

Файл .env лежить у корені проєкту й зберігає налаштування, специфічні для середовища, та секрети: доступи до БД, API-ключі, APP_KEY, режим APP_ENV. Ідея в тому, що той самий код працює в різних середовищах (локально, staging, продакшен) лише завдяки різним .env.

APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=mysql
DB_PASSWORD=secret
STRIPE_KEY=sk_test_...

Ключові правила:

  • .env не комітиться в git (він у .gitignore) - кожен розробник і сервер має власний. Натомість комітять .env.example як шаблон без секретів.
  • Значення зчитуються хелпером env('KEY', 'default'), але викликати env() слід лише у файлах config/.
  • У застосунку звертайтесь через config('services.stripe.key'), а не env(...) напряму: після php artisan config:cache (оптимізація на проді) виклики env() поза конфігом повертають null.

Навіщо: секрети не потрапляють у код/репозиторій, а конфігурацію легко змінювати під середовище без редагування коду.

Докладніше в документації: Конфігурація

Створити файл у config/, що повертає масив, і читати значення через config() з «крапковою» нотацією.

// config/billing.php
return [
    'currency' => env('BILLING_CURRENCY', 'UAH'),
    'trial_days' => (int) env('BILLING_TRIAL_DAYS', 14),
    'gateways' => ['liqpay', 'stripe'],
];
$currency = config('billing.currency');
$days = config('billing.trial_days', 14);       // значення за замовчуванням

$days = Config::integer('billing.trial_days');  // типізоване читання

Правила:

  • env() - лише у файлах config/. Після php artisan config:cache файл .env більше не читається, і env() у коді поверне null;
  • секрети (ключі API, паролі) - у .env, у файлі конфігурації лише посилання на змінну;
  • нову змінну додають і в .env.example, щоб колеги й CI знали про неї.

Типізовані методи Config::string(), integer(), boolean(), array() кидають виняток, якщо значення не того типу. Помилка конфігурації видно одразу, а не в дивній поведінці далі.

Конфігурацію сторонніх пакетів публікують командою php artisan config:publish чи vendor:publish.

Докладніше в документації: Доступ до значень конфігурації

Коротка відповідь: після php artisan config:cache функція env() поза конфігами повертає null.

Чому. Кешування конфігурації зберігає підсумковий масив у один PHP-файл. Файл .env після цього взагалі не читається - у проді його може й не бути. Значення, зчитані під час збирання кешу, всередині конфігів залишаються; будь-який виклик env() в іншому місці виконується вже без завантаженого середовища.

Як ламається:

class WeatherClient
{
    public function __construct()
    {
        // У проді після config:cache тут null.
        $this->key = env('WEATHER_KEY');
    }
}

Найгірше, що локально все працює - кеш конфігурації зазвичай збирають лише в проді.

Правильно: env() живе тільки у файлах config/, а код читає конфіг.

// config/services.php
'weather' => [
    'key' => env('WEATHER_KEY'),
],

// будь-де в застосунку
$this->key = config('services.weather.key');

Побічна вигода: конфіг можна підмінити в тестах, а .env - ні.

config(['services.weather.key' => 'test-key']);

Виняток - файли, що виконуються до завантаження застосунку (bootstrap/), і сам AppServiceProvider тут не виняток: у ньому теж потрібен config().

Докладніше в документації: Конфігурація

php artisan optimize виконує пакет кешувань, які прибирають повторну роботу з кожного запиту:

php artisan config:cache    # усі config/*.php в один масив
php artisan route:cache     # маршрути в серіалізований вигляд
php artisan view:cache      # прекомпіляція Blade
php artisan event:cache     # мапа подій і слухачів

Зворотна дія - php artisan optimize:clear.

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

Коли шкодить:

  • Локально під час розробки. Закешований конфіг не помічає змін у .env, а закешовані маршрути - нових маршрутів. Півгодини пошуку помилки, якої немає.
  • route:cache не працює із замиканнями в маршрутах: команда впаде з помилкою. Маршрути мають вказувати на контролери.
  • env() поза конфігами перестає працювати - див. окреме питання про це.
  • Якщо конфіг залежить від запиту. Кеш збирається один раз під час деплою, тож щось на кшталт мультитенантності за доменом у конфігу зафіксується у стані збирання.

Порядок під час деплою має значення: спершу викласти новий код, потім кешувати. Якщо кешувати до оновлення файлів, у кеш потрапить попередня версія.

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

Перед завантаженням змінних Laravel дивиться, чи задано APP_ENV (змінною оточення процесу) або опцію --env у команді Artisan. Якщо так, і поруч є .env.{APP_ENV}, береться він замість .env.

APP_ENV=staging php artisan migrate          # прочитає .env.staging, якщо він є
php artisan test                             # phpunit.xml ставить APP_ENV=testing -> .env.testing

Типова схема:

  • .env - локальна розробка, у .gitignore;
  • .env.example - шаблон з усіма змінними без секретів, у репозиторії;
  • .env.testing - окрема база й драйвери для тестів (QUEUE_CONNECTION=sync, MAIL_MAILER=array);
  • продакшен - змінні оточення платформи чи секрет-менеджера, а не файл у репозиторії.

Пріоритет: справжні змінні оточення процесу важливіші за значення з .env - так платформа перекриває налаштування без зміни файлів.

Пастка: config:cache, виконаний з одним оточенням, «заморожує» його значення. Закешована конфігурація з продакшену на машині розробника чи навпаки - класична причина «тести пишуть у робочу базу». Тому кеш конфігурації будують лише там, де він буде використаний.

Перевірити, яке оточення активне: app()->environment(), php artisan about.

Докладніше в документації: Додаткові файли оточення

Коли «на сервері працює не так», перше питання - які насправді значення бачить застосунок.

php artisan about                      # версії, оточення, драйвери, стан кешів
php artisan about --only=environment
php artisan config:show database       # увесь файл конфігурації з поточними значеннями
php artisan config:show cache.default

Що показує about:

  • версії Laravel, PHP, Composer;
  • оточення, режим налагодження, URL, мовні налаштування;
  • драйвери кешу, черги, сесії, пошти, бази;
  • чи закешовані конфігурація, маршрути, події, шаблони.

Пакети можуть додавати свої секції через AboutCommand::add().

Типові знахідки:

  • Config: CACHED після зміни .env - нові значення не діють, потрібен config:cache чи config:clear;
  • Debug Mode: ENABLED на продакшені;
  • черга sync замість redis - завдання виконуються в запиті;
  • неправильний APP_URL - посилання в листах ведуть не туди.

Обережно: config:show виводить і секрети (паролі бази, ключі), тож результат не вставляють у чати й тікети без очищення.

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

Проблема: секретів багато, вони змінюються, і їх треба передати новому розробнику, стейджингу й продакшену, не пересилаючи .env у месенджері.

Варіант 1 - зашифрований файл у репозиторії:

php artisan env:encrypt --env=production          # створює .env.production.encrypted
php artisan env:decrypt --env=production --key=... # на сервері чи в CI

Зашифрований файл комітять разом з кодом - історія змін і рев'ю як для коду. Ключ зберігають окремо (секрет CI, LARAVEL_ENV_ENCRYPTION_KEY). Опція --readable шифрує лише значення, лишаючи назви змінних видимими в диффах.

Варіант 2 - секрет-менеджер платформи (AWS Secrets Manager, Vault, Doppler, секрети GitHub Actions, змінні Laravel Cloud чи Forge): значення потрапляють у змінні оточення процесу, файлу немає взагалі.

Принципи незалежно від інструмента:

  • різні секрети для кожного оточення - ключ стейджингу не відкриває продакшен;
  • мінімальні права: CI має лише те, що потрібно для деплою;
  • ротація: після звільнення людини чи витоку - змінити, а не лише видалити доступ;
  • секрети не в журналах, не у виводі config:show, не в стектрейсах (APP_DEBUG=false);
  • APP_KEY при ротації - через APP_PREVIOUS_KEYS, щоб не розлогінити всіх.

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

Докладніше в документації: Шифрування файлів оточення