Narzędzie CLI w CakePHP 5
CakePHP 5 daje całkiem wygodne narzędzia do pisania komend terminalowych, zwłaszcza jeśli chcesz pokazać statusy, ostrzeżenia i postęp długiego procesu. Dzięki obsłudze stylizowanego outputu i helpera Progress można zbudować czytelne narzędzie CLI bez dokładania zewnętrznych bibliotek. Taki CLI sprawdza się przy importach danych, migracjach, synchronizacji z API, masowych aktualizacjach rekordów i zadaniach batchowych. Właśnie w takich przypadkach kolorowy output i progres bar poprawiają UX operatora, bo od razu widać, czy proces działa, ostrzega czy zakończył się sukcesem.
Struktura komendy
W CakePHP 5 komenda zwykle dziedziczy po Command, a główna logika trafia do metody execute(). To dobre miejsce na pobranie argumentów, inicjalizację helperów i uruchomienie właściwego procesu.
<?php
declare(strict_types=1);
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
final class ImportUsersCommand extends Command
{
public function execute(Arguments $args, ConsoleIo $io): int
{
$io->out('Start importu');
return static::CODE_SUCCESS;
}
}
Kolory w output
CakePHP 5 wspiera stylizowany output przez znaczniki w tekście oraz tryby ConsoleOutput::COLOR i ConsoleOutput::PLAIN. W praktyce oznacza to, że możesz wypisywać komunikaty sukcesu, błędy i ostrzeżenia w kolorach, a jeśli trzeba, łatwo przełączyć się na zwykły tekst.
$io->out('<success>Import zakończony pomyślnie</success>');
$io->out('<warning>To jest ostrzeżenie</warning>');
$io->err('<error>Wystąpił błąd</error>');
To podejście jest wygodne, bo sam tekst komendy pozostaje czytelny, a warstwa prezentacji jest oddzielona od logiki.
Progress bar
Do paska postępu CakePHP 5 udostępnia ProgressHelper. Helper można pobrać przez $io->helper('Progress'), zainicjalizować go parametrem total, a potem wywoływać increment() i draw() w trakcie przetwarzania.
$progress = $io->helper('Progress');
$progress->init([
'total' => 100,
]);
for ($i = 0; $i < 100; $i++) {
usleep(20000);
$progress->increment();
$progress->draw();
}
W dokumentacji i przykładach praktycznych ważne jest, żeby nie odświeżać paska zbyt często przy bardzo dużej liczbie rekordów, bo to może niepotrzebnie obciążać output. W wielu batchach lepiej aktualizować go co kilka, kilkadziesiąt albo kilkaset elementów.
Pełny przykład
Poniżej masz prostą komendę importującą dane z tablicy, z kolorami i paskiem postępu.
<?php
declare(strict_types=1);
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
final class ImportUsersCommand extends Command
{
public function execute(Arguments $args, ConsoleIo $io): int
{
$rows = [];
for ($i = 1; $i <= 50; $i++) {
$rows[] = ['id' => $i, 'email' => "user{$i}@example.com"];
}
$io->out('<info>Rozpoczynam import</info>');
$progress = $io->helper('Progress');
$progress->init([
'total' => count($rows),
]);
foreach ($rows as $row) {
usleep(30000);
$progress->increment();
$progress->draw();
}
$io->out();
$io->out('<success>Import zakończony</success>');
return static::CODE_SUCCESS;
}
}
Ten wzorzec jest dobrym punktem startowym, bo pokazuje cały przepływ: przygotowanie danych, komunikat startowy, inicjalizacja progress bara i finalny komunikat sukcesu.
Obsługa błędów
Dobra komenda CLI powinna umieć zakończyć się kodem błędu i wypisać zrozumiały komunikat. W CakePHP 5 warto używać STDERR przez $io->err() oraz zwracać kod inny niż 0, gdy coś się nie uda.
if ($file === null) {
$io->err('<error>Brak pliku wejściowego</error>');
return static::CODE_ERROR;
}
To ważne zwłaszcza wtedy, gdy komenda jest uruchamiana z crona, CI albo zewnętrznego orkiestratora.
Przetwarzanie dużych danych
Jeśli komenda ma pracować na większej liczbie rekordów, lepiej aktualizować progress bar rzadziej niż w każdej iteracji. W praktyce można zwiększać licznik lokalny i odświeżać UI co 100 rekordów, co ogranicza koszt renderowania.
$counter = 0;
foreach ($items as $item) {
$this->process($item);
$counter++;
if ($counter % 100 === 0) {
$progress->increment(100);
$progress->draw();
}
}
To dobry kompromis między informacją zwrotną a wydajnością.
Taki wzorzec szczególnie dobrze działa przy:
- importach CSV i JSON.
- migracjach danych.
- synchronizacji zewnętrznych systemów.
- przetwarzaniu batchy.
- zadaniach administracyjnych uruchamianych ręcznie.