Nette Documentation Preview

syntax
Nette Command-Line
******************

.[perex]
A lightweight library for building command-line applications in PHP. It parses switches, options, and positional arguments, and helps you produce colorful terminal output with ANSI support.

Installation:

```shell
composer require nette/command-line
```

It requires PHP version 8.2 and supports PHP up to 8.5.


Parsing Command-Line Arguments
==============================

Every CLI script needs to handle arguments like `--verbose`, `-o output.txt`, or plain file names. The [api:Nette\CommandLine\Parser] class offers the fastest way to get started: just write your help text and let the parser extract option definitions from it:

```php
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();
```

That's it. The parser understands that `--verbose` is a switch, `--output` requires a value, and `--format` has an optional value with `json` as its fallback. Your help text stays in sync with the actual option definitions.

The `parse()` method returns an associative array. Keys match option names exactly as defined, including the dashes:

```php
[
	'--help' => true,         // or null if not used
	'--verbose' => null,
	'--output' => 'file.txt', // or null if not used
	'--format' => 'json',     // fallback from (default: json)
	'--include' => ['src', 'lib'],
	'--dry-run' => null,
]
```

By default, `parse()` reads from `$_SERVER['argv']`. You can pass a custom array, which is handy for testing:

```php
$args = $parser->parse(['--verbose', '-o', 'out.txt']);
```


Help Text Syntax
----------------

The parser extracts option definitions from formatted help text according to these rules:

| `--verbose`      | Switch (no value)
| `-v, --verbose`  | Switch with short alias
| `--output <file>`| Option with required value
| `--format [type]`| Option with optional value
| `(default: json)`| Sets the fallback value
| `<path>...`      | Repeatable option

Each line defines one option. Option names must be separated from their descriptions by at least two spaces.


Additional Configuration
------------------------

Some settings can't be expressed in the help text. Pass an array as the second parameter, keyed by option name:

```php
$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,
	],
]);
```

Available keys:

| `Parser::Repeatable` | Collect multiple values into an array
| `Parser::RealPath`   | Validate that the file exists and resolve it to an absolute path
| `Parser::Normalizer` | Transform function `fn($value) => ...`
| `Parser::Default`    | Fallback value (same as `(default: x)` in the help text)
| `Parser::Enum`       | Array of allowed values


Fluent API
==========

When you need more control over the option definitions, use the fluent API with the `addSwitch()`, `addOption()`, and `addArgument()` methods. This approach gives you access to all features, including normalizers, enums, and precise control over each parameter:

```php
use Nette\CommandLine\Parser;

$parser = new Parser;
$parser
	->addSwitch('--verbose', '-v')
	->addOption('--output', '-o')
	->addArgument('file');

$args = $parser->parse();
```

As with `addFromHelp()`, you can pass a custom array to `parse()` for testing:

```php
$args = $parser->parse(['--verbose', '-o', 'out.txt', 'input.txt']);
```


Switches, Options, and Arguments
--------------------------------

There are three types of command-line inputs:

**Switches** are flags without values, like `--verbose` or `-v`. They parse as `true` when present, `null` when absent:

```php
$parser->addSwitch('--verbose', '-v');
// --verbose  → true
// -v         → true
// (not used) → null
```

**Options** accept values, like `--output file.txt`. The value can be separated by a space or by `=`:

```php
$parser->addOption('--output', '-o');
// --output file.txt    → 'file.txt'
// --output=file.txt    → 'file.txt'
// -o file.txt          → 'file.txt'
// --output             → throws an exception (value required)
// (not used)           → null
```

Note that the option itself is always optional - not using it returns `null`. However, when it is used, the value is required by default. Set `optionalValue: true` to allow the option without a value (it then parses as `true`):

```php
$parser->addOption('--format', '-f', optionalValue: true);
// --format json        → 'json'
// --format             → true
// (not used)           → null
```

When the same option is used multiple times without `repeatable: true`, the last value wins:

```php
$parser->addOption('--output', '-o');
// -o first.txt -o second.txt  → 'second.txt'
```

**Arguments** are positional values without dashes. By default they are required. Set `optional: true` to make them optional:

```php
$parser->addArgument('input');
// script.php file.txt  → 'file.txt'
// (not used)           → throws an exception

$parser->addArgument('output', optional: true);
// (not used)           → null

$parser->addArgument('output', optional: true, fallback: 'out.txt');
// (not used)           → 'out.txt'
```

Use `fallback` to specify the value used when an optional option or argument is not provided. For options with `optionalValue: true`, note that using the option without a value still parses as `true`, while the fallback is used only when the option is not present at all:

