Middle: питання на співбесіді з теми «Artisan»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
3 питання
Через властивість $signature: аргументи - у фігурних дужках, опції - з --.
protected $signature = 'reports:send
{user : ID користувача}
{period=month : day, week або month}
{--queue : Поставити надсилання в чергу}
{--format=pdf : Формат файлу}
{--tag=* : Можна передати кілька разів}';
php artisan reports:send 7 week --queue --format=csv --tag=sales --tag=q3
Що означають позначки:
{user}- обов'язковий аргумент,{user?}- необов'язковий,{period=month}- зі значенням за замовчуванням;{user*}- масив аргументів;{--queue}- перемикач (true, якщо передали),{--format=}чи{--format=pdf}- опція зі значенням;{--tag=*}- масив значень опції;- текст після
:- опис дляphp artisan help reports:send.
Читання в handle():
$userId = $this->argument('user');
$asCsv = $this->option('format') === 'csv';
Залежності беруть параметрами handle() - контейнер їх впровадить. Для обов'язкових аргументів, які зручно запитати інтерактивно, є інтерфейс PromptsForMissingInput.
$exitCode = Artisan::call('reports:send', ['user' => 7, '--queue' => true]);
$output = Artisan::output();
Artisan::queue('reports:send', ['user' => 7])->onQueue('reports'); // у черзі
Artisan::call() виконує команду синхронно в поточному процесі й повертає код виходу. Artisan::queue() ставить виконання в чергу.
З іншої команди - $this->call('cache:clear') або $this->callSilently() без виводу.
Коли це доречно:
- адмін-кнопка «перебудувати індекс», що запускає наявну команду;
- команда-оркестратор, яка по черзі викликає інші;
- тести консольних команд.
Коли краще інакше: якщо логіку з команди потрібно викликати з контролера, її виносять у клас (дію чи сервіс), який викликають і команда, і контролер. Команда - це інтерфейс для CLI з парсингом аргументів і виводом у консоль; тягнути цей шар у веб-запит означає зайве перетворення параметрів у рядки й назад.
Обережно з Artisan::call() у запиті: важка команда заблокує запит на весь час виконання. Для довгого - черга.
Фасад Process - обгортка над компонентом Symfony Process для запуску зовнішніх програм: git, ffmpeg, pg_dump, скриптів. Він замінює exec() і shell_exec() зручним API з тайм-аутами й тестуванням.
Запуск і результат:
use Illuminate\Support\Facades\Process;
$result = Process::run(['git', 'log', '--oneline', '-5']);
$result->successful(); // код завершення 0
$result->output(); // stdout
$result->errorOutput(); // stderr
$result->exitCode();
Масив аргументів замість рядка - головне правило безпеки. З масивом кожен аргумент передається окремо, без участі shell, і ін'єкція команд неможлива:
Process::run(['convert', $uploadedPath, '-resize', '800x', $outputPath]); // безпечно
Process::run("convert {$uploadedPath} -resize 800x {$outputPath}"); // ін'єкція через назву файлу
Параметри:
Process::path(storage_path('exports'))
->timeout(120) // за замовчуванням 60 секунд
->idleTimeout(30) // без виводу довше - зупинити
->env(['PGPASSWORD' => $password])
->input($csv)
->run(['psql', '-c', '\copy items from stdin csv']);
Process::run($command)->throw(); // виняток при ненульовому коді
Асинхронно й паралельно:
$process = Process::start(['ffmpeg', '-i', $in, $out]);
// ... інша робота ...
$result = $process->wait();
[$first, $second] = Process::concurrently(function (Pool $pool) {
$pool->path(base_path())->command(['npm', 'run', 'lint']);
$pool->path(base_path())->command(['vendor/bin/pint', '--test']);
});
Тестування - без справжнього запуску:
Process::fake([
'*ffmpeg*' => Process::result(output: 'done'),
'*git*' => Process::result(errorOutput: 'fatal', exitCode: 128),
]);
// ... виклик коду ...
Process::assertRan(fn (PendingProcess $process) => $process->command[0] === 'ffmpeg');
Шаблони порівнюються з повним командним рядком, де аргументи масиву взяті в лапки, тому зручні шаблони з * з обох боків. Process::preventStrayProcesses() змушує тест падати, якщо запущено процес, для якого немає підробки, - тести ніколи не запустять справжній rm чи git push.
Де запускати: довгі процеси (конвертація відео, бекапи) - у черзі, а не у вебзапиті; тайм-аут процесу має бути меншим за тайм-аут задачі.