1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Rule.php |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace netfocusinc\argh; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Representation of a Rule used to interpret command line arguments |
11
|
|
|
* |
12
|
|
|
* Rules are a combination of syntax and semantics used to interpret command line arguments. |
13
|
|
|
* When a command line string matches the syntax of a rule, and its character content matches the sematics of the rule |
14
|
|
|
* this command line string can be used to create an Argument. |
15
|
|
|
* |
16
|
|
|
* @author Benjamin Hough |
17
|
|
|
* |
18
|
|
|
* @since 1.0.0 |
19
|
|
|
*/ |
20
|
|
|
class Rule |
21
|
|
|
{ |
22
|
|
|
// |
23
|
|
|
// CONSTANTS |
24
|
|
|
// |
25
|
|
|
|
26
|
|
|
// Syntax Contants |
27
|
|
|
|
28
|
|
|
const ARGH_SYNTAX_FLAG = '[a-z]{1}'; |
29
|
|
|
const ARGH_SYNTAX_FLAGS = '[a-z]+'; |
30
|
|
|
const ARGH_SYNTAX_NAME = '[a-z0-9_]+'; |
31
|
|
|
const ARGH_SYNTAX_VALUE = '[a-z0-9_\.\/]*'; |
32
|
|
|
const ARGH_SYNTAX_LIST = '[a-z0-9_\-,\' ]+'; |
33
|
|
|
const ARGH_SYNTAX_COMMAND = '[a-z0-9_]{2,}'; |
34
|
|
|
const ARGH_SYNTAX_QUOTED = '[a-z0-9_\-\'\\/\. ]+'; |
35
|
|
|
const ARGH_SYNTAX_VARIABLE = '[a-z0-9_\.\/]*'; |
36
|
|
|
|
37
|
|
|
// Semantic Contants |
38
|
|
|
const ARGH_SEMANTICS_FLAG = 1; |
39
|
|
|
const ARGH_SEMANTICS_FLAGS = 2; |
40
|
|
|
const ARGH_SEMANTICS_NAME = 3; |
41
|
|
|
const ARGH_SEMANTICS_VALUE = 4; |
42
|
|
|
const ARGH_SEMANTICS_LIST = 5; |
43
|
|
|
const ARGH_SEMANTICS_COMMAND = 6; |
44
|
|
|
const ARGH_SEMANTICS_VARIABLE = 7; |
45
|
|
|
|
46
|
|
|
// |
47
|
|
|
// PUBLIC PROPERTIES |
48
|
|
|
// |
49
|
|
|
|
50
|
|
|
/** @var string The 'name' of this Rule */ |
51
|
|
|
private $name = null; |
52
|
|
|
|
53
|
|
|
/** @var string An 'example' string that matches the syntax of this Rule */ |
54
|
|
|
private $example = null; |
55
|
|
|
|
56
|
|
|
/** @var string A regular expression defining the acceptable syntax for this Rule */ |
57
|
|
|
private $syntax = null; |
58
|
|
|
|
59
|
|
|
/** @var array Array of Rule's contants defining the semantical meaning for this Rule */ |
60
|
|
|
private $semantics = null; |
61
|
|
|
|
62
|
|
|
// |
63
|
|
|
// STATIC FUNCTIONS |
64
|
|
|
// |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Creates a new Rule with the specified $attributes |
68
|
|
|
* |
69
|
|
|
* Convenience method for creating new Rules with the specified $attributes |
70
|
|
|
* |
71
|
|
|
* @since 1.0.0 |
72
|
|
|
* |
73
|
|
|
* @returns Rule |
74
|
|
|
*/ |
75
|
|
|
public static function createWithAttributes(array $attributes): Rule |
76
|
|
|
{ |
77
|
|
|
// Defaults |
78
|
|
|
$name = null; |
79
|
|
|
$example = null; |
80
|
|
|
$syntax = null; |
81
|
|
|
$semantics = null; |
82
|
|
|
|
83
|
|
|
if( array_key_exists('name', $attributes) ) $name = $attributes['name']; |
84
|
|
|
if( array_key_exists('example', $attributes) ) $example = $attributes['example']; |
85
|
|
|
if( array_key_exists('syntax', $attributes) ) $syntax = $attributes['syntax']; |
86
|
|
|
if( array_key_exists('semantics', $attributes) ) $semantics = $attributes['semantics']; |
87
|
|
|
|
88
|
|
|
return new self($name, $example, $syntax, $semantics); |
89
|
|
|
|
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Returns a (human friendly) string representation of a semantic int |
94
|
|
|
* |
95
|
|
|
* @since 1.0.0 |
96
|
|
|
* |
97
|
|
|
* @param int $semantics |
98
|
|
|
* |
99
|
|
|
* @returns string |
100
|
|
|
*/ |
101
|
|
|
public static function semanticsToString(int $semantics) |
102
|
|
|
{ |
103
|
|
|
switch($semantics) |
104
|
|
|
{ |
105
|
|
|
case self::ARGH_SEMANTICS_FLAG: return 'FLAG'; |
106
|
|
|
case self::ARGH_SEMANTICS_FLAGS: return 'FLAGS'; |
107
|
|
|
case self::ARGH_SEMANTICS_NAME: return 'NAME'; |
108
|
|
|
case self::ARGH_SEMANTICS_VALUE: return 'VALUE'; |
109
|
|
|
case self::ARGH_SEMANTICS_LIST: return 'LIST'; |
110
|
|
|
case self::ARGH_SEMANTICS_COMMAND: return 'COMMAND'; |
111
|
|
|
case self::ARGH_SEMANTICS_VARIABLE: return 'VARIABLE'; |
112
|
|
|
default: return '*invalid*'; |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
// |
117
|
|
|
// PUBLIC METHODS |
118
|
|
|
// |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Rule contructor. |
122
|
|
|
* |
123
|
|
|
* Constructs a new Rule. |
124
|
|
|
* |
125
|
|
|
* @since 1.0.0 |
126
|
|
|
* |
127
|
|
|
* @param string $name |
128
|
|
|
* @param string $example |
129
|
|
|
* @param string $syntax |
130
|
|
|
* @param array $semantics |
131
|
|
|
* |
132
|
|
|
* @throws ArghException |
133
|
|
|
*/ |
134
|
|
|
public function __construct(string $name, string $example, string $syntax, array $semantics) |
135
|
|
|
{ |
136
|
|
|
|
137
|
|
|
// Validate the syntax regular expression |
138
|
|
|
// Suppress error messages |
139
|
|
|
if( @preg_match($syntax, '') === FALSE ) |
140
|
|
|
{ |
141
|
|
|
throw new ArghException('Rule \'' . $name . '\' syntax \'' . $syntax . '\' is not a valid regular expression'); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
// Confirm count(semantics) matches number of parenthesized subpatterns defined by the syntax regular expression |
145
|
|
|
if( substr_count($syntax, '(') != count($semantics) ) |
146
|
|
|
{ |
147
|
|
|
throw new ArghException('Rule \'' . $name . '\' syntax defines ' . substr_count($syntax, '(') . ' sub-patterns, but semantics defines ' . count($semantics)); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
// Set properties on this instance |
151
|
|
|
$this->name = $name; |
152
|
|
|
$this->syntax = $syntax; |
153
|
|
|
$this->semantics = $semantics; |
154
|
|
|
$this->example = $example; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Gets the 'name' property of this Rule |
159
|
|
|
* |
160
|
|
|
* @since 1.0.0 |
161
|
|
|
* |
162
|
|
|
* @return string |
163
|
|
|
*/ |
164
|
|
|
public function name(): string { return $this->name; } |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Gets the 'syntax' property of this Rule |
168
|
|
|
* |
169
|
|
|
* @since 1.0.0 |
170
|
|
|
* |
171
|
|
|
* @return string |
172
|
|
|
*/ |
173
|
|
|
public function syntax(): string { return $this->syntax; } |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Gets the 'semantics' property of this Rule |
177
|
|
|
* |
178
|
|
|
* @since 1.0.0 |
179
|
|
|
* |
180
|
|
|
* @return array |
181
|
|
|
*/ |
182
|
|
|
public function semantics(): array { return $this->semantics; } |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Gets the 'sample' of this Rule |
186
|
|
|
* |
187
|
|
|
* @since 1.0.0 |
188
|
|
|
* |
189
|
|
|
* @return string |
190
|
|
|
*/ |
191
|
|
|
public function example(): string { return $this->example; } |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Does this Rule match a $string |
195
|
|
|
* |
196
|
|
|
* Note that matching a Rule does not guarantee the Rule will result |
197
|
|
|
* in a new Argument. Ultimately, the argument string may not meet all the |
198
|
|
|
* requirements of the Rule, e.g. matching defined parameter name|flag|options |
199
|
|
|
* |
200
|
|
|
* @param $string string A string to check for match with this Rule |
201
|
|
|
* @param $tokens array Reference to an array, on match will contain matching elements |
202
|
|
|
* |
203
|
|
|
* @return bool |
204
|
|
|
*/ |
205
|
|
|
public function match($string, &$tokens=array()): bool |
206
|
|
|
{ |
207
|
|
|
if( preg_match($this->syntax(), $string, $tokens) ) |
208
|
|
|
{ |
209
|
|
|
return TRUE; |
210
|
|
|
} |
211
|
|
|
else |
212
|
|
|
{ |
213
|
|
|
return FALSE; |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
?> |
|
|
|
|
Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.
A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.