{"id":14965415,"url":"https://github.com/nette/command-line","last_synced_at":"2026-02-23T05:03:33.305Z","repository":{"id":19968477,"uuid":"23235463","full_name":"nette/command-line","owner":"nette","description":"⌨ Command line options and arguments parser.","archived":false,"fork":false,"pushed_at":"2024-12-15T19:42:31.000Z","size":60,"stargazers_count":36,"open_issues_count":0,"forks_count":5,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-03-29T03:06:28.393Z","etag":null,"topics":["command-line","component","console","nette","nette-framework","php"],"latest_commit_sha":null,"homepage":"https://nette.org","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nette.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"dg","custom":"https://nette.org/donate"}},"created_at":"2014-08-22T18:31:02.000Z","updated_at":"2024-12-16T08:22:07.000Z","dependencies_parsed_at":"2024-06-18T15:42:30.781Z","dependency_job_id":null,"html_url":"https://github.com/nette/command-line","commit_stats":{"total_commits":63,"total_committers":2,"mean_commits":31.5,"dds":"0.015873015873015928","last_synced_commit":"3d36c3e9510f9e1939d1896b81349c7ab55321c8"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fcommand-line","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fcommand-line/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fcommand-line/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fcommand-line/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nette","download_url":"https://codeload.github.com/nette/command-line/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247284949,"owners_count":20913704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-line","component","console","nette","nette-framework","php"],"created_at":"2024-09-24T13:34:43.420Z","updated_at":"2026-02-23T05:03:33.299Z","avatar_url":"https://github.com/nette.png","language":"PHP","readme":"Nette Command-Line\n==================\n\n[![Downloads this Month](https://img.shields.io/packagist/dm/nette/command-line.svg)](https://packagist.org/packages/nette/command-line)\n[![Tests](https://github.com/nette/command-line/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/command-line/actions)\n[![Coverage Status](https://coveralls.io/repos/github/nette/command-line/badge.svg?branch=master)](https://coveralls.io/github/nette/command-line?branch=master)\n[![Latest Stable Version](https://poser.pugx.org/nette/command-line/v/stable)](https://github.com/nette/command-line/releases)\n[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/command-line/blob/master/license.md)\n\nA lightweight library for building command-line applications in PHP. It provides:\n\n- **Argument parsing** with switches, options, and positional arguments\n- **Colorful terminal output** with ANSI support\n\nInstall it using Composer:\n\n```\ncomposer require nette/command-line\n```\n\nIt requires PHP version 8.2 and supports PHP up to 8.5.\n\nIf you like Nette, **[please make a donation now](https://nette.org/donate)**. Thank you!\n\n\nParsing Command-Line Arguments\n==============================\n\nEvery CLI script needs to handle arguments like `--verbose`, `-o output.txt`, or plain file names. The `Parser` class offers the fastest way to get started: just write your help text and let the parser extract option definitions from it:\n\n```php\nuse Nette\\CommandLine\\Parser;\n\n$parser = new Parser;\n$parser-\u003eaddFromHelp('\n\t-h, --help              Show this help\n\t-v, --verbose           Enable verbose mode\n\t-o, --output \u003cfile\u003e     Output file\n\t-f, --format [type]     Output format (default: json)\n\t-I, --include \u003cpath\u003e... Include paths\n\t--dry-run               Show what would be done\n');\n\n$args = $parser-\u003eparse();\n```\n\nThat's it. The parser understands that `--verbose` is a switch, `--output` requires a value, `--format` has an optional value with `json` as fallback. Your help text stays in sync with actual option definitions.\n\nThe `parse()` method returns an associative array. Keys match option names exactly as defined, including the dashes:\n\n```php\n[\n\t'--help' =\u003e true,         // or null if not used\n\t'--verbose' =\u003e null,\n\t'--output' =\u003e 'file.txt', // or null if not used\n\t'--format' =\u003e 'json',     // fallback from (default: json)\n\t'--include' =\u003e ['src', 'lib'],\n\t'--dry-run' =\u003e null,\n]\n```\n\nBy default, `parse()` reads from `$_SERVER['argv']`. You can pass a custom array for testing:\n\n```php\n$args = $parser-\u003eparse(['--verbose', '-o', 'out.txt']);\n```\n\n\nHelp Text Syntax\n----------------\n\nThe parser extracts option definitions from formatted help text:\n\n| Syntax | Meaning |\n|--------|---------|\n| `--verbose` | Switch (no value) |\n| `-v, --verbose` | Switch with short alias |\n| `--output \u003cfile\u003e` | Option with required value |\n| `--format [type]` | Option with optional value |\n| `(default: json)` | Sets fallback value |\n| `\u003cpath\u003e...` | Repeatable option |\n\nEach line defines one option. Option names must be separated from descriptions by at least two spaces.\n\n\nAdditional Configuration\n------------------------\n\nSome settings can't be expressed in help text. Pass an array as the second parameter, keyed by option name:\n\n```php\n$parser-\u003eaddFromHelp('\n\t-c, --config \u003cfile\u003e   Configuration file\n\t-I, --include \u003cpath\u003e  Include path\n\t-n, --count \u003cnum\u003e     Number of iterations\n', [\n\t'--config' =\u003e [\n\t\tParser::RealPath =\u003e true,\n\t],\n\t'--include' =\u003e [\n\t\tParser::Repeatable =\u003e true,\n\t],\n\t'--count' =\u003e [\n\t\tParser::Normalizer =\u003e fn($v) =\u003e (int) $v,\n\t],\n]);\n```\n\nAvailable keys:\n\n| Key | Description |\n|-----|-------------|\n| `Parser::Repeatable` | Collect multiple values into array |\n| `Parser::RealPath` | Validate file exists and resolve to absolute path |\n| `Parser::Normalizer` | Transform function `fn($value) =\u003e ...` |\n| `Parser::Default` | Fallback value (same as `(default: x)` in help text) |\n| `Parser::Enum` | Array of allowed values |\n\n\nFluent API\n==========\n\nWhen 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:\n\n```php\nuse Nette\\CommandLine\\Parser;\n\n$parser = new Parser;\n$parser\n\t-\u003eaddSwitch('--verbose', '-v')\n\t-\u003eaddOption('--output', '-o')\n\t-\u003eaddArgument('file');\n\n$args = $parser-\u003eparse();\n```\n\nBy default, `parse()` reads from `$_SERVER['argv']`. You can pass a custom array for testing:\n\n```php\n$args = $parser-\u003eparse(['--verbose', '-o', 'out.txt', 'input.txt']);\n```\n\n\nSwitches, Options, and Arguments\n--------------------------------\n\nThere are three types of command-line inputs:\n\n**Switches** are flags without values, like `--verbose` or `-v`. They parse as `true` when present, `null` when absent:\n\n```php\n$parser-\u003eaddSwitch('--verbose', '-v');\n// --verbose  → true\n// -v         → true\n// (not used) → null\n```\n\n**Options** accept values, like `--output file.txt`. The value can be separated by space or `=`:\n\n```php\n$parser-\u003eaddOption('--output', '-o');\n// --output file.txt    → 'file.txt'\n// --output=file.txt    → 'file.txt'\n// -o file.txt          → 'file.txt'\n// --output             → throws exception (value required)\n// (not used)           → null\n```\n\nNote 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`):\n\n```php\n$parser-\u003eaddOption('--format', '-f', optionalValue: true);\n// --format json        → 'json'\n// --format             → true\n// (not used)           → null\n```\n\nWhen the same option is used multiple times without `repeatable: true`, the last value wins:\n\n```php\n$parser-\u003eaddOption('--output', '-o');\n// -o first.txt -o second.txt  → 'second.txt'\n```\n\n**Arguments** are positional values without dashes. By default they are required. Set `optional: true` to make them optional:\n\n```php\n$parser-\u003eaddArgument('input');\n// script.php file.txt  → 'file.txt'\n// (not used)           → throws exception\n\n$parser-\u003eaddArgument('output', optional: true);\n// (not used)           → null\n\n$parser-\u003eaddArgument('output', optional: true, fallback: 'out.txt');\n// (not used)           → 'out.txt'\n```\n\nUse `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:\n\n```php\n$parser-\u003eaddOption('--format', '-f', optionalValue: true, fallback: 'json');\n// --format xml  → 'xml'\n// --format      → true (option used without value)\n// (not used)    → 'json' (fallback)\n```\n\nArguments can appear anywhere on the command line - they don't have to come after options:\n\n```php\n// all of these are equivalent:\n// script.php --verbose input.txt\n// script.php input.txt --verbose\n```\n\n\nRestricting Values with Enum\n----------------------------\n\nLimit accepted values to a specific set:\n\n```php\n$parser-\u003eaddOption('--format', '-f', enum: ['json', 'xml', 'csv']);\n// --format yaml  → throws \"Value of option --format must be json, or xml, or csv.\"\n```\n\n\nRepeatable Options\n------------------\n\nSet `repeatable: true` to collect multiple values into an array:\n\n```php\n$parser-\u003eaddOption('--include', '-I', repeatable: true);\n// -I src -I lib  → ['src', 'lib']\n// (not used)     → []\n\n$parser-\u003eaddArgument('files', optional: true, repeatable: true);\n// a.txt b.txt    → ['a.txt', 'b.txt']\n```\n\n\nTransforming Values\n-------------------\n\nUse `normalizer` to transform parsed values:\n\n```php\n$parser-\u003eaddOption('--count', normalizer: fn($v) =\u003e (int) $v);\n// --count 42  → 42 (integer)\n```\n\nFor file path validation, use the built-in `normalizeRealPath`:\n\n```php\n$parser-\u003eaddOption('--config', normalizer: Parser::normalizeRealPath(...));\n// --config app.ini     → '/full/path/to/app.ini'\n// --config missing.ini → throws \"File path 'missing.ini' not found.\"\n```\n\n\nMixing Both Approaches\n----------------------\n\nYou can combine `addFromHelp()` with fluent methods when you need normalizers for some options:\n\n```php\n$parser\n\t-\u003eaddFromHelp('\n\t\t-v, --verbose  Enable verbose mode\n\t\t-q, --quiet    Suppress output\n\t')\n\t-\u003eaddOption('--config', '-c', normalizer: Parser::normalizeRealPath(...),\n\t\tdescription: 'Configuration file')\n\t-\u003eaddArgument('input', description: 'Input file');\n```\n\n\nError Handling\n--------------\n\nThe parser throws `\\Exception` for invalid input:\n\n```php\nuse Nette\\CommandLine\\Parser;\n\n$parser = new Parser;\n$parser\n\t-\u003eaddOption('--output', '-o')\n\t-\u003eaddArgument('file');\n\ntry {\n\t$args = $parser-\u003eparse();\n} catch (\\Exception $e) {\n\tfwrite(STDERR, \"Error: {$e-\u003egetMessage()}\\n\");\n\texit(1);\n}\n```\n\nCommon error messages:\n\n| Error | Cause |\n|-------|-------|\n| `Option --output requires argument.` | Option used without required value |\n| `Unknown option --foo.` | Unrecognized option |\n| `Missing required argument \u003cfile\u003e.` | Required argument not provided |\n| `Unexpected parameter foo.` | Extra positional argument |\n| `Value of option --format must be json, or xml.` | Value not in enum |\n\nUse `isEmpty()` to check if no command-line arguments were provided (i.e., user ran just `script.php` with nothing after it):\n\n```php\nif ($parser-\u003eisEmpty()) {\n\t$parser-\u003ehelp();\n\texit;\n}\n```\n\n\nHandling --help and --version\n-----------------------------\n\nWhen 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:\n\n```php\n$parser = new Parser;\n$parser\n\t-\u003eaddSwitch('--help', '-h')\n\t-\u003eaddSwitch('--version', '-V')\n\t-\u003eaddArgument('input');  // required\n\n// First, check info options (no validation, no exceptions)\n$info = $parser-\u003eparseOnly(['--help', '--version']);\n\nif ($info['--help']) {\n\t$parser-\u003ehelp();\n\texit;\n}\n\nif ($info['--version']) {\n\techo \"1.0.0\\n\";\n\texit;\n}\n\n// Now do full parsing with validation\n$args = $parser-\u003eparse();\n```\n\nThe `parseOnly()` method:\n- Parses only the specified options, ignoring everything else\n- Respects aliases (`-h` → `--help`)\n- Never throws exceptions\n- Returns `null` for options that weren't used\n\n\nComplete Example\n================\n\nHere's a real-world file converter script combining Parser and Console:\n\n```php\n#!/usr/bin/env php\n\u003c?php\nuse Nette\\CommandLine\\Parser;\n\nrequire __DIR__ . '/vendor/autoload.php';\n\n$parser = new Parser;\n$parser\n\t-\u003eaddFromHelp('\n\t\t-h, --help           Show this help\n\t\t-v, --verbose        Show detailed output\n\t\t-n, --dry-run        Show what would be done\n\t\t-f, --format [type]  Output format (default: json)\n\t\t-o, --output \u003cfile\u003e  Output file\n\t', [\n\t\t'--format' =\u003e [\n\t\t\tParser::Enum =\u003e ['json', 'xml', 'csv'],\n\t\t],\n\t])\n\t-\u003eaddArgument('input', normalizer: Parser::normalizeRealPath(...));\n\n// Handle --help before validation (avoids \"missing argument\" error)\nif ($parser-\u003eisEmpty() || $parser-\u003eparseOnly(['--help'])['--help']) {\n\techo \"Usage: convert [options] \u003cinput\u003e\\n\\n\";\n\t$parser-\u003ehelp();\n\texit;\n}\n\ntry {\n\t$args = $parser-\u003eparse();\n} catch (\\Exception $e) {\n\tfwrite(STDERR, \"Error: {$e-\u003egetMessage()}\\n\");\n\texit(1);\n}\n\nif ($args['--verbose']) {\n\techo \"Converting {$args['input']} to {$args['--format']}...\\n\";\n}\n\nif ($args['--dry-run']) {\n\techo \"Dry run: No changes made.\\n\";\n\texit;\n}\n\n// ... conversion logic here ...\n\necho \"Done!\\n\";\n```\n\nThe script accepts commands like:\n- `convert input.txt` - convert with defaults\n- `convert -v --format xml input.txt` - verbose, XML format\n- `convert -o result.txt input.txt` - specify output file\n- `convert --help` - show help (works even without input file)\n","funding_links":["https://github.com/sponsors/dg","https://nette.org/donate"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnette%2Fcommand-line","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnette%2Fcommand-line","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnette%2Fcommand-line/lists"}