```php
$parser->addOption('--format', '-f', optionalValue: true, fallback: 'json');
// --format xml  → 'xml'
// --format      → true (option used without a value)
// (not used)    → 'json' (fallback)
```

Arguments can appear anywhere on the command line - they don't have to come after the options:

```php
// all of these are equivalent:
// script.php --verbose input.txt
// script.php input.txt --verbose
```


Restricting Values with Enum
----------------------------

Limit the accepted values to a specific set:

```php
$parser->addOption('--format', '-f', enum: ['json', 'xml', 'csv']);
// --format yaml  → throws "Value of option --format must be json, or xml, or csv."
```


Repeatable Options
------------------

Set `repeatable: true` to collect multiple values into an array:

```php
$parser->addOption('--include', '-I', repeatable: true);
// -I src -I lib  → ['src', 'lib']
// (not used)     → []

$parser->addArgument('files', optional: true, repeatable: true);
// a.txt b.txt    → ['a.txt', 'b.txt']
```


Transforming Values
-------------------

Use a `normalizer` to transform the parsed value:

```php
$parser->addOption('--count', normalizer: fn($v) => (int) $v);
// --count 42  → 42 (integer)
```

For file path validation, use the built-in `normalizeRealPath`:

```php
$parser->addOption('--config', normalizer: Parser::normalizeRealPath(...));
// --config app.ini     → '/full/path/to/app.ini'
// --config missing.ini → throws "File path 'missing.ini' not found."
```


Mixing Both Approaches
----------------------

You can combine `addFromHelp()` with the fluent methods when you need normalizers for only some of the options:

```php
$parser
	->addFromHelp('
		-v, --verbose  Enable verbose mode
		-q, --quiet    Suppress output
	')
	->addOption('--config', '-c', normalizer: Parser::normalizeRealPath(...))
	->addArgument('input');
```


Error Handling
--------------

The parser throws `\Exception` for invalid input:

```php
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);
}
```

Common error messages:

| `Option --output requires argument.`          | Option used without its required value
| `Unknown option --foo.`                       | Unrecognized option
| `Missing required argument <file>.`           | Required argument not provided
| `Unexpected parameter foo.`                   | Extra positional argument
| `Value of option --format must be json, or xml.` | Value not in the enum

Use `isEmpty()` to check whether no command-line arguments were provided at all (i.e. the user ran just `script.php` with nothing after it):

```php
if ($parser->isEmpty()) {
	$parser->help();
	exit;
}
```


Handling --help and --version
-----------------------------

When your script has required arguments, running `script.php --help` would normally fail because the required argument is missing. Use `parseOnly()` to check for info options first:

```php
$parser = new Parser;
$parser
	->addSwitch('--help', '-h')
	->addSwitch('--version', '-V')
	->addArgument('input');  // required

// First, check the info options (no validation, no exceptions)
$info = $parser->parseOnly(['--help', '--version']);

if ($info['--help']) {
	$parser->help();
	exit;
}

if ($info['--version']) {
	echo "1.0.0\n";
	exit;
}

// Now do the full parsing with validation
$args = $parser->parse();
```

The `parseOnly()` method:
- parses only the specified options, ignoring everything else,
- respects aliases (`-h` → `--help`),
- never throws exceptions,
- returns `null` for options that weren't used.


Colorful Output
===============

The [api:Nette\CommandLine\Console] class wraps text in ANSI color codes so your output stands out in the terminal:

```php
use Nette\CommandLine\Console;

$console = new Console;
echo $console->color('red', 'Error!') . "\n";
echo $console->color('white/blue', 'White text on blue background') . "\n";
```

The color is given as `'foreground'` or `'foreground/background'`. Available colors are: `black`, `gray`, `silver`, `white`, `navy`, `blue`, `green`, `lime`, `teal`, `aqua`, `maroon`, `red`, `purple`, `fuchsia`, `olive`, and `yellow`.

Colors are enabled automatically only when the output supports them. The `color()` method returns a plain string when colors are disabled, so it's always safe to call. You can force the behavior manually:

```php
$console->useColors(false); // disable colors
$console->useColors(true);  // force colors on
```


Detecting the Terminal
----------------------

