Skip to content

Commit 26eb4a0

Browse files
committed
Add more validates
1 parent 23765d8 commit 26eb4a0

File tree

5 files changed

+183
-63
lines changed

5 files changed

+183
-63
lines changed

README.md

Lines changed: 92 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,41 @@
88
Single file console framework to help you write scripts.
99

1010
<!-- TOC -->
11+
1112
* [PHP Simple Console V2.0](#php-simple-console-v20)
12-
* [Installation](#installation)
13-
* [Getting Started](#getting-started)
14-
* [Run Console by Closure](#run-console-by-closure)
15-
* [Run Console by Custom Class](#run-console-by-custom-class)
16-
* [The Return Value](#the-return-value)
17-
* [Parameter Parser](#parameter-parser)
18-
* [Parameter Definitions](#parameter-definitions)
19-
* [Show Help](#show-help)
20-
* [Override Help Information](#override-help-information)
21-
* [Parameter Configurations](#parameter-configurations)
22-
* [Get Parameters Value](#get-parameters-value)
23-
* [Parameters Type](#parameters-type)
24-
* [`LEVEL` type](#level-type)
25-
* [Parameters Options](#parameters-options)
26-
* [`description`](#description)
27-
* [`required`](#required)
28-
* [`default`](#default)
29-
* [`negatable`](#negatable)
30-
* [Parameters Parsing](#parameters-parsing)
31-
* [Error Handling](#error-handling)
32-
* [Wrong Parameters](#wrong-parameters)
33-
* [Verbosity](#verbosity)
34-
* [The Built-In Options](#the-built-in-options)
35-
* [Disable Built-In Options for Console App](#disable-built-in-options-for-console-app)
36-
* [Input/Output](#inputoutput)
37-
* [STDIN/STDOUT/STDERR](#stdinstdoutstderr)
38-
* [Output Methods](#output-methods)
39-
* [Input and Asking Questions](#input-and-asking-questions)
40-
* [Run Sub-Process](#run-sub-process)
41-
* [Hide Command Name](#hide-command-name)
42-
* [Custom Output](#custom-output)
13+
* [Installation](#installation)
14+
* [Getting Started](#getting-started)
15+
* [Run Console by Closure](#run-console-by-closure)
16+
* [Run Console by Custom Class](#run-console-by-custom-class)
17+
* [The Return Value](#the-return-value)
18+
* [Parameter Parser](#parameter-parser)
19+
* [Parameter Definitions](#parameter-definitions)
20+
* [Show Help](#show-help)
21+
* [Override Help Information](#override-help-information)
22+
* [Parameter Configurations](#parameter-configurations)
23+
* [Get Parameters Value](#get-parameters-value)
24+
* [Parameters Type](#parameters-type)
25+
* [`ARRAY` type](#array-type)
26+
* [`LEVEL` type](#level-type)
27+
* [Parameters Options](#parameters-options)
28+
* [`description`](#description)
29+
* [`required`](#required)
30+
* [`default`](#default)
31+
* [`negatable`](#negatable)
32+
* [Parameters Parsing](#parameters-parsing)
33+
* [Error Handling](#error-handling)
34+
* [Wrong Parameters](#wrong-parameters)
35+
* [Verbosity](#verbosity)
36+
* [The Built-In Options](#the-built-in-options)
37+
* [Disable Built-In Options for Console App](#disable-built-in-options-for-console-app)
38+
* [Input/Output](#inputoutput)
39+
* [STDIN/STDOUT/STDERR](#stdinstdoutstderr)
40+
* [Output Methods](#output-methods)
41+
* [Input and Asking Questions](#input-and-asking-questions)
42+
* [Run Sub-Process](#run-sub-process)
43+
* [Hide Command Name](#hide-command-name)
44+
* [Custom Output](#custom-output)
45+
4346
<!-- TOC -->
4447

4548
## Installation
@@ -145,7 +148,7 @@ $app->execute($argv);
145148

146149
### The Return Value
147150

148-
You can return `true` or `0` as success, `false` or any int larger than `0` as failure. Please refer to
151+
You can return `true` or `0` as success, `false` or any int larger than `0` as failure. Please refer to
149152
[GNU/Linux Exit Codes](https://slg.ddnss.de/list-of-common-exit-codes-for-gnu-linux/).
150153

151154
Simple Console provides constants for success and failure:
@@ -465,21 +468,55 @@ if (($dir = $app['dir']) !== false) {
465468

466469
You can define the type of parameters, it will auto convert to the type you defined.
467470

468-
| Type | Argument | Option | Desscription |
469-
|---------|--------------------------------|---------------------------------------------|---------------------------------------------------------|
470-
| STRING | String type | String type | |
471-
| INT | Integer type | Integer type | |
472-
| FLOAT | Float type | Flot type | Can be int or float, will all converts to float |
473-
| NUMERIC | Int or Float | Int or Float | Can be int or float, will all converts to float |
474-
| BOOLEAN | `1` or `0` | Boolean or `1` or `0`, cannot provide value | Options supports `negatable` |
475-
| ARRAY | Array, should be last argument | Array | Can provide multiple as `string[]` |
476-
| LEVEL | Int type | Int type | Can provide multiple times and convert the times to int |
471+
| Type | Argument | Option | Description |
472+
|---------|------------------------------|-----------------------|-------------------------------------------------------------------------|
473+
| STRING | String type | String type | |
474+
| INT | Integer type | Integer type | |
475+
| FLOAT | Float type | Flot type | Can be int or float, will all converts to float |
476+
| NUMERIC | Int or Float | Int or Float | Can be int or float, will all converts to float |
477+
| BOOLEAN | (X) | Add `--opt` as `TRUE` | Use `negatable` to supports `--opt` as `TRUE` and `--no-opt` as `FALSE` |
478+
| ARRAY | Array, must be last argument | Array | Can provide multiple as `string[]` |
479+
| LEVEL | (X) | Int type | Can provide multiple times and convert the times to int |
480+
481+
All parameter values parsed from `argv` is default as `string` type, and convert to the type you defined.
482+
483+
#### `ARRAY` type
484+
485+
The `ARRAY` can be use to arguments and options.
486+
487+
If you set an argument as `ARRAY` type, it must be last argument, and you can add more tailing arguments.
488+
489+
```php
490+
$app->addParameter('name', $app::STRING);
491+
$app->addParameter('tag', $app::ARRAY);
492+
493+
// Run: console.php foo a b c d e
494+
495+
$app->get('tag'); // [a, b, c ,d, e]
496+
```
497+
498+
Use `--` to escape all following options, all will be treated as arguments, it is useful if you are
499+
writing a proxy script.
500+
501+
```bash
502+
php listen.php --timeout 500 --wait 100 -- php flower.php hello --name=sakura --location Japan --muted
503+
504+
// The last argument values will be:
505+
// ['php', 'flower.php', 'hello', '--name=sakura', '--location', 'Japan', '--muted']
506+
```
477507

478-
All parameter values is default as `string` type, and convert to the type you defined.
508+
If you set an option as `ARRAY` type, it can be used as `--tag a --tag b --tag c`.
509+
510+
```php
511+
$app->addParameter('--tag|-t', $app::ARRAY);
512+
513+
$app->get('tag'); // [a, b, c]
514+
```
479515

480516
#### `LEVEL` type
481517

482-
The `LEVEL` type is a special type, it will convert the times to int. For example, a verbosity level of `-vvv` will be converted to `3`,
518+
The `LEVEL` type is a special type, it will convert the times to int. For example, a verbosity level of `-vvv` will be
519+
converted to `3`,
483520
and `-v` will be converted to `1`. You can use this type to define the verbosity level of your argv parser.
484521

485522
```php
@@ -497,24 +534,25 @@ If you are using `Console` class, the verbosity is built-in option, you don't ne
497534

498535
#### `required`
499536

500-
- **Argument**: Required argument must be provided, otherwise it will throw an error.
537+
- **Argument**: Required argument must be provided, otherwise it will throw an error.
501538
- You should not set a required argument after an optional argument.
502-
- **Option**: All options are `optional`.
503-
- If you set an option as `required`, it means this option requires a value.
539+
- **Option**: All options are `optional`.
540+
- If you set an option as `required`, it means this option requires a value, only `--option` without value is not
541+
allowed.
504542
- `boolean` option should not be required.
505543

506544
#### `default`
507545

508-
- **Argument**: Default value for the argument.
546+
- **Argument**: Default value for the argument.
509547
- If not provided, it will be `null`, `false` for boolean type, or `[]` for array type.
510-
- **Option**: Default value for the option.
548+
- **Option**: Default value for the option.
511549
- If not provided, it will be `null`, `false` for boolean type, or `[]` for array type.
512550

513551
#### `negatable`
514552

515553
- **Argument**: Argument cannot use this option.
516-
- **Option**: Negatable option. Should work with `boolean` type.
517-
- If set to `true`, it will support `--xxx|--no-xxx` 2 styles to set `true|false`.
554+
- **Option**: Negatable option. Should work with `boolean` type.
555+
- If set to `true`, it will support `--xxx|--no-xxx` 2 styles to set `true|false`.
518556
- If you want to set a boolean option's default as `TRUE` and use `--no-xxx` to set it as `FALSE`, you can do this:
519557
```php
520558
$app->addParameter('--muted|-m', $app::BOOLEAN, default: true, negatable: true);
@@ -611,8 +649,8 @@ $app = new \Asika\SimpleConsole\Console();
611649
$app->execute(); // You can use built-in `-h` and `-v` options
612650
```
613651

614-
If you parse `argv` by `ArgvParser`, you must add it manually. To avoid the required parameters error,
615-
you can set `validate` to `false` when parsing. Then validate and cast parameters after parsing and help
652+
If you parse `argv` by `ArgvParser`, you must add it manually. To avoid the required parameters error,
653+
you can set `validate` to `false` when parsing. Then validate and cast parameters after parsing and help
616654
content display.
617655

618656
```php
@@ -671,7 +709,6 @@ $app->execute(
671709
);
672710
```
673711

674-
675712
## Input/Output
676713

677714
### STDIN/STDOUT/STDERR
@@ -701,6 +738,7 @@ echo stream_get_contents($fp);
701738
### Output Methods
702739

703740
To output messages, you can use these methods:
741+
704742
- `write(string $message, bool $err = false)`: Write to STDOUT or STDERR
705743
- `writeln(string $message, bool $err = false)`: Write to STDOUT or STDERR with a new line
706744
- `newLine(int $lines, bool $err = false)`: Write empty new lines to STDOUT or STDERR
@@ -803,7 +841,7 @@ $app->exec(
803841
);
804842
```
805843

806-
By now, running sub-process by `prop_open()` is in BETA, if `prop_open()` not work for your environment, simply override
844+
By now, running sub-process by `prop_open()` is in BETA, if `prop_open()` not work for your environment, simply override
807845
`exec()` to use PHP `system()` instead.
808846

809847
```php

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.0.0-beta1
1+
2.0.0-beta2

src/Console.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@ public static function createArgvParser(\Closure|null $configure = null): ArgvPa
5252
return $parser;
5353
}
5454

55-
public static function parseArgv(\Closure|null $configure = null, ?array $argv = null, bool $validate = true): array
56-
{
55+
public static function parseArgv(
56+
\Closure|null $configure = null,
57+
?array $argv = null,
58+
bool $validate = true
59+
): array {
5760
return static::createArgvParser($configure)->parse($argv ?? $_SERVER['argv'], $validate);
5861
}
5962

@@ -360,7 +363,7 @@ public function addParameter(
360363
array_push($this->existsNames, ...((array) $parameter->name));
361364
$this->parameters[$parameter->primaryName] = $parameter;
362365

363-
return $parameter;
366+
return $parameter->selfValidate();
364367
}
365368

366369
public function removeParameter(string $name): void
@@ -404,6 +407,9 @@ public function mustGetOption(string $name): Parameter
404407

405408
public function parse(array $argv, bool $validate = true): array
406409
{
410+
foreach ($this->parameters as $parameter) {
411+
$parameter->selfValidate();
412+
}
407413
array_shift($argv);
408414
$this->currentArgument = 0;
409415
$this->parseOptions = true;
@@ -560,7 +566,7 @@ public function setOptionValue(string $name, mixed $value = null): void
560566
* @method self description(string $value)
561567
* @method self required(bool $value)
562568
* @method self negatable(bool $value)
563-
* @method self default(bool $value)
569+
* @method self default(mixed $value)
564570
*/
565571
class Parameter
566572
{
@@ -655,6 +661,10 @@ public function __construct(
655661
$this->name[$i] = ltrim($n, '-');
656662
}
657663
}
664+
}
665+
666+
public function selfValidate(): static
667+
{
658668
if ($this->isArray && !is_array($this->defaultValue)) {
659669
throw new \InvalidArgumentException("Default value of \"{$this->primaryName}\" must be an array.");
660670
}
@@ -664,6 +674,11 @@ public function __construct(
664674
"Argument \"{$this->primaryName}\" cannot be negatable."
665675
);
666676
}
677+
if ($this->isBoolean || $this->isLevel) {
678+
throw new \InvalidArgumentException(
679+
"Argument \"{$this->primaryName}\" cannot be type: {$this->type->name}."
680+
);
681+
}
667682
} else {
668683
if ($this->negatable && $this->required) {
669684
throw new \InvalidArgumentException(
@@ -676,6 +691,8 @@ public function __construct(
676691
"Default value of \"{$this->primaryName}\" cannot be set when required is true."
677692
);
678693
}
694+
695+
return $this;
679696
}
680697

681698
public function hasName(string $name): bool
@@ -728,7 +745,7 @@ public function __call(string $name, array $args)
728745
if (property_exists($this, $name)) {
729746
$this->{$name} = $args[0];
730747

731-
return $this;
748+
return $this->selfValidate();
732749
}
733750
throw new \BadMethodCallException("Method $name() does not exist.");
734751
}

test/ArgvParserTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,73 @@ public static function parametersProvider()
145145
];
146146
}
147147

148+
#[DataProvider('invalidConfigureProvider')]
149+
public function testInvalidParametersConfigurations(\Closure $closure, string $expected): void
150+
{
151+
$this->expectExceptionMessage($expected);
152+
153+
$parser = new ArgvParser();
154+
$closure($parser);
155+
}
156+
157+
public static function invalidConfigureProvider()
158+
{
159+
return [
160+
'Argument must not negatable' => [
161+
function (ArgvParser $parser) {
162+
$parser->addParameter('name', ParameterType::STRING, negatable: true);
163+
},
164+
'Argument "name" cannot be negatable.'
165+
],
166+
'ARRAY type default vlaue must b array' => [
167+
function (ArgvParser $parser) {
168+
$parser->addParameter('name', ParameterType::ARRAY, default: false);
169+
},
170+
'Default value of "name" must be an array.'
171+
],
172+
'Argument require should not has default' => [
173+
function (ArgvParser $parser) {
174+
$parser->addParameter('name', ParameterType::STRING)
175+
->required(true)
176+
->default('A');
177+
},
178+
'Default value of "name" cannot be set when required is true.'
179+
],
180+
'Argument cannot be BOOLEAN' => [
181+
function (ArgvParser $parser) {
182+
$parser->addParameter('name', ParameterType::BOOLEAN);
183+
},
184+
'Argument "name" cannot be type: BOOLEAN.'
185+
],
186+
'Argument cannot be LEVEL' => [
187+
function (ArgvParser $parser) {
188+
$parser->addParameter('name', ParameterType::LEVEL);
189+
},
190+
'Argument "name" cannot be type: LEVEL.'
191+
],
192+
193+
// Options
194+
'Negatable option cannot require value' => [
195+
function (ArgvParser $parser) {
196+
$parser->addParameter('--muted|-m', ParameterType::BOOLEAN)
197+
->required(true)
198+
->negatable(true);
199+
},
200+
'Negatable option "muted" cannot be required.'
201+
],
202+
203+
// Both
204+
'Option require should not has default' => [
205+
function (ArgvParser $parser) {
206+
$parser->addParameter('--name', ParameterType::STRING)
207+
->required(true)
208+
->default('A');
209+
},
210+
'Default value of "name" cannot be set when required is true.'
211+
],
212+
];
213+
}
214+
148215
public function testArgumentRequired(): void
149216
{
150217
$parser = new ArgvParser();

test/test.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ protected function configure(): void
1111
{
1212
// Arguments
1313
$this->addParameter('name', type: $this::STRING, description: 'Your name', required: true);
14-
$this->addParameter('age', type: $this::INT, description: 'Your age');
14+
$this->addParameter('age', type: $this::LEVEL, description: 'Your age');
1515

1616
// Name starts with `-` or `--` will be treated as option
1717
$this->addParameter('--height', type: $this::FLOAT, description: 'Your height', required: true);
@@ -21,8 +21,6 @@ protected function configure(): void
2121

2222
protected function doExecute(): int|bool
2323
{
24-
throw new \RuntimeException('An error occurred.');
25-
2624
$this->writeln('Hello');
2725
$this->writeln('Name: ' . $this->get('name'));
2826
$this->writeln('Age: ' . $this->get('age'));

0 commit comments

Comments
 (0)