0% прочитано

Як я спростила роботу з Laradock Multi-PHP: dphp, dartisan і dcomposer без входу в контейнер

Показую, як я організувала зручну роботу з PHP 8.3, 8.4 і 8.5 у Laradock та замінила довгі Docker-команди на прості dphp, dartisan і dcomposer.

14 серпня 2026 р. 8 хв читанняLaradock

Чому я взагалі вирішила змінити свій Laradock workflow

Довгий час мій локальний Laradock був максимально простим у щоденній роботі. Я запускала потрібні сервіси один раз, а далі працювала з проєктами без постійної думки про Docker.

Ситуація змінилася, коли одночасно знадобилися кілька версій PHP. Частина проєктів залишалася на PHP 8.3, інші вже працювали на PHP 8.4, а нові - на PHP 8.5.

Це multi-PHP середовище я підготувала під час оновлення локального Laravel-середовища та пакета до PHP 8.5. Наступною проблемою стало зробити цю конфігурацію достатньо зручною для щоденної роботи з CLI.

Для HTTP це вирішується досить природно: кожен Nginx virtual host може використовувати свій PHP-FPM service. Але з CLI усе стало менш зручним. Для Artisan, Composer або звичайного php -v потрібно було пам'ятати, який workspace належить конкретному проєкту, і писати довгі Docker Compose команди.

Я хотіла повернутися до простого workflow, але при цьому зберегти повноцінне multi-PHP середовище.

До
bash
docker compose run --rm --no-deps workspace-85 bash -lc \ 'cd /var/www/icanup && php artisan test'
Після
bash
dartisan test

Multi-PHP архітектура: окремий CLI і PHP-FPM для кожної версії

Перше важливе рішення - не змішувати CLI runtime та PHP-FPM.

PHP-FPM обслуговує HTTP-запити через Nginx, а workspace використовується для CLI: Artisan, Composer, PHPUnit, PHPStan та інших development-команд.

У моєму Laradock фінальна схема виглядає так:

PHP

CLI service

FPM service

PHP 8.3

workspace

php-fpm

PHP 8.4

workspace-84

php-fpm-84

PHP 8.5

workspace-85

php-fpm-85

PHP 8.3 залишається default runtime, а 8.4 і 8.5 працюють як додаткові паралельні середовища.

Чому одного workspace недостатньо

Якщо сайт працює через php-fpm-85, це ще не означає, що php artisan або Composer теж запускаються на PHP 8.5. CLI і FPM - незалежні runtime.

Саме тому для multi-PHP середовища я використовую відповідні workspace, workspace-84 і workspace-85.

Як я налаштувала persistent workspaces

Я не хотіла створювати тимчасовий контейнер для кожної CLI-команди. Тому всі три workspace працюють як звичайні persistent Docker Compose services.

Основний workspace зберігає свої host ports, а альтернативні workspace-84 і workspace-85 їх не публікують. Завдяки цьому всі три контейнери можуть бути запущені одночасно без конфліктів портів.

PHP-FPM services також пов'язані зі своїми відповідними workspace.

YAML
php-fpm-84:  depends_on: !override    - workspace-84      workspace-84:    ports: !reset []      php-fpm-85:    depends_on: !override      - workspace-85        workspace-85:    ports: !reset []
YAML
nginx:   depends_on:     - php-fpm    - php-fpm-84    - php-fpm-85

Однаковий UID і GID для host та workspace

Ще одна проблема проявилася вже після запуску wrappers.

Код монтується в контейнер із host-машини та належить моєму локальному користувачу з UID/GID 1000:1000. Спочатку користувач laradock у контейнерах мав інший UID, через що Git визначав repository як такий, що має підозріле ownership.

Замість додавання глобального safe.directory=* я вирівняла UID/GID користувача Laradock із користувачем host-машини.

ENV
WORKSPACE_PUID=1000WORKSPACE_PGID=1000
Не маскуйте ownership проблему через safe.directory=*

Коли bind-mounted файли належать UID 1000, development-команди в контейнері також краще виконувати користувачем із UID 1000. Це вирішує не тільки Git warning, але й запобігає появі файлів root:root у робочих проєктах.

Локальний project-to-workspace mapping

Я не можу додавати службові Docker-файли до кожного робочого repository, тому mapping версій PHP знаходиться повністю поза application projects.

Для цього використовую локальний файл:

~/.config/laradock/projects.conf

PHP 8.3 є default, тому проєкти на 8.3 взагалі не потрібно перераховувати. У mapping потрапляють лише проєкти, яким потрібен альтернативний workspace.

text
default=workspace # PHP 8.4/home/alyona/Projects/some-project/project-core=workspace-84 # PHP 8.5/home/alyona/Projects/icanup=workspace-85