Two static methods help you decide whether to use terminal-only features. `detectColors()` returns `false` when the [NO_COLOR |https://no-color.org] environment variable is set, or when the output isn't a CLI terminal; the `FORCE_COLOR` variable overrides the terminal check:

```php
if (Console::detectColors()) {
	// the terminal supports ANSI colors
}
```

`detectTerminal()` tells you whether the output is an interactive terminal (a TTY). This is useful for auto-disabling features that only make sense in a terminal, such as progress indicators, line-rewriting output, or interactive prompts:

```php
if (Console::detectTerminal()) {
	// output goes to an interactive terminal, not a file or pipe
}
```


Complete Example
================

Here's a real-world file converter script combining `Parser` and `Console`:

```php
#!/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(...));

// Handle --help before validation (avoids the "missing argument" error)
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;
}

// ... conversion logic here ...

echo "Done!\n";
```

The script accepts commands like:

- `convert input.txt` - convert with the defaults
- `convert -v --format xml input.txt` - verbose, XML format
- `convert -o result.txt input.txt` - specify the output file
- `convert --help` - show the help (works even without the input file)


{{leftbar: nette:@menu-topics}}
{{sitename: Nette Documentation}}

Nette Command-Line

A lightweight library for building command-line applications in PHP. It parses switches, options, and positional arguments, and helps you produce colorful terminal output with ANSI support.

Installation:

composer require nette/command-line

It requires PHP version 8.2 and supports PHP up to 8.5.

Parsing Command-Line Arguments

Every CLI script needs to handle arguments like --verbose, -o output.txt, or plain file names. The Nette\CommandLine\Parser class offers the fastest way to get started: just write your help text and let the parser extract option definitions from it:

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();

That's it. The parser understands that --verbose is a switch, --output requires a value, and --format has an optional value with json as its fallback. Your help text stays in sync with the actual option definitions.

The parse() method returns an associative array. Keys match option names exactly as defined, including the dashes:

[
	'--help' => true,         // or null if not used
	'--verbose' => null,
	'--output' => 'file.txt', // or null if not used
	'--format' => 'json',     // fallback from (default: json)
	'--include' => ['src', 'lib'],
	'--dry-run' => null,
]

By default, parse() reads from $_SERVER['argv']. You can pass a custom array, which is handy for testing:

$args = $parser->parse(['--verbose', '-o', 'out.txt']);

Help Text Syntax

The parser extracts option definitions from formatted help text according to these rules:

--verbose Switch (no value)
-v, --verbose Switch with short alias
--output <file> Option with required value
--format [type] Option with optional value
(default: json) Sets the fallback value
<path>... Repeatable option

Each line defines one option. Option names must be separated from their descriptions by at least two spaces.

Additional Configuration

Some settings can't be expressed in the help text. Pass an array as the second parameter, keyed by option name:

$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,
	],
]);

Available keys:

Parser::Repeatable Collect multiple values into an array
Parser::RealPath Validate that the file exists and resolve it to an absolute path
Parser::Normalizer Transform function fn($value) => ...
Parser::Default Fallback value (same as (default: x) in the help text)
Parser::Enum Array of allowed values

Fluent API

When you need more control over the option definitions, use the fluent API with the addSwitch(), addOption(), and addArgument() methods. This approach gives you access to all features, including normalizers, enums, and precise control over each parameter:

use Nette\CommandLine\Parser;

$parser = new Parser;
$parser
	->addSwitch('--verbose', '-v')
	->addOption('--output', '-o')
	->addArgument('file');

$args = $parser->parse();

As with addFromHelp(), you can pass a custom array to parse() for testing:

$args = $parser->parse(['--verbose', '-o', 'out.txt', 'input.txt']);

Switches, Options, and Arguments

There are three types of command-line inputs:

Switches are flags without values, like --verbose or -v. They parse as true when present, null when absent:

$parser->addSwitch('--verbose', '-v');
// --verbose  → true
// -v         → true
// (not used) → null

Options accept values, like --output file.txt. The value can be separated by a space or by =:

$parser->addOption('--output', '-o');
// --output file.txt    → 'file.txt'
// --output=file.txt    → 'file.txt'
// -o file.txt          → 'file.txt'
// --output             → throws an exception (value required)
// (not used)           → null

Note that the option itself is always optional – not using it returns null. However, when it is used, the value is required by default. Set optionalValue: true to allow the option without a value (it then parses as true):

$parser->addOption('--format', '-f', optionalValue: true);
// --format json        → 'json'
// --format             → true
// (not used)           → null

When the same option is used multiple times without repeatable: true, the last value wins:

$parser->addOption('--output', '-o');
// -o first.txt -o second.txt  → 'second.txt'

Arguments are positional values without dashes. By default they are required. Set optional: true to make them optional:

$parser->addArgument('input');
// script.php file.txt  → 'file.txt'
// (not used)           → throws an exception

$parser->addArgument('output', optional: true);
// (not used)           → null

$parser->addArgument('output', optional: true, fallback: 'out.txt');
// (not used)           → 'out.txt'

Use fallback to specify the value used when an optional option or argument is not provided. For options with optionalValue: true, note that using the option without a value still parses as true, while the fallback is used only when the option is not present at all:

