/classes/script/arguments/parser.php

https://github.com/tharkun/atoum · PHP · 238 lines · 186 code · 52 blank · 0 comment · 16 complexity · 68f4aa2feea219a9b515b8e48a88cb0b MD5 · raw file

  1. <?php
  2. namespace mageekguy\atoum\script\arguments;
  3. use
  4. mageekguy\atoum,
  5. mageekguy\atoum\exceptions
  6. ;
  7. class parser implements \iteratorAggregate
  8. {
  9. protected $values = array();
  10. protected $handlers = array();
  11. protected $priorities = array();
  12. public function __construct(atoum\superglobals $superglobals = null)
  13. {
  14. $this->setSuperglobals($superglobals ?: new atoum\superglobals());
  15. }
  16. public function setSuperglobals(atoum\superglobals $superglobals)
  17. {
  18. $this->superglobals = $superglobals;
  19. return $this;
  20. }
  21. public function getSuperglobals()
  22. {
  23. return $this->superglobals;
  24. }
  25. public function resetValues()
  26. {
  27. $this->values = array();
  28. return $this;
  29. }
  30. public function getHandlers()
  31. {
  32. return $this->handlers;
  33. }
  34. public function getPriorities()
  35. {
  36. return $this->priorities;
  37. }
  38. public function getIterator()
  39. {
  40. return new \arrayIterator($this->getValues());
  41. }
  42. public function parse(atoum\script $script, array $array = array())
  43. {
  44. $this->init($array);
  45. $priorities = $this->priorities;
  46. uksort($this->values, function($arg1, $arg2) use ($priorities) {
  47. switch (true)
  48. {
  49. case isset($priorities[$arg1]) === false:
  50. case isset($priorities[$arg2]) === false:
  51. return - PHP_INT_MAX;
  52. default:
  53. return ($priorities[$arg1] > $priorities[$arg2] ? -1 : ($priorities[$arg1] == $priorities[$arg2] ? 0 : 1));
  54. }
  55. }
  56. );
  57. foreach ($this->values as $argument => $values)
  58. {
  59. $this->triggerHandlers($argument, $values, $script);
  60. }
  61. return $this;
  62. }
  63. public function getValues($argument = null)
  64. {
  65. return ($argument === null ? $this->values : (isset($this->values[$argument]) === false ? null : $this->values[$argument]));
  66. }
  67. public function addHandler(\closure $handler, array $arguments, $priority = 0)
  68. {
  69. $invoke = new \reflectionMethod($handler, '__invoke');
  70. if ($invoke->getNumberOfParameters() < 3)
  71. {
  72. throw new exceptions\runtime('Handler must take three arguments');
  73. }
  74. foreach ($arguments as $argument)
  75. {
  76. if (self::isArgument($argument) === false)
  77. {
  78. throw new exceptions\runtime('Argument \'' . $argument . '\' is invalid');
  79. }
  80. $this->handlers[$argument][] = $handler;
  81. $this->priorities[$argument] = (int) $priority;
  82. }
  83. return $this;
  84. }
  85. public function resetHandlers()
  86. {
  87. $this->handlers = array();
  88. $this->priorities = array();
  89. return $this;
  90. }
  91. public function argumentIsHandled($argument)
  92. {
  93. return (isset($this->values[$argument]) === true);
  94. }
  95. public function argumentsAreHandled(array $arguments)
  96. {
  97. return (sizeof(array_intersect(array_keys($this->values), $arguments)) > 0);
  98. }
  99. public function init(array $array = array())
  100. {
  101. if (sizeof($array) <= 0)
  102. {
  103. $array = array_slice($this->superglobals->_SERVER['argv'], 1);
  104. }
  105. $this->resetValues();
  106. $arguments = new \arrayIterator($array);
  107. if (sizeof($arguments) > 0)
  108. {
  109. $value = $arguments->current();
  110. if (self::isArgument($value) === false)
  111. {
  112. throw new exceptions\runtime\unexpectedValue('First argument \'' . $value . '\' is invalid');
  113. }
  114. $argument = $value;
  115. $this->values[$argument] = array();
  116. $arguments->next();
  117. while ($arguments->valid() === true)
  118. {
  119. $value = $arguments->current();
  120. if (self::isArgument($value) === false)
  121. {
  122. $this->values[$argument][] = $value;
  123. }
  124. else
  125. {
  126. $argument = $value;
  127. if(isset($this->values[$argument]) === false) {
  128. $this->values[$argument] = array();
  129. }
  130. }
  131. $arguments->next();
  132. }
  133. }
  134. return $this;
  135. }
  136. public function triggerHandlers($argument, array $values, atoum\script $script)
  137. {
  138. if (isset($this->handlers[$argument]) === true)
  139. {
  140. $this->invokeHandlers($script, $argument, $values);
  141. }
  142. else
  143. {
  144. $argumentMetaphone = metaphone($argument);
  145. $min = null;
  146. $closestArgument = null;
  147. $handlerArguments = array_keys($this->handlers);
  148. natsort($handlerArguments);
  149. foreach ($handlerArguments as $handlerArgument)
  150. {
  151. $levenshtein = levenshtein($argumentMetaphone, metaphone($handlerArgument));
  152. if ($min === null || $levenshtein < $min)
  153. {
  154. $min = $levenshtein;
  155. $closestArgument = $handlerArgument;
  156. }
  157. }
  158. if ($closestArgument === null)
  159. {
  160. throw new exceptions\runtime\unexpectedValue('Argument \'' . $argument . '\' is unknown');
  161. }
  162. else if ($min > 0)
  163. {
  164. throw new exceptions\runtime\unexpectedValue('Argument \'' . $argument . '\' is unknown, did you mean \'' . $closestArgument . '\' ?');
  165. }
  166. else
  167. {
  168. $this->invokeHandlers($script, $closestArgument, $values);
  169. }
  170. }
  171. return $this;
  172. }
  173. public function invokeHandlers(atoum\script $script, $argument, array $values)
  174. {
  175. $position = array_search($argument, array_keys($this->values)) + 1;
  176. foreach ($this->handlers[$argument] as $handler)
  177. {
  178. $handler->__invoke($script, $argument, $values, $position);
  179. }
  180. return $this;
  181. }
  182. public static function isArgument($value)
  183. {
  184. return (preg_match('/^(\+|-{1,2})[a-z][-_a-z0-9]*/i', $value) === 1);
  185. }
  186. }