Console.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. <?php
  2. /**
  3. * Hoa
  4. *
  5. *
  6. * @license
  7. *
  8. * New BSD License
  9. *
  10. * Copyright © 2007-2017, Hoa community. All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the Hoa nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. namespace Hoa\Console;
  36. use Hoa\Consistency;
  37. /**
  38. * Class \Hoa\Console.
  39. *
  40. * A set of utils and helpers about the console.
  41. *
  42. * @copyright Copyright © 2007-2017 Hoa community
  43. * @license New BSD License
  44. */
  45. class Console
  46. {
  47. /**
  48. * Pipe mode: FIFO.
  49. *
  50. * @var int
  51. */
  52. const IS_FIFO = 0;
  53. /**
  54. * Pipe mode: character.
  55. *
  56. * @var int
  57. */
  58. const IS_CHARACTER = 1;
  59. /**
  60. * Pipe mode: directory.
  61. *
  62. * @var int
  63. */
  64. const IS_DIRECTORY = 2;
  65. /**
  66. * Pipe mode: block.
  67. *
  68. * @var int
  69. */
  70. const IS_BLOCK = 3;
  71. /**
  72. * Pipe mode: regular.
  73. *
  74. * @var int
  75. */
  76. const IS_REGULAR = 4;
  77. /**
  78. * Pipe mode: link.
  79. *
  80. * @var int
  81. */
  82. const IS_LINK = 5;
  83. /**
  84. * Pipe mode: socket.
  85. *
  86. * @var int
  87. */
  88. const IS_SOCKET = 6;
  89. /**
  90. * Pipe mode: whiteout.
  91. *
  92. * @var int
  93. */
  94. const IS_WHITEOUT = 7;
  95. /**
  96. * Advanced interaction is on.
  97. *
  98. * @var bool
  99. */
  100. private static $_advanced = null;
  101. /**
  102. * Previous STTY configuration.
  103. *
  104. * @var string
  105. */
  106. private static $_old = null;
  107. /**
  108. * Mode.
  109. *
  110. * @var array
  111. */
  112. protected static $_mode = [];
  113. /**
  114. * Input.
  115. *
  116. * @var \Hoa\Console\Input
  117. */
  118. protected static $_input = null;
  119. /**
  120. * Output.
  121. *
  122. * @var \Hoa\Console\Output
  123. */
  124. protected static $_output = null;
  125. /**
  126. * Tput.
  127. *
  128. * @var \Hoa\Console\Tput
  129. */
  130. protected static $_tput = null;
  131. /**
  132. * Prepare the environment for advanced interactions.
  133. *
  134. * @param bool $force Force it if STDIN is not direct.
  135. * @return bool
  136. */
  137. public static function advancedInteraction($force = false)
  138. {
  139. if (null !== self::$_advanced) {
  140. return self::$_advanced;
  141. }
  142. if (OS_WIN) {
  143. return self::$_advanced = false;
  144. }
  145. if (false === $force &&
  146. true === defined('STDIN') &&
  147. false === self::isDirect(STDIN)) {
  148. return self::$_advanced = false;
  149. }
  150. self::$_old = Processus::execute('stty -g < /dev/tty', false);
  151. Processus::execute('stty -echo -icanon min 1 time 0 < /dev/tty', false);
  152. return self::$_advanced = true;
  153. }
  154. /**
  155. * Restore previous interaction options.
  156. *
  157. * @return void
  158. */
  159. public static function restoreInteraction()
  160. {
  161. if (null === self::$_old) {
  162. return;
  163. }
  164. Processus::execute('stty ' . self::$_old . ' < /dev/tty', false);
  165. return;
  166. }
  167. /**
  168. * Get mode of a certain pipe.
  169. * Inspired by sys/stat.h.
  170. *
  171. * @param resource $pipe Pipe.
  172. * @return int
  173. */
  174. public static function getMode($pipe = STDIN)
  175. {
  176. $_pipe = (int) $pipe;
  177. if (isset(self::$_mode[$_pipe])) {
  178. return self::$_mode[$_pipe];
  179. }
  180. $stat = fstat($pipe);
  181. switch ($stat['mode'] & 0170000) {
  182. // named pipe (fifo).
  183. case 0010000:
  184. $mode = self::IS_FIFO;
  185. break;
  186. // character special.
  187. case 0020000:
  188. $mode = self::IS_CHARACTER;
  189. break;
  190. // directory.
  191. case 0040000:
  192. $mode = self::IS_DIRECTORY;
  193. break;
  194. // block special.
  195. case 0060000:
  196. $mode = self::IS_BLOCK;
  197. break;
  198. // regular.
  199. case 0100000:
  200. $mode = self::IS_REGULAR;
  201. break;
  202. // symbolic link.
  203. case 0120000:
  204. $mode = self::IS_LINK;
  205. break;
  206. // socket.
  207. case 0140000:
  208. $mode = self::IS_SOCKET;
  209. break;
  210. // whiteout.
  211. case 0160000:
  212. $mode = self::IS_WHITEOUT;
  213. break;
  214. default:
  215. $mode = -1;
  216. }
  217. return self::$_mode[$_pipe] = $mode;
  218. }
  219. /**
  220. * Check whether a certain pipe is a character device (keyboard, screen
  221. * etc.).
  222. * For example:
  223. * $ php Mode.php
  224. * In this case, self::isDirect(STDOUT) will return true.
  225. *
  226. * @param resource $pipe Pipe.
  227. * @return bool
  228. */
  229. public static function isDirect($pipe)
  230. {
  231. return self::IS_CHARACTER === self::getMode($pipe);
  232. }
  233. /**
  234. * Check whether a certain pipe is a pipe.
  235. * For example:
  236. * $ php Mode.php | foobar
  237. * In this case, self::isPipe(STDOUT) will return true.
  238. *
  239. * @param resource $pipe Pipe.
  240. * @return bool
  241. */
  242. public static function isPipe($pipe)
  243. {
  244. return self::IS_FIFO === self::getMode($pipe);
  245. }
  246. /**
  247. * Check whether a certain pipe is a redirection.
  248. * For example:
  249. * $ php Mode.php < foobar
  250. * In this case, self::isRedirection(STDIN) will return true.
  251. *
  252. * @param resource $pipe Pipe.
  253. * @return bool
  254. */
  255. public static function isRedirection($pipe)
  256. {
  257. $mode = self::getMode($pipe);
  258. return
  259. self::IS_REGULAR === $mode ||
  260. self::IS_DIRECTORY === $mode ||
  261. self::IS_LINK === $mode ||
  262. self::IS_SOCKET === $mode ||
  263. self::IS_BLOCK === $mode;
  264. }
  265. /**
  266. * Set input layer.
  267. *
  268. * @param \Hoa\Console\Input $input Input.
  269. * @return \Hoa\Console\Input
  270. */
  271. public static function setInput(Input $input)
  272. {
  273. $old = static::$_input;
  274. static::$_input = $input;
  275. return $old;
  276. }
  277. /**
  278. * Get input layer.
  279. *
  280. * @return \Hoa\Console\Input
  281. */
  282. public static function getInput()
  283. {
  284. if (null === static::$_input) {
  285. static::$_input = new Input();
  286. }
  287. return static::$_input;
  288. }
  289. /**
  290. * Set output layer.
  291. *
  292. * @param \Hoa\Console\Output $output Output.
  293. * @return \Hoa\Console\Output
  294. */
  295. public static function setOutput(Output $output)
  296. {
  297. $old = static::$_output;
  298. static::$_output = $output;
  299. return $old;
  300. }
  301. /**
  302. * Get output layer.
  303. *
  304. * @return \Hoa\Console\Output
  305. */
  306. public static function getOutput()
  307. {
  308. if (null === static::$_output) {
  309. static::$_output = new Output();
  310. }
  311. return static::$_output;
  312. }
  313. /**
  314. * Set tput.
  315. *
  316. * @param \Hoa\Console\Tput $tput Tput.
  317. * @return \Hoa\Console\Tput
  318. */
  319. public static function setTput(Tput $tput)
  320. {
  321. $old = static::$_tput;
  322. static::$_tput = $tput;
  323. return $old;
  324. }
  325. /**
  326. * Get the current tput instance of the current process.
  327. *
  328. * @return \Hoa\Console\Tput
  329. */
  330. public static function getTput()
  331. {
  332. if (null === static::$_tput) {
  333. static::$_tput = new Tput();
  334. }
  335. return static::$_tput;
  336. }
  337. /**
  338. * Check whether we are running behind TMUX(1).
  339. *
  340. * @return bool
  341. */
  342. public static function isTmuxRunning()
  343. {
  344. return isset($_SERVER['TMUX']);
  345. }
  346. }
  347. /**
  348. * Restore interaction.
  349. */
  350. Consistency::registerShutdownFunction(xcallable('Hoa\Console\Console::restoreInteraction'));
  351. /**
  352. * Flex entity.
  353. */
  354. Consistency::flexEntity('Hoa\Console\Console');