Nette Command-Line
Una libreria leggera per costruire applicazioni da riga di comando in PHP. Analizza switch, opzioni e argomenti posizionali e vi aiuta a produrre output colorato nel terminale con il supporto ANSI.
Installazione:
composer require nette/command-line
Richiede PHP versione 8.2 e supporta PHP fino alla 8.5.
Analisi degli argomenti da riga di comando
Ogni script CLI deve gestire argomenti come --verbose, -o output.txt oppure semplici nomi di file. La
classe Nette\CommandLine\Parser offre il modo
più rapido di cominciare: basta scrivere il vostro testo di aiuto e lasciare che il parser ne ricavi le definizioni delle
opzioni:
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();
Ecco fatto. Il parser capisce che --verbose è uno switch, che --output richiede un valore e che
--format ha un valore facoltativo con json come ripiego. Il vostro testo di aiuto resta allineato alle
definizioni reali delle opzioni.
Il metodo parse() restituisce un array associativo. Le chiavi corrispondono esattamente ai nomi delle opzioni come
sono definiti, trattini compresi:
[
'--help' => true, // oppure null se non è stata usata
'--verbose' => null,
'--output' => 'file.txt', // oppure null se non è stata usata
'--format' => 'json', // ripiego da (default: json)
'--include' => ['src', 'lib'],
'--dry-run' => null,
]
Per impostazione predefinita parse() legge da $_SERVER['argv']. Potete passare un array vostro, il
che torna comodo per i test:
$args = $parser->parse(['--verbose', '-o', 'out.txt']);
Sintassi del testo di aiuto
Il parser ricava le definizioni delle opzioni dal testo di aiuto formattato secondo queste regole:
--verbose |
Switch (senza valore) |
-v, --verbose |
Switch con alias breve |
--output <file> |
Opzione con valore obbligatorio |
--format [type] |
Opzione con valore facoltativo |
(default: json) |
Imposta il valore di ripiego |
<path>... |
Opzione ripetibile |
Ogni riga definisce un'opzione. I nomi delle opzioni devono essere separati dalle loro descrizioni da almeno due spazi.
Configurazione aggiuntiva
Alcune impostazioni non si possono esprimere nel testo di aiuto. Passate come secondo parametro un array con chiavi corrispondenti ai nomi delle opzioni:
$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,
],
]);
Chiavi disponibili:
Parser::Repeatable |
Raccoglie più valori in un array |
Parser::RealPath |
Verifica che il file esista e lo risolve in un percorso assoluto |
Parser::Normalizer |
Funzione di trasformazione fn($value) => ... |
Parser::Default |
Valore di ripiego (uguale a (default: x) nel testo di aiuto) |
Parser::Enum |
Array dei valori consentiti |
API fluent
Quando vi serve più controllo sulle definizioni delle opzioni, usate l'API fluent con i metodi addSwitch(),
addOption() e addArgument(). Questo approccio vi dà accesso a tutte le funzionalità, compresi
normalizzatori, enum e controllo preciso su ogni parametro:
use Nette\CommandLine\Parser;
$parser = new Parser;
$parser
->addSwitch('--verbose', '-v')
->addOption('--output', '-o')
->addArgument('file');
$args = $parser->parse();
Come con addFromHelp(), potete passare a parse() un array vostro per i test:
$args = $parser->parse(['--verbose', '-o', 'out.txt', 'input.txt']);
Switch, opzioni e argomenti
Ci sono tre tipi di input da riga di comando:
Gli switch sono flag senza valore, come --verbose oppure -v. Vengono analizzati come
true quando sono presenti, come null quando mancano:
$parser->addSwitch('--verbose', '-v');
// --verbose → true
// -v → true
// (non usato) → null
Le opzioni accettano valori, come --output file.txt. Il valore si può separare con uno spazio oppure con
=:
$parser->addOption('--output', '-o');
// --output file.txt → 'file.txt'
// --output=file.txt → 'file.txt'
// -o file.txt → 'file.txt'
// --output → lancia un'eccezione (valore obbligatorio)
// (non usata) → null
Notate che l'opzione in sé è sempre facoltativa: non usarla restituisce null. Quando però viene usata, il
valore è obbligatorio per impostazione predefinita. Impostate optionalValue: true per permettere l'opzione senza
valore (viene allora analizzata come true):
$parser->addOption('--format', '-f', optionalValue: true);
// --format json → 'json'
// --format → true
// (non usata) → null
Quando la stessa opzione viene usata più volte senza repeatable: true, vince l'ultimo valore:
$parser->addOption('--output', '-o');
// -o first.txt -o second.txt → 'second.txt'
Gli argomenti sono valori posizionali senza trattini. Per impostazione predefinita sono obbligatori. Impostate
optional: true per renderli facoltativi:
$parser->addArgument('input');
// script.php file.txt → 'file.txt'
// (non usato) → lancia un'eccezione
$parser->addArgument('output', optional: true);
// (non usato) → null
$parser->addArgument('output', optional: true, fallback: 'out.txt');
// (non usato) → 'out.txt'
Usate fallback per indicare il valore usato quando un'opzione o un argomento facoltativo non viene fornito. Per
le opzioni con optionalValue: true tenete presente che usare l'opzione senza valore viene comunque analizzato come
true, mentre il ripiego si usa solo quando l'opzione non è presente affatto:
$parser->addOption('--format', '-f', optionalValue: true, fallback: 'json');
// --format xml → 'xml'
// --format → true (opzione usata senza valore)
// (non usata) → 'json' (ripiego)
Gli argomenti possono comparire in qualsiasi punto della riga di comando, non devono per forza venire dopo le opzioni:
// tutte queste forme sono equivalenti:
// script.php --verbose input.txt
// script.php input.txt --verbose
Limitare i valori con enum
Limitate i valori accettati a un insieme determinato:
$parser->addOption('--format', '-f', enum: ['json', 'xml', 'csv']);
// --format yaml → lancia "Value of option --format must be json, or xml, or csv."
Opzioni ripetibili
Impostate repeatable: true per raccogliere più valori in un array:
$parser->addOption('--include', '-I', repeatable: true);
// -I src -I lib → ['src', 'lib']
// (non usata) → []
$parser->addArgument('files', optional: true, repeatable: true);
// a.txt b.txt → ['a.txt', 'b.txt']
Trasformare i valori
Usate un normalizer per trasformare il valore analizzato:
$parser->addOption('--count', normalizer: fn($v) => (int) $v);
// --count 42 → 42 (intero)
Per validare i percorsi dei file usate il normalizeRealPath integrato:
$parser->addOption('--config', normalizer: Parser::normalizeRealPath(...));
// --config app.ini → '/full/path/to/app.ini'
// --config missing.ini → lancia "File path 'missing.ini' not found."
Combinare i due approcci
Potete combinare addFromHelp() con i metodi fluent quando vi servono i normalizzatori solo per alcune
opzioni:
$parser
->addFromHelp('
-v, --verbose Enable verbose mode
-q, --quiet Suppress output
')
->addOption('--config', '-c', normalizer: Parser::normalizeRealPath(...))
->addArgument('input');
Gestione degli errori
Il parser lancia \Exception per gli input non validi:
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);
}
Messaggi di errore più frequenti:
Option --output requires argument. |
Opzione usata senza il valore obbligatorio |
Unknown option --foo. |
Opzione non riconosciuta |
Missing required argument <file>. |
Argomento obbligatorio non fornito |
Unexpected parameter foo. |
Argomento posizionale in più |
Value of option --format must be json, or xml. |
Valore non presente nell'enum |
Usate isEmpty() per verificare se non è stato fornito alcun argomento da riga di comando (cioè se l'utente ha
lanciato solo script.php senza nulla dopo):
if ($parser->isEmpty()) {
$parser->help();
exit;
}
Gestire –help e –version
Quando il vostro script ha argomenti obbligatori, lanciare script.php --help fallirebbe normalmente perché manca
l'argomento obbligatorio. Usate parseOnly() per controllare prima le opzioni informative:
$parser = new Parser;
$parser
->addSwitch('--help', '-h')
->addSwitch('--version', '-V')
->addArgument('input'); // obbligatorio
// prima controlliamo le opzioni informative (nessuna validazione, nessuna eccezione)
$info = $parser->parseOnly(['--help', '--version']);
if ($info['--help']) {
$parser->help();
exit;
}
if ($info['--version']) {
echo "1.0.0\n";
exit;
}
// ora facciamo l'analisi completa con la validazione
$args = $parser->parse();
Il metodo parseOnly():
- analizza solo le opzioni indicate, ignorando tutto il resto,
- rispetta gli alias (
-h→--help), - non lancia mai eccezioni,
- restituisce
nullper le opzioni che non sono state usate.
Output colorato
La classe Nette\CommandLine\Console racchiude il testo nei codici colore ANSI, così il vostro output spicca nel terminale:
use Nette\CommandLine\Console;
$console = new Console;
echo $console->color('red', 'Error!') . "\n";
echo $console->color('white/blue', 'White text on blue background') . "\n";
Il colore si indica come 'primo piano' oppure 'primo piano/sfondo'. I colori disponibili sono:
black, gray, silver, white, navy, blue,
green, lime, teal, aqua, maroon, red,
purple, fuchsia, olive e yellow.
I colori si attivano automaticamente solo quando l'output li supporta. Il metodo color() restituisce una stringa
semplice quando i colori sono disattivati, quindi si può sempre chiamare senza rischi. Il comportamento lo potete forzare
a mano:
$console->useColors(false); // disattiva i colori
$console->useColors(true); // forza i colori
Rilevare il terminale
Due metodi statici vi aiutano a decidere se usare funzionalità legate al terminale. detectColors() restituisce
false quando è impostata la variabile d'ambiente NO_COLOR, oppure quando l'output
non è un terminale CLI; la variabile FORCE_COLOR ha la precedenza sul controllo del terminale:
if (Console::detectColors()) {
// il terminale supporta i colori ANSI
}
detectTerminal() vi dice se l'output è un terminale interattivo (un TTY). Torna utile per disattivare
automaticamente le funzionalità che hanno senso solo in un terminale, come gli indicatori di avanzamento, l'output che riscrive
la riga o le richieste interattive:
if (Console::detectTerminal()) {
// l'output va a un terminale interattivo, non a un file o a una pipe
}
Esempio completo
Ecco uno script reale di conversione file che combina Parser e 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(...));
// gestiamo --help prima della validazione (evita l'errore "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;
}
// ... qui la logica di conversione ...
echo "Done!\n";
Lo script accetta comandi come:
convert input.txt– conversione con i valori predefiniticonvert -v --format xml input.txt– modalità verbose, formato XMLconvert -o result.txt input.txt– indica il file di outputconvert --help– mostra l'aiuto (funziona anche senza il file di input)