Nette Command-Line
Odlehčená knihovna pro tvorbu konzolových aplikací v PHP. Zpracuje přepínače, volby i poziční argumenty a pomůže vám vytvářet barevný výstup do terminálu s podporou ANSI.
Instalace:
composer require nette/command-line
Vyžaduje PHP verze 8.2 a podporuje PHP až do verze 8.5.
Zpracování argumentů příkazové řádky
Každý konzolový skript musí umět zpracovat argumenty jako --verbose, -o output.txt nebo prostá
jména souborů. Třída Nette\CommandLine\Parser nabízí nejrychlejší
způsob, jak začít: stačí napsat text nápovědy a parser z něj definice voleb sám vyextrahuje:
use Nette\CommandLine\Parser;
$parser = new Parser;
$parser->addFromHelp('
-h, --help Show this help
-v, --verbose Enable verbose mode
-o, --output <file> Output file
-f, --format [type] Output format (default: json)
-I, --include <path>... Include paths
--dry-run Show what would be done
');
$args = $parser->parse();
A je to. Parser pozná, že --verbose je přepínač, --output vyžaduje hodnotu a
--format má nepovinnou hodnotu s výchozí hodnotou json. Text nápovědy tak zůstává v souladu se
skutečnými definicemi voleb.
Metoda parse() vrací asociativní pole. Klíče přesně odpovídají názvům voleb tak, jak jste je definovali,
včetně pomlček:
[
'--help' => true, // nebo null, pokud nebylo použito
'--verbose' => null,
'--output' => 'file.txt', // nebo null, pokud nebylo použito
'--format' => 'json', // výchozí hodnota z (default: json)
'--include' => ['src', 'lib'],
'--dry-run' => null,
]
Ve výchozím nastavení čte parse() z $_SERVER['argv']. Můžete předat vlastní pole, což se
hodí při testování:
$args = $parser->parse(['--verbose', '-o', 'out.txt']);
Syntaxe textu nápovědy
Parser extrahuje definice voleb z formátovaného textu nápovědy podle těchto pravidel:
--verbose |
přepínač (bez hodnoty) |
-v, --verbose |
přepínač s krátkým aliasem |
--output <file> |
volba s povinnou hodnotou |
--format [type] |
volba s nepovinnou hodnotou |
(default: json) |
nastaví výchozí hodnotu |
<path>... |
opakovatelná volba |
Každý řádek definuje jednu volbu. Názvy voleb musí být od popisu odděleny alespoň dvěma mezerami.
Další konfigurace
Některá nastavení nelze v textu nápovědy vyjádřit. Předejte je jako pole ve druhém parametru, kde klíčem je název volby:
$parser->addFromHelp('
-c, --config <file> Configuration file
-I, --include <path> Include path
-n, --count <num> Number of iterations
', [
'--config' => [
Parser::RealPath => true,
],
'--include' => [
Parser::Repeatable => true,
],
'--count' => [
Parser::Normalizer => fn($v) => (int) $v,
],
]);
Dostupné klíče:
Parser::Repeatable |
sbírá více hodnot do pole |
Parser::RealPath |
ověří, že soubor existuje, a převede cestu na absolutní |
Parser::Normalizer |
transformační funkce fn($value) => ... |
Parser::Default |
výchozí hodnota (totéž jako (default: x) v textu nápovědy) |
Parser::Enum |
pole povolených hodnot |
Plynulé API
Když potřebujete nad definicemi voleb větší kontrolu, použijte plynulé API s metodami addSwitch(),
addOption() a addArgument(). Tento přístup vám zpřístupní všechny možnosti, včetně
normalizátorů, výčtů a přesného řízení každého parametru:
use Nette\CommandLine\Parser;
$parser = new Parser;
$parser
->addSwitch('--verbose', '-v')
->addOption('--output', '-o')
->addArgument('file');
$args = $parser->parse();
Stejně jako u addFromHelp() můžete metodě parse() předat vlastní pole pro testování:
$args = $parser->parse(['--verbose', '-o', 'out.txt', 'input.txt']);
Přepínače, volby a argumenty
Existují tři typy vstupů z příkazové řádky:
Přepínače jsou příznaky bez hodnoty, jako --verbose nebo -v. Když jsou přítomné,
vyhodnotí se jako true, jinak jako null:
$parser->addSwitch('--verbose', '-v');
// --verbose → true
// -v → true
// (nepoužito) → null
Volby přijímají hodnoty, jako --output file.txt. Hodnotu lze oddělit mezerou nebo znakem
=:
$parser->addOption('--output', '-o');
// --output file.txt → 'file.txt'
// --output=file.txt → 'file.txt'
// -o file.txt → 'file.txt'
// --output → vyhodí výjimku (hodnota je povinná)
// (nepoužito) → null
Všimněte si, že samotná volba je vždy nepovinná – její nepoužití vrátí null. Pokud ji ale
použijete, je hodnota ve výchozím nastavení povinná. Nastavením optionalValue: true povolíte volbu i bez
hodnoty (pak se vyhodnotí jako true):
$parser->addOption('--format', '-f', optionalValue: true);
// --format json → 'json'
// --format → true
// (nepoužito) → null
Když se stejná volba použije vícekrát bez repeatable: true, vyhraje poslední hodnota:
$parser->addOption('--output', '-o');
// -o first.txt -o second.txt → 'second.txt'
Argumenty jsou poziční hodnoty bez pomlček. Ve výchozím nastavení jsou povinné. Nastavením
optional: true je učiníte nepovinnými:
$parser->addArgument('input');
// script.php file.txt → 'file.txt'
// (nepoužito) → vyhodí výjimku
$parser->addArgument('output', optional: true);
// (nepoužito) → null
$parser->addArgument('output', optional: true, fallback: 'out.txt');
// (nepoužito) → 'out.txt'
Pomocí fallback určíte hodnotu, která se použije, když nepovinná volba nebo argument nejsou zadány.
U voleb s optionalValue: true platí, že použití volby bez hodnoty se stále vyhodnotí jako true,
zatímco výchozí hodnota se použije pouze tehdy, když volba není přítomna vůbec:
$parser->addOption('--format', '-f', optionalValue: true, fallback: 'json');
// --format xml → 'xml'
// --format → true (volba použita bez hodnoty)
// (nepoužito) → 'json' (výchozí hodnota)
Argumenty se mohou na příkazové řádce objevit kdekoli – nemusí následovat až za volbami:
// všechny tyto zápisy jsou rovnocenné:
// script.php --verbose input.txt
// script.php input.txt --verbose
Omezení hodnot pomocí výčtu
Omezte přijímané hodnoty na konkrétní množinu:
$parser->addOption('--format', '-f', enum: ['json', 'xml', 'csv']);
// --format yaml → vyhodí "Value of option --format must be json, or xml, or csv."
Opakovatelné volby
Nastavením repeatable: true shromáždíte více hodnot do pole:
$parser->addOption('--include', '-I', repeatable: true);
// -I src -I lib → ['src', 'lib']
// (nepoužito) → []
$parser->addArgument('files', optional: true, repeatable: true);
// a.txt b.txt → ['a.txt', 'b.txt']
Transformace hodnot
Pomocí normalizer transformujete zpracovanou hodnotu:
$parser->addOption('--count', normalizer: fn($v) => (int) $v);
// --count 42 → 42 (celé číslo)
Pro ověření cesty k souboru použijte vestavěný normalizeRealPath:
$parser->addOption('--config', normalizer: Parser::normalizeRealPath(...));
// --config app.ini → '/full/path/to/app.ini'
// --config missing.ini → vyhodí "File path 'missing.ini' not found."
Kombinace obou přístupů
Když potřebujete normalizátory jen pro některé volby, můžete addFromHelp() zkombinovat s plynulými
metodami:
$parser
->addFromHelp('
-v, --verbose Enable verbose mode
-q, --quiet Suppress output
')
->addOption('--config', '-c', normalizer: Parser::normalizeRealPath(...))
->addArgument('input');
Ošetření chyb
Parser vyhodí \Exception při neplatném vstupu:
use Nette\CommandLine\Parser;
$parser = new Parser;
$parser
->addOption('--output', '-o')
->addArgument('file');
try {
$args = $parser->parse();
} catch (\Exception $e) {
fwrite(STDERR, "Error: {$e->getMessage()}\n");
exit(1);
}
Běžné chybové zprávy:
Option --output requires argument. |
volba použita bez povinné hodnoty |
Unknown option --foo. |
neznámá volba |
Missing required argument <file>. |
nezadán povinný argument |
Unexpected parameter foo. |
přebývající poziční argument |
Value of option --format must be json, or xml. |
hodnota není ve výčtu |
Metodou isEmpty() zjistíte, zda nebyly zadány vůbec žádné argumenty příkazové řádky (tj. uživatel
spustil pouze script.php bez čehokoli za ním):
if ($parser->isEmpty()) {
$parser->help();
exit;
}
Obsluha –help a –version
Když má váš skript povinné argumenty, spuštění script.php --help by normálně selhalo, protože chybí
povinný argument. Použijte parseOnly(), abyste informativní volby zkontrolovali jako první:
$parser = new Parser;
$parser
->addSwitch('--help', '-h')
->addSwitch('--version', '-V')
->addArgument('input'); // povinný
// Nejprve zkontrolujeme informativní volby (bez validace, bez výjimek)
$info = $parser->parseOnly(['--help', '--version']);
if ($info['--help']) {
$parser->help();
exit;
}
if ($info['--version']) {
echo "1.0.0\n";
exit;
}
// Teprve teď provedeme plné zpracování s validací
$args = $parser->parse();
Metoda parseOnly():
- zpracuje pouze zadané volby a vše ostatní ignoruje,
- respektuje aliasy (
-h→--help), - nikdy nevyhodí výjimku,
- vrátí
nullpro volby, které nebyly použity.
Barevný výstup
Třída Nette\CommandLine\Console obalí text ANSI kódy barev, takže váš výstup v terminálu vynikne:
use Nette\CommandLine\Console;
$console = new Console;
echo $console->color('red', 'Error!') . "\n";
echo $console->color('white/blue', 'White text on blue background') . "\n";
Barva se zadává jako 'popředí' nebo 'popředí/pozadí'. Dostupné barvy jsou:
black, gray, silver, white, navy, blue,
green, lime, teal, aqua, maroon, red,
purple, fuchsia, olive a yellow.
Barvy se zapnou automaticky jen tehdy, když je výstup podporuje. Metoda color() vrátí při vypnutých barvách
prostý řetězec, takže je vždy bezpečné ji volat. Chování můžete nastavit i ručně:
$console->useColors(false); // vypne barvy
$console->useColors(true); // vynutí barvy
Detekce terminálu
Dvě statické metody vám pomohou rozhodnout, zda použít funkce určené jen pro terminál. detectColors()
vrátí false, když je nastavená proměnná prostředí NO_COLOR nebo když
výstup není konzolový terminál; proměnná FORCE_COLOR kontrolu terminálu přebije:
if (Console::detectColors()) {
// terminál podporuje ANSI barvy
}
detectTerminal() vám řekne, zda je výstupem interaktivní terminál (TTY). To se hodí pro automatické
vypnutí funkcí, které dávají smysl jen v terminálu, jako jsou ukazatele průběhu, přepisování řádků nebo
interaktivní dotazy:
if (Console::detectTerminal()) {
// výstup jde do interaktivního terminálu, ne do souboru či roury
}
Kompletní příklad
Tady je skript konvertoru souborů z praxe, který kombinuje Parser a Console:
#!/usr/bin/env php
<?php
use Nette\CommandLine\Parser;
require __DIR__ . '/vendor/autoload.php';
$parser = new Parser;
$parser
->addFromHelp('
-h, --help Show this help
-v, --verbose Show detailed output
-n, --dry-run Show what would be done
-f, --format [type] Output format (default: json)
-o, --output <file> Output file
', [
'--format' => [
Parser::Enum => ['json', 'xml', 'csv'],
],
])
->addArgument('input', normalizer: Parser::normalizeRealPath(...));
// Obsloužíme --help ještě před validací (vyhneme se chybě "missing argument")
if ($parser->isEmpty() || $parser->parseOnly(['--help'])['--help']) {
echo "Usage: convert [options] <input>\n\n";
$parser->help();
exit;
}
try {
$args = $parser->parse();
} catch (\Exception $e) {
fwrite(STDERR, "Error: {$e->getMessage()}\n");
exit(1);
}
if ($args['--verbose']) {
echo "Converting {$args['input']} to {$args['--format']}...\n";
}
if ($args['--dry-run']) {
echo "Dry run: no changes made.\n";
exit;
}
// ... zde následuje logika konverze ...
echo "Done!\n";
Skript přijímá příkazy jako:
convert input.txt– konverze s výchozím nastavenímconvert -v --format xml input.txt– podrobný výstup, formát XMLconvert -o result.txt input.txt– určení výstupního souboruconvert --help– zobrazení nápovědy (funguje i bez vstupního souboru)