Hoa central
GetOption.php
Go to the documentation of this file.
1 <?php
2 
37 namespace Hoa\Console;
38 
39 use Hoa\Ustring;
40 
53 class GetOption
54 {
60  const NO_ARGUMENT = 0;
61 
67  const REQUIRED_ARGUMENT = 1;
68 
74  const OPTIONAL_ARGUMENT = 2;
75 
81  const OPTION_NAME = 0;
82 
88  const OPTION_HAS_ARG = 1;
89 
95  const OPTION_VAL = 2;
96 
110  protected $_options = [];
111 
117  protected $_parser = null;
118 
124  protected $_pipette = [];
125 
126 
127 
136  public function __construct(Array $options, Parser $parser)
137  {
138  $this->_options = $options;
139  $this->_parser = $parser;
140 
141  if (empty($options)) {
142  $this->_pipette[null] = null;
143 
144  return;
145  }
146 
147  $names = [];
148 
149  foreach ($options as $i => $option) {
150  if (isset($option[self::OPTION_NAME])) {
151  $names[$option[self::OPTION_NAME]] = $i;
152  }
153 
154  if (isset($option[self::OPTION_VAL])) {
155  $names[$option[self::OPTION_VAL]] = $i;
156  }
157  }
158 
159  $_names = array_keys($names);
160  $switches = $parser->getSwitches();
161 
162  foreach ($switches as $name => $value) {
163  if (false === in_array($name, $_names)) {
164  if (1 === strlen($name)) {
165  $this->_pipette[] = ['__ambiguous', [
166  'solutions' => [],
167  'value' => $value,
168  'option' => $name
169  ]];
170 
171  continue;
172  }
173 
174  $haystack = implode(';', $_names);
175  $differences = (int) ceil(strlen($name) / 3);
176  $searched = Ustring\Search::approximated(
177  $haystack,
178  $name,
179  $differences
180  );
181  $solutions = [];
182 
183  foreach ($searched as $s) {
184  $h = substr($haystack, $s['i'], $s['l']);
185 
186  if (false !== strpos($h, ';') ||
187  false !== in_array($h, array_keys($switches)) ||
188  false === in_array($h, $_names)) {
189  continue;
190  }
191 
192  $solutions[] = $h;
193  }
194 
195  if (empty($solutions)) {
196  continue;
197  }
198 
199  $this->_pipette[] = ['__ambiguous', [
200  'solutions' => $solutions,
201  'value' => $value,
202  'option' => $name
203  ]];
204 
205  continue;
206  }
207 
208  $option = $options[$names[$name]];
209  $argument = $option[self::OPTION_HAS_ARG];
210 
211  if (self::NO_ARGUMENT === $argument) {
212  if (!is_bool($value)) {
213  $parser->transferSwitchToInput($name, $value);
214  }
215  } elseif (self::REQUIRED_ARGUMENT === $argument && !is_string($value)) {
216  throw new Exception(
217  'The argument %s requires a value (it is not a switch).',
218  0,
219  $name
220  );
221  }
222 
223  $this->_pipette[] = [$option[self::OPTION_VAL], $value];
224  }
225 
226  $this->_pipette[null] = null;
227  reset($this->_pipette);
228 
229  return;
230  }
231 
242  public function getOption(&$optionValue, $short = null)
243  {
244  static $first = true;
245 
246  if (true === $this->isPipetteEmpty() && true === $first) {
247  $first = false;
248  $optionValue = null;
249 
250  return false;
251  }
252 
253  $k = key($this->_pipette);
254  $c = current($this->_pipette);
255  $key = $c[0];
256  $value = $c[1];
257 
258  if (null == $k && null === $c) {
259  reset($this->_pipette);
260  $first = true;
261 
262  return false;
263  }
264 
265  $allow = [];
266 
267  if (null === $short) {
268  foreach ($this->_options as $option) {
269  $allow[] = $option[self::OPTION_VAL];
270  }
271  } else {
272  $allow = str_split($short);
273  }
274 
275  if (!in_array($key, $allow) && '__ambiguous' != $key) {
276  return false;
277  }
278 
279  $optionValue = $value;
280  $return = $key;
281  next($this->_pipette);
282 
283  return $return;
284  }
285 
291  public function isPipetteEmpty()
292  {
293  return count($this->_pipette) == 1;
294  }
295 
306  public function resolveOptionAmbiguity(Array $solutions)
307  {
308  if (!isset($solutions['solutions']) ||
309  !isset($solutions['value']) ||
310  !isset($solutions['option'])) {
311  throw new Exception(
312  'Cannot resolve option ambiguity because the given solution ' .
313  'seems to be corruped.',
314  1
315  );
316  }
317 
318  $choices = $solutions['solutions'];
319 
320  if (1 > count($choices)) {
321  throw new Exception(
322  'Cannot resolve ambiguity, fix your typo in the option %s :-).',
323  2,
324  $solutions['option']
325  );
326  }
327 
328  $theSolution = $choices[0];
329 
330  foreach ($this->_options as $option) {
331  if ($theSolution == $option[self::OPTION_NAME] ||
332  $theSolution == $option[self::OPTION_VAL]) {
333  $argument = $option[self::OPTION_HAS_ARG];
334  $value = $solutions['value'];
335 
336  if (self::NO_ARGUMENT === $argument) {
337  if (!is_bool($value)) {
338  $this->_parser->transferSwitchToInput($theSolution, $value);
339  }
340  } elseif (self::REQUIRED_ARGUMENT === $argument &&
341  !is_string($value)) {
342  throw new Exception(
343  'The argument %s requires a value (it is not a switch).',
344  3,
345  $theSolution
346  );
347  }
348 
349  unset($this->_pipette[null]);
350  $this->_pipette[] = [$option[self::OPTION_VAL], $value];
351  $this->_pipette[null] = null;
352 
353  return;
354  }
355  }
356 
357  return;
358  }
359 }
static approximated($y, $x, $k)
Definition: Search.php:58
transferSwitchToInput($name, &$value)
Definition: Parser.php:351
__construct(Array $options, Parser $parser)
Definition: GetOption.php:136
resolveOptionAmbiguity(Array $solutions)
Definition: GetOption.php:306
getOption(&$optionValue, $short=null)
Definition: GetOption.php:242