1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace clearice\argparser; |
4
|
|
|
|
5
|
|
|
|
6
|
|
|
class ArgumentParser |
7
|
|
|
{ |
8
|
|
|
private $description; |
9
|
|
|
private $footer; |
10
|
|
|
private $options = []; |
11
|
|
|
|
12
|
|
|
public function setDescription($description) |
13
|
|
|
{ |
14
|
|
|
$this->description = $description; |
15
|
|
|
} |
16
|
|
|
|
17
|
|
|
public function setFooter($footer) |
18
|
|
|
{ |
19
|
|
|
$this->footer = $footer; |
20
|
|
|
} |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Add a value to the available possible options for later parsing. |
24
|
|
|
* @param $key |
25
|
|
|
* @param $value |
26
|
|
|
* @throws OptionExistsException |
27
|
|
|
*/ |
28
|
3 |
|
private function addToOptionArray($key, $value) |
29
|
|
|
{ |
30
|
3 |
|
if(isset($value[$key]) && !isset($this->options[$value[$key]])) { |
31
|
3 |
|
$this->options[$value[$key]] = $value; |
32
|
2 |
|
} else if(isset($value[$key])) { |
33
|
2 |
|
throw new OptionExistsException("An argument option with $key {$value[$key]} already exists."); |
34
|
|
|
} |
35
|
3 |
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Add an option to be parsed. |
39
|
|
|
* Arguments are presented as a structured array with the following possible keys. |
40
|
|
|
* |
41
|
|
|
* name: The name of the option prefixed with a double dash -- |
42
|
|
|
* short_name: A shorter single character option prefixed with a single dash - |
43
|
|
|
* type: Required for all options that take values. An option specified without a type is considered to be a |
44
|
|
|
* boolean flag. |
45
|
|
|
* help: A help message for the option |
46
|
|
|
* |
47
|
|
|
* @param $option |
48
|
|
|
* @throws OptionExistsException |
49
|
|
|
* @throws InvalidArgumentDescriptionException |
50
|
|
|
*/ |
51
|
4 |
|
public function addOption($option) |
52
|
|
|
{ |
53
|
4 |
|
if(!isset($option['name'])) { |
54
|
1 |
|
throw new InvalidArgumentDescriptionException("Argument must have a name"); |
55
|
|
|
} |
56
|
3 |
|
$this->addToOptionArray('name', $option); |
57
|
3 |
|
$this->addToOptionArray('short_name', $option); |
58
|
3 |
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Parse a long argument that is prefixed with a double dash "--" |
62
|
|
|
* |
63
|
|
|
* @param $arguments |
64
|
|
|
* @param $argPointer |
65
|
|
|
* @return array |
66
|
|
|
* @throws InvalidValueException |
67
|
|
|
*/ |
68
|
1 |
|
private function parseLongArgument($arguments, &$argPointer) |
69
|
|
|
{ |
70
|
1 |
|
$string = substr($arguments[$argPointer], 2); |
71
|
1 |
|
preg_match("/(?<name>[a-zA-Z_0-9-]+)(?<equal>=?)(?<value>.*)/", $string, $matches); |
72
|
1 |
|
$name = $matches['name']; |
73
|
1 |
|
$option = $this->options[$name]; |
74
|
|
|
|
75
|
1 |
|
if(isset($option['type'])) { |
76
|
1 |
|
if($matches['equal'] === '=') { |
77
|
1 |
|
$value = $matches['value']; |
78
|
1 |
View Code Duplication |
} else if (isset($arguments[$argPointer + 1]) && $arguments[$argPointer + 1][0] != '-') { |
|
|
|
|
79
|
1 |
|
$value = $arguments[$argPointer + 1]; |
80
|
1 |
|
$argPointer++; |
81
|
|
|
} else { |
82
|
1 |
|
throw new InvalidValueException("A value must be passed along with argument $name."); |
83
|
|
|
} |
84
|
|
|
} else { |
85
|
1 |
|
$value = true; |
86
|
|
|
} |
87
|
|
|
|
88
|
1 |
|
return [$name => $value]; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Parse a short argument that is prefixed with a single dash '-' |
93
|
|
|
* |
94
|
|
|
* @param $arguments |
95
|
|
|
* @param $argPointer |
96
|
|
|
* @return array |
97
|
|
|
* @throws InvalidValueException |
98
|
|
|
*/ |
99
|
1 |
|
public function parseShortArgument($arguments, &$argPointer) |
100
|
|
|
{ |
101
|
1 |
|
$argument = $arguments[$argPointer]; |
102
|
1 |
|
$key = substr($argument, 1, 1); |
103
|
1 |
|
$option = $this->options[$key]; |
104
|
|
|
|
105
|
1 |
|
if(isset($option['type'])) { |
106
|
1 |
|
if(substr($argument, 2) != "") { |
107
|
1 |
|
$value = substr($argument, 2); |
108
|
1 |
View Code Duplication |
} else if(isset($arguments[$argPointer + 1]) && $arguments[$argPointer + 1][0] != '-') { |
|
|
|
|
109
|
1 |
|
$value = $arguments[$argPointer + 1]; |
110
|
1 |
|
$argPointer++; |
111
|
|
|
} else { |
112
|
1 |
|
throw new InvalidValueException("A value must be passed along with argument ${option['name']}"); |
113
|
|
|
} |
114
|
|
|
} else { |
115
|
|
|
$value = true; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
return [$option['name'] => $value]; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Parses command line arguments and return a structured array of options and their associated values. |
123
|
|
|
* |
124
|
|
|
* @param array $arguments An optional array of arguments that would be parsed instead of those passed to the CLI. |
125
|
|
|
* @return array |
126
|
|
|
* @throws InvalidValueException |
127
|
|
|
*/ |
128
|
|
|
public function parse($arguments = null) |
129
|
|
|
{ |
130
|
1 |
|
global $argv; |
131
|
1 |
|
$arguments = $arguments ?? $argv; |
132
|
1 |
|
$numArguments = count($arguments); |
133
|
1 |
|
$output = []; |
134
|
|
|
|
135
|
1 |
|
for($argPointer = 1; $argPointer < $numArguments; $argPointer++) { |
136
|
1 |
|
$arg = $arguments[$argPointer]; |
137
|
1 |
|
if(substr($arg, 0, 2) == "--") { |
138
|
1 |
|
$output = array_merge($output, $this->parseLongArgument($arguments, $argPointer)); |
139
|
1 |
|
} else if ($arg[0] == '-') { |
140
|
1 |
|
$output = array_merge($output, $this->parseShortArgument($arguments, $argPointer)); |
141
|
|
|
} else { |
142
|
1 |
|
$output['__args'] = isset($output['__args']) ? array_merge($output['__args'], [$arg]) : [$arg]; |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
|
146
|
1 |
|
return $output; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
public function getHelp() |
150
|
|
|
{ |
151
|
|
|
|
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.