Команда php artisan dev запускає чотири процеси в одному терміналі: сервер розробки, queue worker, Pail та Vite. До Laravel 13.25 усі вони писали у спільний потік виводу, що створювало проблеми, коли Vite перебудовувався під час логування queue worker'а, а ви намагалися прочитати stack trace з Pail.
Раніше команда artisan dev використовувала concurrently, але Laravel 13.25 замінює його на @laravel/multiplex - Terminal UI, який тримає кожен процес окремо. Нічого встановлювати чи налаштовувати не потрібно, достатньо запустити команду:
php artisan dev
Три режими роботи
Tabs - режим за замовчуванням. Кожен зареєстрований процес отримує власну вкладку, між якими можна перемикатися для читання виводу одного процесу за раз. Окремі процеси можна перезапускати або очищати їхні логи без впливу на інші, а процес, що завершився з помилкою, автоматично перезапускається.
Stream - старий режим роботи з покращеним інструментарієм: один об'єднаний потік виводу, але з можливістю прокручування та пошуку, а не лише те, що зберігається в історії вашого терміналу.
Inline - звичайний вивід без жодного UI. Цей режим вибирається автоматично, коли немає TTY, що потрібно при запуску artisan dev під process supervisor'ом, у контейнері або в CI.
Вибрати режим для одного запуску можна так:
php artisan dev --tabs
php artisan dev --stream
php artisan dev --inline
Короткі форми прапорців: -t, -s та -i. Також можна перемикатися між tabs та stream під час роботи UI, тому прапорець потрібен лише коли ви хочете стартувати не з режиму за замовчуванням.
Пошук працює в обох режимах tabs та stream, а мітки часу можна ввімкнути в будь-якому режимі:
php artisan dev --timestamps
Коли команда завершується, усе, що було в буфері, виводиться в основний термінал у стилі stream. Вихід з UI не видаляє вивід, на який ви дивилися.
Встановлення режиму за замовчуванням для проєкту
Прапорці для окремого запуску підходять для разових випадків. Якщо всі в команді мають отримувати режим stream або якщо вам потрібні мітки часу щоразу, встановіть це в методі boot сервіс-провайдера поруч з реєстраціями процесів:
namespace App\Providers;
use Illuminate\Foundation\DevCommands;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
if (! $this->app->environment('local')) {
return;
}
DevCommands::stream();
DevCommands::withTimestamps();
}
}
Сім методів для налаштування команди:
DevCommands::tabs() - одна вкладка на процес (за замовчуванням)
DevCommands::stream() - один об'єднаний потік, що прокручується
DevCommands::inline() - звичайний вивід, без UI
DevCommands::withTimestamps() - додає мітку часу до кожного рядка
DevCommands::disableAutoRestart() - залишає процес, що завершився з помилкою, мертвим замість перезапуску
DevCommands::bufferSize(5000) - кількість рядків на процес у режимі tabs
DevCommands::streamBufferSize(10000) - загальна кількість рядків у режимі stream
Прапорці командного рядка мають пріоритет над провайдером, тому php artisan dev --tabs перевизначає виклик DevCommands::stream(), а --no-restart перевизначає автоперезапуск для цього запуску.
Розміри буферів - це два параметри, про які варто подумати. Кожен процес тримає власний кільцевий буфер у режимі tabs, тому балакучий процес Vite не може витіснити вивід queue worker'а з видимості, як це відбувається у спільному scrollback. Збільште bufferSize(), якщо виявите, що прокручуєте за верх вкладки.
Автоматичний перезапуск
Процес, що завершився з помилкою, перезапускається автоматично. Це справжня зміна поведінки порівняно з --kill-others-on-fail, який artisan dev передавав concurrently з версії 13.18, де смерть одного процесу забирала з собою весь стек.
Новий режим за замовчуванням відповідає поширеному випадку: queue worker, що помирає через фатальну помилку, поки ви редагуєте клас job, або Vite, що падає через невірний import. Ви виправляєте файл, і процес повертається без перезапуску всього стека. Коли ви хочете побачити помилку та зупинитися, вимкніть автоперезапуск:
php artisan dev --no-restart
Або назавжди через DevCommands::disableAutoRestart().
Де що працює
Шлях через multiplex поки що лише для macOS та Linux. Windows продовжує працювати через concurrently, і методи режимів та буферів там є no-op, хоча --timestamps та --no-restart обидва транслюються в еквівалентні прапорці concurrently, тому ці два працюють на всіх платформах.
Інша вимога - Node v22.13 або новіший, що є мінімумом для @laravel/multiplex. Фреймворк прив'язує пакет до конкретної minor-версії та запускає його через exec-команду менеджера пакетів вашого проєкту, тому pnpm-проєкт запускає pnpm dlx, а npm-проєкт - npx без жодного налаштування. Залежність у package.json додавати не потрібно.
Ще один прапорець, здебільшого для інструментів, а не для читання:
php artisan dev --json
Він виводить JSON-події, розділені новими рядками, замість форматованого виводу, і означає --inline. Це хук для обгортання artisan dev у щось інше - розширення редактора або dashboard, який хоче структурований вивід процесу.
Виправлення SERVER_HOST
Пов'язана зміна потрапила в той самий реліз. Процес server за замовчуванням реєструвався як serve --host=localhost, і оскільки явна опція командного рядка перебиває базований на середовищі режим за замовчуванням ServeCommand, SERVER_HOST ігнорувався після того, як скелет застосунку перейшов зі старого скрипта composer dev на artisan dev. Експонування сервера розробки на інший пристрій у вашій мережі перестало працювати.
Тепер за замовчуванням просто serve, тому це працює як раніше:
SERVER_HOST=0.0.0.0 php artisan dev
Якщо ви реєстрували власний процес server для обходу цієї проблеми, можете його видалити.
Додаткові матеріали
Інтеграцію multiplex зробив Joe Tannenbaum у PR #61100, а виправлення режиму за замовчуванням для serve - @n3crosis у PR #61090.