Skip to content

Commit 08335b6

Browse files
committed
improved readme.md
1 parent 78e235d commit 08335b6

1 file changed

Lines changed: 225 additions & 56 deletions

File tree

readme.md

Lines changed: 225 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -83,40 +83,6 @@ The parser extracts option definitions from formatted help text:
8383
Each line defines one option. Option names must be separated from descriptions by at least two spaces.
8484

8585

86-
Switches and Options
87-
--------------------
88-
89-
**Switches** are flags without values, defined as `--verbose` or `-v, --verbose` in help text. They parse as `true` when present, `null` when absent.
90-
91-
**Options** accept values. Use `<value>` for required value, `[value]` for optional:
92-
93-
```
94-
--output <file> → value required, --output alone throws exception
95-
--format [type] → value optional, --format alone parses as true
96-
```
97-
98-
The option itself is always optional - not using it returns null (or the fallback if `(default: x)` is specified).
99-
100-
101-
Positional Arguments
102-
--------------------
103-
104-
Positional arguments are values without dashes. They can't be defined in help text - use the second parameter instead:
105-
106-
```php
107-
$parser->addFromHelp('
108-
-v, --verbose Enable verbose mode
109-
', [
110-
'file' => [], // required argument
111-
'output' => [Parser::Optional => true], // optional argument
112-
]);
113-
```
114-
115-
This accepts commands like `script.php input.txt` or `script.php -v input.txt output.txt`.
116-
117-
Arguments can appear anywhere on the command line - they don't have to come after options.
118-
119-
12086
Additional Configuration
12187
------------------------
12288

@@ -151,12 +117,178 @@ Available keys:
151117
| `Parser::Enum` | Array of allowed values |
152118

153119

120+
Fluent API
121+
==========
122+
123+
When you need more control over option definitions, use the fluent API with `addSwitch()`, `addOption()`, and `addArgument()` methods. This approach gives you access to all features including normalizers, enums, and precise control over each parameter:
124+
125+
```php
126+
use Nette\CommandLine\Parser;
127+
128+
$parser = new Parser;
129+
$parser
130+
->addSwitch('--verbose', '-v')
131+
->addOption('--output', '-o')
132+
->addArgument('file');
133+
134+
$args = $parser->parse();
135+
```
136+
137+
By default, `parse()` reads from `$_SERVER['argv']`. You can pass a custom array for testing:
138+
139+
```php
140+
$args = $parser->parse(['--verbose', '-o', 'out.txt', 'input.txt']);
141+
```
142+
143+
144+
Switches, Options, and Arguments
145+
--------------------------------
146+
147+
There are three types of command-line inputs:
148+
149+
**Switches** are flags without values, like `--verbose` or `-v`. They parse as `true` when present, `null` when absent:
150+
151+
```php
152+
$parser->addSwitch('--verbose', '-v');
153+
// --verbose → true
154+
// -v → true
155+
// (not used) → null
156+
```
157+
158+
**Options** accept values, like `--output file.txt`. The value can be separated by space or `=`:
159+
160+
```php
161+
$parser->addOption('--output', '-o');
162+
// --output file.txt → 'file.txt'
163+
// --output=file.txt → 'file.txt'
164+
// -o file.txt → 'file.txt'
165+
// --output → throws exception (value required)
166+
// (not used) → null
167+
```
168+
169+
Note that the option itself is always optional - not using it returns null. However, when used, the value is required by default. Set `optionalValue: true` to allow the option without a value (parses as `true`):
170+
171+
```php
172+
$parser->addOption('--format', '-f', optionalValue: true);
173+
// --format json → 'json'
174+
// --format → true
175+
// (not used) → null
176+
```
177+
178+
When the same option is used multiple times without `repeatable: true`, the last value wins:
179+
180+
```php
181+
$parser->addOption('--output', '-o');
182+
// -o first.txt -o second.txt → 'second.txt'
183+
```
184+
185+
**Arguments** are positional values without dashes. By default they are required. Set `optional: true` to make them optional:
186+
187+
```php
188+
$parser->addArgument('input');
189+
// script.php file.txt → 'file.txt'
190+
// (not used) → throws exception
191+
192+
$parser->addArgument('output', optional: true);
193+
// (not used) → null
194+
195+
$parser->addArgument('output', optional: true, fallback: 'out.txt');
196+
// (not used) → 'out.txt'
197+
```
198+
199+
Use `fallback` to specify the value when an 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:
200+
201+
```php
202+
$parser->addOption('--format', '-f', optionalValue: true, fallback: 'json');
203+
// --format xml → 'xml'
204+
// --format → true (option used without value)
205+
// (not used) → 'json' (fallback)
206+
```
207+
208+
Arguments can appear anywhere on the command line - they don't have to come after options:
209+
210+
```php
211+
// all of these are equivalent:
212+
// script.php --verbose input.txt
213+
// script.php input.txt --verbose
214+
```
215+
216+
217+
Restricting Values with Enum
218+
----------------------------
219+
220+
Limit accepted values to a specific set:
221+
222+
```php
223+
$parser->addOption('--format', '-f', enum: ['json', 'xml', 'csv']);
224+
// --format yaml → throws "Value of option --format must be json, or xml, or csv."
225+
```
226+
227+
228+
Repeatable Options
229+
------------------
230+
231+
Set `repeatable: true` to collect multiple values into an array:
232+
233+
```php
234+
$parser->addOption('--include', '-I', repeatable: true);
235+
// -I src -I lib → ['src', 'lib']
236+
// (not used) → []
237+
238+
$parser->addArgument('files', optional: true, repeatable: true);
239+
// a.txt b.txt → ['a.txt', 'b.txt']
240+
```
241+
242+
243+
Transforming Values
244+
-------------------
245+
246+
Use `normalizer` to transform parsed values:
247+
248+
```php
249+
$parser->addOption('--count', normalizer: fn($v) => (int) $v);
250+
// --count 42 → 42 (integer)
251+
```
252+
253+
For file path validation, use the built-in `normalizeRealPath`:
254+
255+
```php
256+
$parser->addOption('--config', normalizer: Parser::normalizeRealPath(...));
257+
// --config app.ini → '/full/path/to/app.ini'
258+
// --config missing.ini → throws "File path 'missing.ini' not found."
259+
```
260+
261+
262+
Mixing Both Approaches
263+
----------------------
264+
265+
You can combine `addFromHelp()` with fluent methods when you need normalizers for some options:
266+
267+
```php
268+
$parser
269+
->addFromHelp('
270+
-v, --verbose Enable verbose mode
271+
-q, --quiet Suppress output
272+
')
273+
->addOption('--config', '-c', normalizer: Parser::normalizeRealPath(...),
274+
description: 'Configuration file')
275+
->addArgument('input', description: 'Input file');
276+
```
277+
278+
154279
Error Handling
155280
--------------
156281

