ConsoleIo.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Console;
  16. use Cake\Console\ConsoleInput;
  17. use Cake\Console\ConsoleOutput;
  18. use Cake\Log\Engine\ConsoleLog;
  19. use Cake\Log\Log;
  20. /**
  21. * A wrapper around the various IO operations shell tasks need to do.
  22. *
  23. * Packages up the stdout, stderr, and stdin streams providing a simple
  24. * consistent interface for shells to use. This class also makes mocking streams
  25. * easy to do in unit tests.
  26. */
  27. class ConsoleIo {
  28. /**
  29. * The output stream
  30. *
  31. * @var \Cake\Console\ConsoleOutput
  32. */
  33. protected $_out;
  34. /**
  35. * The error stream
  36. *
  37. * @var \Cake\Console\ConsoleOutput
  38. */
  39. protected $_err;
  40. /**
  41. * The input stream
  42. *
  43. * @var \Cake\Console\ConsoleInput
  44. */
  45. protected $_in;
  46. /**
  47. * Output constant making verbose shells.
  48. *
  49. * @var int
  50. */
  51. const VERBOSE = 2;
  52. /**
  53. * Output constant for making normal shells.
  54. *
  55. * @var int
  56. */
  57. const NORMAL = 1;
  58. /**
  59. * Output constants for making quiet shells.
  60. *
  61. * @var int
  62. */
  63. const QUIET = 0;
  64. /**
  65. * The current output level.
  66. *
  67. * @var int
  68. */
  69. protected $_level = ConsoleIo::NORMAL;
  70. /**
  71. * The number of bytes last written to the output stream
  72. * used when overwriting the previous message.
  73. *
  74. * @var int
  75. */
  76. protected $_lastWritten = 0;
  77. /**
  78. * Constructor
  79. *
  80. * @param \Cake\Console\ConsoleOutput $out A ConsoleOutput object for stdout.
  81. * @param \Cake\Console\ConsoleOutput $err A ConsoleOutput object for stderr.
  82. * @param \Cake\Console\ConsoleInput $in A ConsoleInput object for stdin.
  83. */
  84. public function __construct(ConsoleOutput $out = null, ConsoleOutput $err = null, ConsoleInput $in = null) {
  85. $this->_out = $out ? $out : new ConsoleOutput('php://stdout');
  86. $this->_err = $err ? $err : new ConsoleOutput('php://stderr');
  87. $this->_in = $in ? $in : new ConsoleInput('php://stdin');
  88. }
  89. /**
  90. * Get/set the current output level.
  91. *
  92. * @param null|int $level The current output level.
  93. * @return int The current output level.
  94. */
  95. public function level($level = null) {
  96. if ($level !== null) {
  97. $this->_level = $level;
  98. }
  99. return $this->_level;
  100. }
  101. /**
  102. * Output at the verbose level.
  103. *
  104. * @param string|array $message A string or an array of strings to output
  105. * @param int $newlines Number of newlines to append
  106. * @return int|bool Returns the number of bytes returned from writing to stdout.
  107. */
  108. public function verbose($message, $newlines = 1) {
  109. return $this->out($message, $newlines, self::VERBOSE);
  110. }
  111. /**
  112. * Output at all levels.
  113. *
  114. * @param string|array $message A string or an array of strings to output
  115. * @param int $newlines Number of newlines to append
  116. * @return int|bool Returns the number of bytes returned from writing to stdout.
  117. */
  118. public function quiet($message, $newlines = 1) {
  119. return $this->out($message, $newlines, self::QUIET);
  120. }
  121. /**
  122. * Outputs a single or multiple messages to stdout. If no parameters
  123. * are passed outputs just a newline.
  124. *
  125. * ### Output levels
  126. *
  127. * There are 3 built-in output level. Shell::QUIET, Shell::NORMAL, Shell::VERBOSE.
  128. * The verbose and quiet output levels, map to the `verbose` and `quiet` output switches
  129. * present in most shells. Using Shell::QUIET for a message means it will always display.
  130. * While using Shell::VERBOSE means it will only display when verbose output is toggled.
  131. *
  132. * @param string|array $message A string or an array of strings to output
  133. * @param int $newlines Number of newlines to append
  134. * @param int $level The message's output level, see above.
  135. * @return int|bool Returns the number of bytes returned from writing to stdout.
  136. */
  137. public function out($message = null, $newlines = 1, $level = ConsoleIo::NORMAL) {
  138. if ($level <= $this->_level) {
  139. $this->_lastWritten = $this->_out->write($message, $newlines);
  140. return $this->_lastWritten;
  141. }
  142. return true;
  143. }
  144. /**
  145. * Overwrite some already output text.
  146. *
  147. * Useful for building progress bars, or when you want to replace
  148. * text already output to the screen with new text.
  149. *
  150. * **Warning** You cannot overwrite text that contains newlines.
  151. *
  152. * @param array|string $message The message to output.
  153. * @param int $newlines Number of newlines to append.
  154. * @param int $size The number of bytes to overwrite. Defaults to the
  155. * length of the last message output.
  156. * @return int|bool Returns the number of bytes returned from writing to stdout.
  157. */
  158. public function overwrite($message, $newlines = 1, $size = null) {
  159. $size = $size ?: $this->_lastWritten;
  160. // Output backspaces.
  161. $this->out(str_repeat("\x08", $size), 0);
  162. $newBytes = $this->out($message, 0);
  163. // Fill any remaining bytes with spaces.
  164. $fill = $size - $newBytes;
  165. if ($fill > 0) {
  166. $this->out(str_repeat(' ', $fill), 0);
  167. }
  168. if ($newlines) {
  169. $this->out($this->nl($newlines), 0);
  170. }
  171. }
  172. /**
  173. * Outputs a single or multiple error messages to stderr. If no parameters
  174. * are passed outputs just a newline.
  175. *
  176. * @param string|array $message A string or an array of strings to output
  177. * @param int $newlines Number of newlines to append
  178. * @return void
  179. */
  180. public function err($message = null, $newlines = 1) {
  181. $this->_err->write($message, $newlines);
  182. }
  183. /**
  184. * Returns a single or multiple linefeeds sequences.
  185. *
  186. * @param int $multiplier Number of times the linefeed sequence should be repeated
  187. * @return string
  188. */
  189. public function nl($multiplier = 1) {
  190. return str_repeat(ConsoleOutput::LF, $multiplier);
  191. }
  192. /**
  193. * Outputs a series of minus characters to the standard output, acts as a visual separator.
  194. *
  195. * @param int $newlines Number of newlines to pre- and append
  196. * @param int $width Width of the line, defaults to 79
  197. * @return void
  198. */
  199. public function hr($newlines = 0, $width = 79) {
  200. $this->out(null, $newlines);
  201. $this->out(str_repeat('-', $width));
  202. $this->out(null, $newlines);
  203. }
  204. /**
  205. * Prompts the user for input, and returns it.
  206. *
  207. * @param string $prompt Prompt text.
  208. * @param string $default Default input value.
  209. * @return mixed Either the default value, or the user-provided input.
  210. */
  211. public function ask($prompt, $default = null) {
  212. return $this->_getInput($prompt, null, $default);
  213. }
  214. /**
  215. * Change the output mode of the stdout stream
  216. *
  217. * @param int $mode The output mode.
  218. * @return void
  219. * @see \Cake\Console\ConsoleOutput::outputAs()
  220. */
  221. public function outputAs($mode) {
  222. $this->_out->outputAs($mode);
  223. }
  224. /**
  225. * Add a new output style or get defined styles.
  226. *
  227. * @param string $style The style to get or create.
  228. * @param array $definition The array definition of the style to change or create a style
  229. * or false to remove a style.
  230. * @return mixed If you are getting styles, the style or null will be returned. If you are creating/modifying
  231. * styles true will be returned.
  232. * @see \Cake\Console\ConsoleOutput::styles()
  233. */
  234. public function styles($style = null, $definition = null) {
  235. $this->_out->styles($style, $definition);
  236. }
  237. /**
  238. * Prompts the user for input based on a list of options, and returns it.
  239. *
  240. * @param string $prompt Prompt text.
  241. * @param string|array $options Array or string of options.
  242. * @param string $default Default input value.
  243. * @return mixed Either the default value, or the user-provided input.
  244. */
  245. public function askChoice($prompt, $options, $default = null) {
  246. $originalOptions = $options;
  247. if ($options && is_string($options)) {
  248. if (strpos($options, ',')) {
  249. $options = explode(',', $options);
  250. } elseif (strpos($options, '/')) {
  251. $options = explode('/', $options);
  252. } else {
  253. $options = [$options];
  254. }
  255. }
  256. $printOptions = '(' . implode('/', $options) . ')';
  257. $options = array_merge(
  258. array_map('strtolower', $options),
  259. array_map('strtoupper', $options),
  260. $options
  261. );
  262. $in = '';
  263. while ($in === '' || !in_array($in, $options)) {
  264. $in = $this->_getInput($prompt, $printOptions, $default);
  265. }
  266. return $in;
  267. }
  268. /**
  269. * Prompts the user for input, and returns it.
  270. *
  271. * @param string $prompt Prompt text.
  272. * @param string|array $options Array or string of options.
  273. * @param string $default Default input value.
  274. * @return string Either the default value, or the user-provided input.
  275. */
  276. protected function _getInput($prompt, $options, $default) {
  277. $optionsText = '';
  278. if (isset($options)) {
  279. $optionsText = " $options ";
  280. }
  281. $defaultText = '';
  282. if ($default !== null) {
  283. $defaultText = "[$default] ";
  284. }
  285. $this->_out->write('<question>' . $prompt . "</question>$optionsText\n$defaultText> ", 0);
  286. $result = $this->_in->read();
  287. $result = trim($result);
  288. if ($default !== null && ($result === '' || $result === null)) {
  289. return $default;
  290. }
  291. return $result;
  292. }
  293. /**
  294. * Connects or disconnects the loggers to the console output.
  295. *
  296. * Used to enable or disable logging stream output to stdout and stderr
  297. * If you don't wish all log output in stdout or stderr
  298. * through Cake's Log class, call this function with `$enable=false`.
  299. *
  300. * @param bool $enable Whether you want loggers on or off.
  301. * @return void
  302. */
  303. public function setLoggers($enable) {
  304. Log::drop('stdout');
  305. Log::drop('stderr');
  306. if (!$enable) {
  307. return;
  308. }
  309. $stdout = new ConsoleLog([
  310. 'types' => ['notice', 'info', 'debug'],
  311. 'stream' => $this->_out
  312. ]);
  313. Log::config('stdout', ['engine' => $stdout]);
  314. $stderr = new ConsoleLog([
  315. 'types' => ['emergency', 'alert', 'critical', 'error', 'warning'],
  316. 'stream' => $this->_err,
  317. ]);
  318. Log::config('stderr', ['engine' => $stderr]);
  319. }
  320. }