CLImate.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <?php
  2. namespace League\CLImate;
  3. use League\CLImate\Argument\Manager as ArgumentManager;
  4. use League\CLImate\Decorator\Style;
  5. use League\CLImate\Settings\Manager as SettingsManager;
  6. use League\CLImate\TerminalObject\Dynamic\Spinner;
  7. use League\CLImate\TerminalObject\Router\Router;
  8. use League\CLImate\Util\Helper;
  9. use League\CLImate\Util\Output;
  10. use League\CLImate\Util\UtilFactory;
  11. /**
  12. * @method CLImate black(string $str = null)
  13. * @method CLImate red(string $str = null)
  14. * @method CLImate green(string $str = null)
  15. * @method CLImate yellow(string $str = null)
  16. * @method CLImate blue(string $str = null)
  17. * @method CLImate magenta(string $str = null)
  18. * @method CLImate cyan(string $str = null)
  19. * @method CLImate lightGray(string $str = null)
  20. * @method CLImate darkGray(string $str = null)
  21. * @method CLImate lightRed(string $str = null)
  22. * @method CLImate lightGreen(string $str = null)
  23. * @method CLImate lightYellow(string $str = null)
  24. * @method CLImate lightBlue(string $str = null)
  25. * @method CLImate lightMagenta(string $str = null)
  26. * @method CLImate lightCyan(string $str = null)
  27. * @method CLImate white(string $str = null)
  28. *
  29. * @method CLImate backgroundBlack(string $str = null)
  30. * @method CLImate backgroundRed(string $str = null)
  31. * @method CLImate backgroundGreen(string $str = null)
  32. * @method CLImate backgroundYellow(string $str = null)
  33. * @method CLImate backgroundBlue(string $str = null)
  34. * @method CLImate backgroundMagenta(string $str = null)
  35. * @method CLImate backgroundCyan(string $str = null)
  36. * @method CLImate backgroundLightGray(string $str = null)
  37. * @method CLImate backgroundDarkGray(string $str = null)
  38. * @method CLImate backgroundLightRed(string $str = null)
  39. * @method CLImate backgroundLightGreen(string $str = null)
  40. * @method CLImate backgroundLightYellow(string $str = null)
  41. * @method CLImate backgroundLightBlue(string $str = null)
  42. * @method CLImate backgroundLightMagenta(string $str = null)
  43. * @method CLImate backgroundLightCyan(string $str = null)
  44. * @method CLImate backgroundWhite(string $str = null)
  45. *
  46. * @method CLImate bold(string $str = null)
  47. * @method CLImate dim(string $str = null)
  48. * @method CLImate underline(string $str = null)
  49. * @method CLImate blink(string $str = null)
  50. * @method CLImate invert(string $str = null)
  51. * @method CLImate hidden(string $str = null)
  52. *
  53. * @method CLImate info(string $str = null)
  54. * @method CLImate comment(string $str = null)
  55. * @method CLImate whisper(string $str = null)
  56. * @method CLImate shout(string $str = null)
  57. * @method CLImate error(string $str = null)
  58. *
  59. * @method mixed out(string $str)
  60. * @method mixed inline(string $str)
  61. * @method mixed table(array $data)
  62. * @method mixed json(mixed $var)
  63. * @method mixed br($count = 1)
  64. * @method mixed tab($count = 1)
  65. * @method mixed draw(string $art)
  66. * @method mixed border(string $char = null, integer $length = null)
  67. * @method mixed dump(mixed $var)
  68. * @method mixed flank(string $output, string $char = null, integer $length = null)
  69. * @method mixed progress(integer $total = null)
  70. * @method Spinner spinner(string $label = null, string ...$characters = null)
  71. * @method mixed padding(integer $length = 0, string $char = '.')
  72. * @method mixed input(string $prompt, Util\Reader\ReaderInterface $reader = null)
  73. * @method mixed confirm(string $prompt, Util\Reader\ReaderInterface $reader = null)
  74. * @method mixed password(string $prompt, Util\Reader\ReaderInterface $reader = null)
  75. * @method mixed checkboxes(string $prompt, array $options, Util\Reader\ReaderInterface $reader = null)
  76. * @method mixed radio(string $prompt, array $options, Util\Reader\ReaderInterface $reader = null)
  77. * @method mixed animation(string $art, TerminalObject\Helper\Sleeper $sleeper = null)
  78. * @method mixed columns(array $data, $column_count = null)
  79. * @method mixed clear()
  80. * @method CLImate clearLine()
  81. *
  82. * @method CLImate addArt(string $dir)
  83. */
  84. class CLImate
  85. {
  86. /**
  87. * An instance of the Style class
  88. *
  89. * @var \League\CLImate\Decorator\Style $style
  90. */
  91. public $style;
  92. /**
  93. * An instance of the Terminal Object Router class
  94. *
  95. * @var \League\CLImate\TerminalObject\Router\Router $router
  96. */
  97. protected $router;
  98. /**
  99. * An instance of the Settings Manager class
  100. *
  101. * @var \League\CLImate\Settings\Manager $settings
  102. */
  103. protected $settings;
  104. /**
  105. * An instance of the Argument Manager class
  106. *
  107. * @var \League\CLImate\Argument\Manager $arguments
  108. */
  109. public $arguments;
  110. /**
  111. * An instance of the Output class
  112. *
  113. * @var \League\CLImate\Util\Output $output
  114. */
  115. public $output;
  116. /**
  117. * An instance of the Util Factory
  118. *
  119. * @var \League\CLImate\Util\UtilFactory $util
  120. */
  121. protected $util;
  122. public function __construct()
  123. {
  124. $this->setStyle(new Style());
  125. $this->setRouter(new Router());
  126. $this->setSettingsManager(new SettingsManager());
  127. $this->setOutput(new Output());
  128. $this->setUtil(new UtilFactory());
  129. $this->setArgumentManager(new ArgumentManager());
  130. }
  131. /**
  132. * Set the style property
  133. *
  134. * @param \League\CLImate\Decorator\Style $style
  135. */
  136. public function setStyle(Style $style)
  137. {
  138. $this->style = $style;
  139. }
  140. /**
  141. * Set the router property
  142. *
  143. * @param \League\CLImate\TerminalObject\Router\Router $router
  144. */
  145. public function setRouter(Router $router)
  146. {
  147. $this->router = $router;
  148. }
  149. /**
  150. * Set the settings property
  151. *
  152. * @param \League\CLImate\Settings\Manager $manager
  153. */
  154. public function setSettingsManager(SettingsManager $manager)
  155. {
  156. $this->settings = $manager;
  157. }
  158. /**
  159. * Set the arguments property
  160. *
  161. * @param \League\CLImate\Argument\Manager $manager
  162. */
  163. public function setArgumentManager(ArgumentManager $manager)
  164. {
  165. $this->arguments = $manager;
  166. }
  167. /**
  168. * Set the output property
  169. *
  170. * @param \League\CLImate\Util\Output $output
  171. */
  172. public function setOutput(Output $output)
  173. {
  174. $this->output = $output;
  175. }
  176. /**
  177. * Set the util property
  178. *
  179. * @param \League\CLImate\Util\UtilFactory $util
  180. */
  181. public function setUtil(UtilFactory $util)
  182. {
  183. $this->util = $util;
  184. }
  185. /**
  186. * Extend CLImate with custom methods
  187. *
  188. * @param string|object|array $class
  189. * @param string $key Optional custom key instead of class name
  190. *
  191. * @return \League\CLImate\CLImate
  192. */
  193. public function extend($class, $key = null)
  194. {
  195. $this->router->addExtension($key, $class);
  196. return $this;
  197. }
  198. /**
  199. * Force ansi support on
  200. *
  201. * @return \League\CLImate\CLImate
  202. */
  203. public function forceAnsiOn()
  204. {
  205. $this->util->system->forceAnsi();
  206. return $this;
  207. }
  208. /**
  209. * Force ansi support off
  210. *
  211. * @return \League\CLImate\CLImate
  212. */
  213. public function forceAnsiOff()
  214. {
  215. $this->util->system->forceAnsi(false);
  216. return $this;
  217. }
  218. /**
  219. * Write line to writer once
  220. *
  221. * @param string|array $writer
  222. *
  223. * @return \League\CLImate\CLImate
  224. */
  225. public function to($writer)
  226. {
  227. $this->output->once($writer);
  228. return $this;
  229. }
  230. /**
  231. * Output the program's usage statement
  232. *
  233. * @param array $argv
  234. * @return void
  235. */
  236. public function usage(array $argv = null)
  237. {
  238. $this->arguments->usage($this, $argv);
  239. }
  240. /**
  241. * Set the program's description
  242. *
  243. * @param string $description
  244. *
  245. * @return \League\CLImate\CLImate
  246. */
  247. public function description($description)
  248. {
  249. $this->arguments->description($description);
  250. return $this;
  251. }
  252. /**
  253. * Check if we have valid output
  254. *
  255. * @param mixed $output
  256. *
  257. * @return boolean
  258. */
  259. protected function hasOutput($output)
  260. {
  261. if (!empty($output)) {
  262. return true;
  263. }
  264. // Check for type first to avoid errors with objects/arrays/etc
  265. return ((is_string($output) || is_numeric($output)) && strlen($output) > 0);
  266. }
  267. /**
  268. * Search for the method within the string
  269. * and route it if we find one.
  270. *
  271. * @param string $method
  272. * @param string $name
  273. *
  274. * @return string The new string without the executed method.
  275. */
  276. protected function parseStyleMethod($method, $name)
  277. {
  278. // If the name starts with this method string...
  279. if (substr($name, 0, strlen($method)) == $method) {
  280. // ...remove the method name from the beginning of the string...
  281. $name = substr($name, strlen($method));
  282. // ...and trim off any of those underscores hanging around
  283. $name = ltrim($name, '_');
  284. $this->style->set($method);
  285. }
  286. return $name;
  287. }
  288. /**
  289. * Search for any style methods within the name and apply them
  290. *
  291. * @param string $name
  292. * @param array $method_search
  293. *
  294. * @return string Anything left over after applying styles
  295. */
  296. protected function applyStyleMethods($name, $method_search = null)
  297. {
  298. // Get all of the possible style attributes
  299. $method_search = $method_search ?: array_keys($this->style->all());
  300. $new_name = $this->searchForStyleMethods($name, $method_search);
  301. // While we still have a name left and we keep finding methods,
  302. // loop through the possibilities
  303. if (strlen($new_name) > 0 && $new_name != $name) {
  304. return $this->applyStyleMethods($new_name, $method_search);
  305. }
  306. return $new_name;
  307. }
  308. /**
  309. * Search for style methods in the current name
  310. *
  311. * @param string $name
  312. * @param array $search
  313. * @return string
  314. */
  315. protected function searchForStyleMethods($name, $search)
  316. {
  317. // Loop through the possible methods
  318. foreach ($search as $method) {
  319. // See if we found a valid method
  320. $name = $this->parseStyleMethod($method, $name);
  321. }
  322. return $name;
  323. }
  324. /**
  325. * Build up the terminal object and return it
  326. *
  327. * @param string $name
  328. * @param array $arguments
  329. *
  330. * @return object|null
  331. */
  332. protected function buildTerminalObject($name, $arguments)
  333. {
  334. // Retrieve the parser for the current set of styles
  335. $parser = $this->style->parser($this->util->system);
  336. // Reset the styles
  337. $this->style->reset();
  338. // Execute the terminal object
  339. $this->router->settings($this->settings);
  340. $this->router->parser($parser);
  341. $this->router->output($this->output);
  342. $this->router->util($this->util);
  343. return $this->router->execute($name, $arguments);
  344. }
  345. /**
  346. * Route anything leftover after styles were applied
  347. *
  348. * @param string $name
  349. * @param array $arguments
  350. *
  351. * @return object|null
  352. */
  353. protected function routeRemainingMethod($name, array $arguments)
  354. {
  355. // If we still have something left, let's figure out what it is
  356. if ($this->router->exists($name)) {
  357. $obj = $this->buildTerminalObject($name, $arguments);
  358. // If something was returned, return it
  359. if (is_object($obj)) {
  360. return $obj;
  361. }
  362. } elseif ($this->settings->exists($name)) {
  363. $this->settings->add($name, reset($arguments));
  364. // Handle passthroughs to the arguments manager.
  365. } else {
  366. // If we can't find it at this point, let's fail gracefully
  367. $this->out(reset($arguments));
  368. }
  369. }
  370. /**
  371. * Magic method for anything called that doesn't exist
  372. *
  373. * @param string $requested_method
  374. * @param array $arguments
  375. *
  376. * @return \League\CLImate\CLImate|\League\CLImate\TerminalObject\Dynamic\DynamicTerminalObject
  377. *
  378. * List of many of the possible method being called here
  379. * documented at the top of this class.
  380. */
  381. public function __call($requested_method, $arguments)
  382. {
  383. // Apply any style methods that we can find first
  384. $name = $this->applyStyleMethods(Helper::snakeCase($requested_method));
  385. // The first argument is the string|array|object we want to echo out
  386. $output = reset($arguments);
  387. if (strlen($name)) {
  388. // If we have something left, let's try and route it to the appropriate place
  389. if ($result = $this->routeRemainingMethod($name, $arguments)) {
  390. return $result;
  391. }
  392. } elseif ($this->hasOutput($output)) {
  393. // If we have fulfilled all of the requested methods and we have output, output it
  394. $this->out($output);
  395. }
  396. return $this;
  397. }
  398. }