CLImate.php 12 KB

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