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

Junior: питання на співбесіді з теми «Composer і PSR»

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

4 питання

  • composer install ставить рівно ті версії, що записані в composer.lock. Нічого не оновлює.
  • composer update шукає найновіші версії, дозволені обмеженнями з composer.json, ставить їх і переписує composer.lock.

Звідси правило:

  • На проді, в CI і після git pull - лише install. Так у всіх однакові версії, і те, що перевірили тести, саме те, що поїде на сервер.
  • update - свідома дія розробника, щоб оновити залежності. Після неї ганяють тести і комітять новий composer.lock.
composer install --no-dev --optimize-autoloader   # прод
composer update laravel/framework --with-dependencies   # оновити один пакет

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

Якщо composer.lock немає, install поводиться як update і створює його.

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

composer.json описує дозволені версії (^11.0 - будь-яка 11.x). composer.lock фіксує точні версії всіх пакетів, разом із залежностями залежностей, і хеші їхнього вмісту.

Для застосунку - комітити обов'язково. Інакше кожен composer install у колеги, в CI чи на сервері поставить «найновіше, що підходить», і версії поступово розійдуться. Баг «у мене працює» часто саме звідси.

Для бібліотеки (пакета, який ставлять інші) lock-файл зазвичай не комітять або він ні на що не впливає: застосунок, який ставить бібліотеку, однаково розв'язує версії сам, за своїм lock-файлом.

Що ще дає lock-файл:

  • composer install швидший, бо не треба розв'язувати залежності.
  • composer audit перевіряє на відомі вразливості саме встановлені версії.
  • Diff composer.lock у pull request показує, що реально оновилося.

Конфлікти в lock-файлі при злитті гілок не правлять руками: беруть версію однієї гілки й заново виконують ту composer require чи update, яка була в іншій.

Докладніше в документації: composer.lock у системі контролю версій

  • require - залежності, без яких застосунок не працює: фреймворк, бібліотеки для бізнес-логіки, SDK.
  • require-dev - потрібні лише розробникам і CI: тестовий фреймворк (Pest, PHPUnit), статичний аналіз (PHPStan, Larastan), форматування (Pint), Faker, налагоджувальні панелі.
composer require guzzlehttp/guzzle
composer require --dev pestphp/pest

На продакшені dev-залежності не ставлять:

composer install --no-dev --optimize-autoloader

Це:

  • зменшує розмір застосунку й образу;
  • прибирає з сервера інструменти, яких там не має бути (менше коду - менша поверхня атаки);
  • пришвидшує автозавантаження.

Типова помилка: клас з dev-пакета використовується в робочому коді. Локально все працює, бо dev-пакети встановлено, а на проді - Class not found. Приклад - фабрики й Faker у сидерах, які запускають на проді, або налагоджувальний пакет, підключений у сервіс-провайдері без перевірки оточення.

Як ловити: CI-крок, що встановлює залежності з --no-dev і запускає застосунок (або хоча б php artisan чи тести, що не потребують dev-пакетів), а також composer-require-checker для перевірки, що весь код покритий оголошеними залежностями.

Для бібліотек require-dev ще важливіший: споживачі бібліотеки отримують лише її require, тож тестові інструменти бібліотеки до них не потрапляють.

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

Скрипти в composer.json - іменовані команди, які запускають через composer run (або коротко composer <назва>), і хуки на події Composer.

"scripts": {
    "test": "pest --parallel",
    "lint": ["pint --test", "phpstan analyse"],
    "dev": [
        "Composer\\Config::disableProcessTimeout",
        "npx concurrently \"php artisan serve\" \"php artisan queue:listen\" \"npm run dev\""
    ],
    "post-autoload-dump": [
        "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
        "@php artisan package:discover --ansi"
    ]
}
composer test
composer lint
composer dev

Навіщо:

  • Єдині команди для команди й CI. Не треба пам'ятати прапорці кожного інструмента - composer test однаковий локально й у пайплайні.
  • Хуки на події: post-install-cmd, post-update-cmd, post-autoload-dump, post-create-project-cmd. Laravel через post-autoload-dump виявляє пакети (package:discover), а через post-create-project-cmd - генерує APP_KEY.

Що варто знати:

  • @php у скрипті використовує той самий PHP, яким запущено Composer; @composer - той самий Composer; @other-script - викликати інший скрипт.
  • Виконувані файли з vendor/bin доступні в скриптах без повного шляху.
  • За замовчуванням скрипт обмежений 300 секундами; для довгих (dev-сервер) - Composer\Config::disableProcessTimeout.
  • composer install --no-scripts вимикає хуки - корисно в Docker-збиранні, поки код застосунку ще не скопійовано.

Безпека: скрипти пакетів-залежностей Composer не виконує - лише скрипти кореневого composer.json. Плагіни ж пакетів потребують явного дозволу в allow-plugins.

Докладніше в документації: Скрипти Composer