cli.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.5
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2013 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. /**
  14. * Cli class
  15. *
  16. * Interact with the command line by accepting input options, parameters and output text
  17. *
  18. * @package Fuel
  19. * @category Core
  20. * @author Phil Sturgeon
  21. * @link http://docs.fuelphp.com/classes/cli.html
  22. */
  23. class Cli
  24. {
  25. public static $readline_support = false;
  26. public static $wait_msg = 'Press any key to continue...';
  27. protected static $args = array();
  28. protected static $foreground_colors = array(
  29. 'black' => '0;30',
  30. 'dark_gray' => '1;30',
  31. 'blue' => '0;34',
  32. 'dark_blue' => '1;34',
  33. 'light_blue' => '1;34',
  34. 'green' => '0;32',
  35. 'light_green' => '1;32',
  36. 'cyan' => '0;36',
  37. 'light_cyan' => '1;36',
  38. 'red' => '0;31',
  39. 'light_red' => '1;31',
  40. 'purple' => '0;35',
  41. 'light_purple' => '1;35',
  42. 'light_yellow' => '0;33',
  43. 'yellow' => '1;33',
  44. 'light_gray' => '0;37',
  45. 'white' => '1;37',
  46. );
  47. protected static $background_colors = array(
  48. 'black' => '40',
  49. 'red' => '41',
  50. 'green' => '42',
  51. 'yellow' => '43',
  52. 'blue' => '44',
  53. 'magenta' => '45',
  54. 'cyan' => '46',
  55. 'light_gray' => '47',
  56. );
  57. /**
  58. * Static constructor. Parses all the CLI params.
  59. */
  60. public static function _init()
  61. {
  62. if ( ! \Fuel::$is_cli)
  63. {
  64. throw new \Exception('Cli class cannot be used outside of the command line.');
  65. }
  66. for ($i = 1; $i < $_SERVER['argc']; $i++)
  67. {
  68. $arg = explode('=', $_SERVER['argv'][$i]);
  69. static::$args[$i] = $arg[0];
  70. if (count($arg) > 1 || strncmp($arg[0], '-', 1) === 0)
  71. {
  72. static::$args[ltrim($arg[0], '-')] = isset($arg[1]) ? $arg[1] : true;
  73. }
  74. }
  75. // Readline is an extension for PHP that makes interactive with PHP much more bash-like
  76. // http://www.php.net/manual/en/readline.installation.php
  77. static::$readline_support = extension_loaded('readline');
  78. }
  79. /**
  80. * Returns the option with the given name. You can also give the option
  81. * number.
  82. *
  83. * Named options must be in the following formats:
  84. * php index.php user -v --v -name=John --name=John
  85. *
  86. * @param string|int $name the name of the option (int if unnamed)
  87. * @param mixed $default value to return if the option is not defined
  88. * @return mixed
  89. */
  90. public static function option($name, $default = null)
  91. {
  92. if ( ! isset(static::$args[$name]))
  93. {
  94. return \Fuel::value($default);
  95. }
  96. return static::$args[$name];
  97. }
  98. /**
  99. * Allows you to set a commandline option from code
  100. *
  101. * @param string|int $name the name of the option (int if unnamed)
  102. * @param mixed|null $value value to set, or null to delete the option
  103. * @return mixed
  104. */
  105. public static function set_option($name, $value = null)
  106. {
  107. if ($value === null)
  108. {
  109. if (isset(static::$args[$name]))
  110. {
  111. unset(static::$args[$name]);
  112. }
  113. }
  114. else
  115. {
  116. static::$args[$name] = $value;
  117. }
  118. }
  119. /**
  120. * Get input from the shell, using readline or the standard STDIN
  121. *
  122. * Named options must be in the following formats:
  123. * php index.php user -v --v -name=John --name=John
  124. *
  125. * @param string|int $name the name of the option (int if unnamed)
  126. * @return string
  127. */
  128. public static function input($prefix = '')
  129. {
  130. if (static::$readline_support)
  131. {
  132. return readline($prefix);
  133. }
  134. echo $prefix;
  135. return fgets(STDIN);
  136. }
  137. /**
  138. * Asks the user for input. This can have either 1 or 2 arguments.
  139. *
  140. * Usage:
  141. *
  142. * // Waits for any key press
  143. * CLI::prompt();
  144. *
  145. * // Takes any input
  146. * $color = CLI::prompt('What is your favorite color?');
  147. *
  148. * // Takes any input, but offers default
  149. * $color = CLI::prompt('What is your favourite color?', 'white');
  150. *
  151. * // Will only accept the options in the array
  152. * $ready = CLI::prompt('Are you ready?', array('y','n'));
  153. *
  154. * @return string the user input
  155. */
  156. public static function prompt()
  157. {
  158. $args = func_get_args();
  159. $options = array();
  160. $output = '';
  161. $default = null;
  162. // How many we got
  163. $arg_count = count($args);
  164. // Is the last argument a boolean? True means required
  165. $required = end($args) === true;
  166. // Reduce the argument count if required was passed, we don't care about that anymore
  167. $required === true and --$arg_count;
  168. // This method can take a few crazy combinations of arguments, so lets work it out
  169. switch ($arg_count)
  170. {
  171. case 2:
  172. // E.g: $ready = CLI::prompt('Are you ready?', array('y','n'));
  173. if (is_array($args[1]))
  174. {
  175. list($output, $options)=$args;
  176. }
  177. // E.g: $color = CLI::prompt('What is your favourite color?', 'white');
  178. elseif (is_string($args[1]))
  179. {
  180. list($output, $default)=$args;
  181. }
  182. break;
  183. case 1:
  184. // No question (probably been asked already) so just show options
  185. // E.g: $ready = CLI::prompt(array('y','n'));
  186. if (is_array($args[0]))
  187. {
  188. $options = $args[0];
  189. }
  190. // Question without options
  191. // E.g: $ready = CLI::prompt('What did you do today?');
  192. elseif (is_string($args[0]))
  193. {
  194. $output = $args[0];
  195. }
  196. break;
  197. }
  198. // If a question has been asked with the read
  199. if ($output !== '')
  200. {
  201. $extra_output = '';
  202. if ($default !== null)
  203. {
  204. $extra_output = ' [ Default: "'.$default.'" ]';
  205. }
  206. elseif ($options !== array())
  207. {
  208. $extra_output = ' [ '.implode(', ', $options).' ]';
  209. }
  210. fwrite(STDOUT, $output.$extra_output.': ');
  211. }
  212. // Read the input from keyboard.
  213. $input = trim(static::input()) ?: $default;
  214. // No input provided and we require one (default will stop this being called)
  215. if (empty($input) and $required === true)
  216. {
  217. static::write('This is required.');
  218. static::new_line();
  219. $input = forward_static_call_array(array(__CLASS__, 'prompt'), $args);
  220. }
  221. // If options are provided and the choice is not in the array, tell them to try again
  222. if ( ! empty($options) and ! in_array($input, $options))
  223. {
  224. static::write('This is not a valid option. Please try again.');
  225. static::new_line();
  226. $input = forward_static_call_array(array(__CLASS__, 'prompt'), $args);
  227. }
  228. return $input;
  229. }
  230. /**
  231. * Outputs a string to the cli. If you send an array it will implode them
  232. * with a line break.
  233. *
  234. * @param string|array $text the text to output, or array of lines
  235. */
  236. public static function write($text = '', $foreground = null, $background = null)
  237. {
  238. if (is_array($text))
  239. {
  240. $text = implode(PHP_EOL, $text);
  241. }
  242. if ($foreground or $background)
  243. {
  244. $text = static::color($text, $foreground, $background);
  245. }
  246. fwrite(STDOUT, $text.PHP_EOL);
  247. }
  248. /**
  249. * Outputs an error to the CLI using STDERR instead of STDOUT
  250. *
  251. * @param string|array $text the text to output, or array of errors
  252. */
  253. public static function error($text = '', $foreground = 'light_red', $background = null)
  254. {
  255. if (is_array($text))
  256. {
  257. $text = implode(PHP_EOL, $text);
  258. }
  259. if ($foreground OR $background)
  260. {
  261. $text = static::color($text, $foreground, $background);
  262. }
  263. fwrite(STDERR, $text.PHP_EOL);
  264. }
  265. /**
  266. * Beeps a certain number of times.
  267. *
  268. * @param int $num the number of times to beep
  269. */
  270. public static function beep($num = 1)
  271. {
  272. echo str_repeat("\x07", $num);
  273. }
  274. /**
  275. * Waits a certain number of seconds, optionally showing a wait message and
  276. * waiting for a key press.
  277. *
  278. * @param int $seconds number of seconds
  279. * @param bool $countdown show a countdown or not
  280. */
  281. public static function wait($seconds = 0, $countdown = false)
  282. {
  283. if ($countdown === true)
  284. {
  285. $time = $seconds;
  286. while ($time > 0)
  287. {
  288. fwrite(STDOUT, $time.'... ');
  289. sleep(1);
  290. $time--;
  291. }
  292. static::write();
  293. }
  294. else
  295. {
  296. if ($seconds > 0)
  297. {
  298. sleep($seconds);
  299. }
  300. else
  301. {
  302. static::write(static::$wait_msg);
  303. static::input();
  304. }
  305. }
  306. }
  307. /**
  308. * if operating system === windows
  309. */
  310. public static function is_windows()
  311. {
  312. return 'win' === strtolower(substr(php_uname("s"), 0, 3));
  313. }
  314. /**
  315. * Enter a number of empty lines
  316. *
  317. * @param integer Number of lines to output
  318. * @return void
  319. */
  320. public static function new_line($num = 1)
  321. {
  322. // Do it once or more, write with empty string gives us a new line
  323. for($i = 0; $i < $num; $i++)
  324. {
  325. static::write();
  326. }
  327. }
  328. /**
  329. * Clears the screen of output
  330. *
  331. * @return void
  332. */
  333. public static function clear_screen()
  334. {
  335. static::is_windows()
  336. // Windows is a bit crap at this, but their terminal is tiny so shove this in
  337. ? static::new_line(40)
  338. // Anything with a flair of Unix will handle these magic characters
  339. : fwrite(STDOUT, chr(27)."[H".chr(27)."[2J");
  340. }
  341. /**
  342. * Returns the given text with the correct color codes for a foreground and
  343. * optionally a background color.
  344. *
  345. * @param string $text the text to color
  346. * @param string $foreground the foreground color
  347. * @param string $background the background color
  348. * @return string the color coded string
  349. */
  350. public static function color($text, $foreground, $background = null)
  351. {
  352. if (static::is_windows() and ! \Input::server('ANSICON'))
  353. {
  354. return $text;
  355. }
  356. if ( ! array_key_exists($foreground, static::$foreground_colors))
  357. {
  358. throw new \FuelException('Invalid CLI foreground color: '.$foreground);
  359. }
  360. if ( $background !== null and ! array_key_exists($background, static::$background_colors))
  361. {
  362. throw new \FuelException('Invalid CLI background color: '.$background);
  363. }
  364. $string = "\033[".static::$foreground_colors[$foreground]."m";
  365. if ($background !== null)
  366. {
  367. $string .= "\033[".static::$background_colors[$background]."m";
  368. }
  369. $string .= $text."\033[0m";
  370. return $string;
  371. }
  372. /**
  373. * Spawn Background Process
  374. *
  375. * Launches a background process (note, provides no security itself, $call must be sanitised prior to use)
  376. * @param string $call the system call to make
  377. * @return void
  378. * @author raccettura
  379. * @link http://robert.accettura.com/blog/2006/09/14/asynchronous-processing-with-php/
  380. */
  381. public static function spawn($call, $output = '/dev/null')
  382. {
  383. // Windows
  384. if(static::is_windows())
  385. {
  386. pclose(popen('start /b '.$call, 'r'));
  387. }
  388. // Some sort of UNIX
  389. else
  390. {
  391. pclose(popen($call.' > '.$output.' &', 'r'));
  392. }
  393. }
  394. }