157282
The parser throws `\Exception` for invalid input:
158283

159284
```php
285+
use Nette\CommandLine\Parser;
286+
287+
$parser = new Parser;
288+
$parser
289+
->addOption('--output', '-o')
290+
->addArgument('file');
291+
160292
try {
161293
$args = $parser->parse();
162294
} catch (\Exception $e) {
@@ -175,7 +307,7 @@ Common error messages:
175307
| `Unexpected parameter foo.` | Extra positional argument |
176308
| `Value of option --format must be json, or xml.` | Value not in enum |
177309

178-
Use `isEmpty()` to check if no command-line arguments were provided:
310+
Use `isEmpty()` to check if no command-line arguments were provided (i.e., user ran just `script.php` with nothing after it):
179311

180312
```php
181313
if ($parser->isEmpty()) {
@@ -185,10 +317,46 @@ if ($parser->isEmpty()) {
185317
```
186318

187319

320+
Handling --help and --version
321+
-----------------------------
322+
323+
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:
324+
325+
```php
326+
$parser = new Parser;
327+
$parser
328+
->addSwitch('--help', '-h')
329+
->addSwitch('--version', '-V')
330+
->addArgument('input'); // required
331+
332+
// First, check info options (no validation, no exceptions)
333+
$info = $parser->parseOnly(['--help', '--version']);
334+
335+
if ($info['--help']) {
336+
$parser->help();
337+
exit;
338+
}
339+
340+
if ($info['--version']) {
341+
echo "1.0.0\n";
342+
exit;
343+
}
344+
345+
// Now do full parsing with validation
346+
$args = $parser->parse();
347+
```
348+
349+
The `parseOnly()` method:
350+
- Parses only the specified options, ignoring everything else
351+
- Respects aliases (`-h``--help`)
352+
- Never throws exceptions
353+
- Returns `null` for options that weren't used
354+
355+
188356
Complete Example
189357
================
190358

191-
Here's a practical script showing the typical usage pattern:
359+
Here's a real-world file converter script combining Parser and Console:
192360

193361
```php
194362
#!/usr/bin/env php
@@ -198,20 +366,26 @@ use Nette\CommandLine\Parser;
198366
require __DIR__ . '/vendor/autoload.php';
199367

200368
$parser = new Parser;
201-
$parser->addFromHelp('
202-
-h, --help Show this help
203-
-v, --verbose Show detailed output
204-
-n, --dry-run Show what would be done
205-
-f, --format [type] Output format (default: json)
206-
-o, --output <file> Output file
207-
', [
208-
'--format' => [
209-
Parser::Enum => ['json', 'xml', 'csv'],
210-
],
211-
'input' => [
212-
Parser::RealPath => true, // required positional argument
213-
],
214-
]);
369+
$parser
370+
->addFromHelp('
371+
-h, --help Show this help
372+
-v, --verbose Show detailed output
373+
-n, --dry-run Show what would be done
374+
-f, --format [type] Output format (default: json)
375+
-o, --output <file> Output file
376+
', [
377+
'--format' => [
378+
Parser::Enum => ['json', 'xml', 'csv'],
379+
],
380+
])
381+
->addArgument('input', normalizer: Parser::normalizeRealPath(...));
382+
383+
// Handle --help before validation (avoids "missing argument" error)
384+
if ($parser->isEmpty() || $parser->parseOnly(['--help'])['--help']) {
385+
echo "Usage: convert [options] <input>\n\n";
386+
$parser->help();
387+
exit;
388+
}
215389

216390
try {
217391
$args = $parser->parse();
@@ -220,12 +394,6 @@ try {
220394
exit(1);
221395
}
222396

223-
if ($parser->isEmpty() || $args['--help']) {
224-
echo "Usage: convert [options] <input>\n\n";
225-
$parser->help();
226-
exit;
227-
}
228-
229397
if ($args['--verbose']) {
230398
echo "Converting {$args['input']} to {$args['--format']}...\n";
231399
}
@@ -244,3 +412,4 @@ The script accepts commands like:
244412
- `convert input.txt` - convert with defaults
245413
- `convert -v --format xml input.txt` - verbose, XML format
246414
- `convert -o result.txt input.txt` - specify output file
415+
- `convert --help` - show help (works even without input file)

0 commit comments

Comments
 (0)