ConsoleIo.php 13 KB

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