Один dispatcher замість десятків PHP-команд

Я не хотіла створювати окремі php83, php84, php85, composer84, composer85 та подібні aliases.

Проєкт уже має локальний mapping, отже CLI сам може визначити потрібний workspace.

Для цього я використовую один dispatcher - laradock-exec.

Він:

  • визначає Git root поточного проєкту;

  • знаходить відповідний workspace у projects.conf;

  • використовує workspace як default;

  • перетворює host path /home/alyona/Projects/... на container path /var/www/...;

  • перевіряє, чи існує і чи запущений потрібний service;

  • запускає команду через docker compose exec;

  • виконує її від користувача laradock.

bash
exec docker compose exec \  --user laradock \  -w "$container_dir" \  "$service" \  "$@"

dphp, dartisan і dcomposer

Поверх dispatcher я залишила лише три маленькі wrapper-команди.

  1. dphp запускає PHP у правильному workspace.

  2. dartisan є shortcut для php artisan.

  3. dcomposer запускає Composer на тій самій версії PHP, що й CLI runtime проєкту.

dphp
#!/usr/bin/env bashexec "${HOME}/.local/bin/laradock-exec" php "$@"
dartisan
#!/usr/bin/env bashexec "${HOME}/.local/bin/laradock-exec" php artisan "$@"
dcomposer
#!/usr/bin/env bashexec "${HOME}/.local/bin/laradock-exec" composer "$@"

Як це виглядає у щоденній роботі

Тепер я працюю з Docker-based PHP runtime майже так само, як зі звичайним локальним PHP.

bash
cd ~/Projects/icanup dphp -vdartisan testdartisan migratedcomposer install

Для IcanUp dispatcher автоматично вибирає workspace-85, тому всі ці команди працюють на PHP 8.5.

В іншому проєкті Core ті самі команди автоматично використовують workspace-84.

А будь-який проєкт, якого немає у mapping, переходить на default workspace із PHP 8.3.

Це також прибирає одну просту причину помилок — випадковий запуск проєкту або його тестів на неправильній версії PHP. Я вже бачила, наскільки помітним це може бути, коли той самий тест із round() поводився по-різному на PHP 8.3 і PHP 8.4.

Мені більше не потрібно пам'ятати назву контейнера під час кожного запуску Artisan або Composer.

До
bash
cd ~/Projects/laradock docker compose exec \  --user laradock \  -w /var/www/icanup \  workspace-85 \  php artisan test
Після
bash
cd ~/Projects/icanup dartisan test

Як додати ще один PHP 8.4 або PHP 8.5 проєкт

Новий проєкт не потребує жодних Docker-файлів усередині repository. Спочатку я визначаю його Git root:

bash
git rev-parse --show-toplevel

Після цього достатньо додати один рядок до ~/.config/laradock/projects.conf.

text
# PHP 8.4/home/alyona/Projects/project=workspace-84 # PHP 8.5/home/alyona/Projects/project=workspace-85
PHP 8.3 projects додавати не потрібно. Оскільки default=workspace, усі проєкти без explicit mapping автоматично працюють через PHP 8.3.

Команди працюють навіть із вкладених директорій

Dispatcher використовує git rev-parse --show-toplevel, тому мені не потрібно перебувати в root directory проєкту.

Наприклад, навіть із:

~/Projects/icanup/app/Services

команда dphp -v визначає root ~/Projects/icanup, вибирає workspace-85 і запускається з /var/www/icanup усередині контейнера.

Це маленька деталь, але саме вона робить wrapper зручним у реальній щоденній роботі.

Що я отримала в результаті

  • PHP 8.3, 8.4 та 8.5 працюють одночасно.

  • Для кожної версії є окремий CLI workspace і PHP-FPM.

  • Не потрібно входити в Docker containers.

  • Не потрібно створювати temporary workspace containers.

  • Не потрібно додавати Docker runtime files до робочих repositories.

  • Artisan та Composer завжди використовують правильну версію PHP.

  • Команди виконуються з правильним UID/GID.

  • PHP 8.3 залишається default для старіших проєктів.

  • Для PHP 8.4/8.5 достатньо одного рядка локального mapping.

Docker має спрощувати development environment, а не змушувати мене щоразу думати, в якому контейнері виконати php artisan.

Якимів Альона

Multi-PHP конфігурація сама по собі не повинна робити локальну розробку складнішою.

Для мене найкращим рішенням виявилось залишити чітке розділення між PHP-FPM та CLI runtime, використовувати persistent workspaces і додати дуже тонкий локальний CLI layer поверх Docker Compose.

У результаті інфраструктура залишається повністю контейнеризованою, але під час щоденної роботи я користуюся короткими командами dphp, dartisan і dcomposer.