$parser->addOption('--format', '-f', optionalValue: true, fallback: 'json');
// --format xml  → 'xml'
// --format      → true (option used without a value)
// (not used)    → 'json' (fallback)

Arguments can appear anywhere on the command line – they don't have to come after the options:

// all of these are equivalent:
// script.php --verbose input.txt
// script.php input.txt --verbose

Restricting Values with Enum

Limit the accepted values to a specific set:

$parser->addOption('--format', '-f', enum: ['json', 'xml', 'csv']);
// --format yaml  → throws "Value of option --format must be json, or xml, or csv."

Repeatable Options

Set repeatable: true to collect multiple values into an array:

$parser->addOption('--include', '-I', repeatable: true);
// -I src -I lib  → ['src', 'lib']
// (not used)     → []

$parser->addArgument('files', optional: true, repeatable: true);
// a.txt b.txt    → ['a.txt', 'b.txt']

Transforming Values

Use a normalizer to transform the parsed value:

$parser->addOption('--count', normalizer: fn($v) => (int) $v);
// --count 42  → 42 (integer)

For file path validation, use the built-in normalizeRealPath:

$parser->addOption('--config', normalizer: Parser::normalizeRealPath(...));
// --config app.ini     → '/full/path/to/app.ini'
// --config missing.ini → throws "File path 'missing.ini' not found."

Mixing Both Approaches

You can combine addFromHelp() with the fluent methods when you need normalizers for only some of the options:

$parser
	->addFromHelp('
		-v, --verbose  Enable verbose mode
		-q, --quiet    Suppress output
	')
	->addOption('--config', '-c', normalizer: Parser::normalizeRealPath(...))
	->addArgument('input');

Error Handling

The parser throws \Exception for invalid input:

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);
}

Common error messages:

Option --output requires argument. Option used without its required value
Unknown option --foo. Unrecognized option
Missing required argument <file>. Required argument not provided
Unexpected parameter foo. Extra positional argument
Value of option --format must be json, or xml. Value not in the enum

Use isEmpty() to check whether no command-line arguments were provided at all (i.e. the user ran just script.php with nothing after it):

if ($parser->isEmpty()) {
	$parser->help();
	exit;
}

Handling –help and –version

When your script has required arguments, running script.php --help would normally fail because the required argument is missing. Use parseOnly() to check for info options first:

$parser = new Parser;
$parser
	->addSwitch('--help', '-h')
	->addSwitch('--version', '-V')
	->addArgument('input');  // required

// First, check the info options (no validation, no exceptions)
$info = $parser->parseOnly(['--help', '--version']);

if ($info['--help']) {
	$parser->help();
	exit;
}

if ($info['--version']) {
	echo "1.0.0\n";
	exit;
}

// Now do the full parsing with validation
$args = $parser->parse();

The parseOnly() method:

  • parses only the specified options, ignoring everything else,
  • respects aliases (-h--help),
  • never throws exceptions,
  • returns null for options that weren't used.

Colorful Output

The Nette\CommandLine\Console class wraps text in ANSI color codes so your output stands out in the terminal:

use Nette\CommandLine\Console;

$console = new Console;
echo $console->color('red', 'Error!') . "\n";
echo $console->color('white/blue', 'White text on blue background') . "\n";

The color is given as 'foreground' or 'foreground/background'. Available colors are: black, gray, silver, white, navy, blue, green, lime, teal, aqua, maroon, red, purple, fuchsia, olive, and yellow.

Colors are enabled automatically only when the output supports them. The color() method returns a plain string when colors are disabled, so it's always safe to call. You can force the behavior manually:

$console->useColors(false); // disable colors
$console->useColors(true);  // force colors on

Detecting the Terminal

Two static methods help you decide whether to use terminal-only features. detectColors() returns false when the NO_COLOR environment variable is set, or when the output isn't a CLI terminal; the FORCE_COLOR variable overrides the terminal check:

if (Console::detectColors()) {
	// the terminal supports ANSI colors
}

detectTerminal() tells you whether the output is an interactive terminal (a TTY). This is useful for auto-disabling features that only make sense in a terminal, such as progress indicators, line-rewriting output, or interactive prompts:

if (Console::detectTerminal()) {
	// output goes to an interactive terminal, not a file or pipe
}

Complete Example

Here's a real-world file converter script combining Parser and 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(...));

// Handle --help before validation (avoids the "missing argument" error)
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;
}

// ... conversion logic here ...

echo "Done!\n";

The script accepts commands like:

  • convert input.txt – convert with the defaults
  • convert -v --format xml input.txt – verbose, XML format
  • convert -o result.txt input.txt – specify the output file
  • convert --help – show the help (works even without the input file)