PageRenderTime 42ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Console/Adapter/Posix.php

http://github.com/zendframework/zf2
PHP | 407 lines | 214 code | 45 blank | 148 comment | 25 complexity | 370ae026587a0be6674ff3513ca6e673 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Console\Adapter;
  10. use ReflectionClass;
  11. use Zend\Console\Charset;
  12. use Zend\Console\Exception;
  13. use Zend\Console\Color\Xterm256;
  14. use Zend\Console\ColorInterface as Color;
  15. /**
  16. * @todo Add GNU readline support
  17. * @link http://en.wikipedia.org/wiki/ANSI_escape_code
  18. */
  19. class Posix extends AbstractAdapter
  20. {
  21. /**
  22. * Whether or not mbstring is enabled
  23. *
  24. * @var null|bool
  25. */
  26. protected static $hasMBString;
  27. /**
  28. * @var Charset\CharsetInterface
  29. */
  30. protected $charset;
  31. /**
  32. * Map of colors to ANSI codes
  33. *
  34. * @var array
  35. */
  36. protected static $ansiColorMap = array(
  37. 'fg' => array(
  38. Color::NORMAL => '22;39',
  39. Color::RESET => '22;39',
  40. Color::BLACK => '0;30',
  41. Color::RED => '0;31',
  42. Color::GREEN => '0;32',
  43. Color::YELLOW => '0;33',
  44. Color::BLUE => '0;34',
  45. Color::MAGENTA => '0;35',
  46. Color::CYAN => '0;36',
  47. Color::WHITE => '0;37',
  48. Color::GRAY => '1;30',
  49. Color::LIGHT_RED => '1;31',
  50. Color::LIGHT_GREEN => '1;32',
  51. Color::LIGHT_YELLOW => '1;33',
  52. Color::LIGHT_BLUE => '1;34',
  53. Color::LIGHT_MAGENTA => '1;35',
  54. Color::LIGHT_CYAN => '1;36',
  55. Color::LIGHT_WHITE => '1;37',
  56. ),
  57. 'bg' => array(
  58. Color::NORMAL => '0;49',
  59. Color::RESET => '0;49',
  60. Color::BLACK => '40',
  61. Color::RED => '41',
  62. Color::GREEN => '42',
  63. Color::YELLOW => '43',
  64. Color::BLUE => '44',
  65. Color::MAGENTA => '45',
  66. Color::CYAN => '46',
  67. Color::WHITE => '47',
  68. Color::GRAY => '40',
  69. Color::LIGHT_RED => '41',
  70. Color::LIGHT_GREEN => '42',
  71. Color::LIGHT_YELLOW => '43',
  72. Color::LIGHT_BLUE => '44',
  73. Color::LIGHT_MAGENTA => '45',
  74. Color::LIGHT_CYAN => '46',
  75. Color::LIGHT_WHITE => '47',
  76. ),
  77. );
  78. /**
  79. * Last fetched TTY mode
  80. *
  81. * @var string|null
  82. */
  83. protected $lastTTYMode = null;
  84. /**
  85. * Write a single line of text to console and advance cursor to the next line.
  86. *
  87. * This override works around a bug in some terminals that cause the background color
  88. * to fill the next line after EOL. To remedy this, we are sending the colored string with
  89. * appropriate color reset sequences before sending EOL character.
  90. *
  91. * @link https://github.com/zendframework/zf2/issues/4167
  92. * @param string $text
  93. * @param null|int $color
  94. * @param null|int $bgColor
  95. */
  96. public function writeLine($text = "", $color = null, $bgColor = null)
  97. {
  98. $this->write($text, $color, $bgColor);
  99. $this->write(PHP_EOL);
  100. }
  101. /**
  102. * Determine and return current console width.
  103. *
  104. * @return int
  105. */
  106. public function getWidth()
  107. {
  108. static $width;
  109. if ($width > 0) {
  110. return $width;
  111. }
  112. /**
  113. * Try to read env variable
  114. */
  115. if (($result = getenv('COLUMNS')) !== false) {
  116. return $width = (int) $result;
  117. }
  118. /**
  119. * Try to read console size from "tput" command
  120. */
  121. $result = exec('tput cols', $output, $return);
  122. if (!$return && is_numeric($result)) {
  123. return $width = (int) $result;
  124. }
  125. return $width = parent::getWidth();
  126. }
  127. /**
  128. * Determine and return current console height.
  129. *
  130. * @return false|int
  131. */
  132. public function getHeight()
  133. {
  134. static $height;
  135. if ($height > 0) {
  136. return $height;
  137. }
  138. // Try to read env variable
  139. if (($result = getenv('LINES')) !== false) {
  140. return $height = (int) $result;
  141. }
  142. // Try to read console size from "tput" command
  143. $result = exec('tput lines', $output, $return);
  144. if (!$return && is_numeric($result)) {
  145. return $height = (int) $result;
  146. }
  147. return $height = parent::getHeight();
  148. }
  149. /**
  150. * Run a mode command and store results
  151. *
  152. * @return void
  153. */
  154. protected function runModeCommand()
  155. {
  156. exec('mode', $output, $return);
  157. if ($return || !count($output)) {
  158. $this->modeResult = '';
  159. } else {
  160. $this->modeResult = trim(implode('', $output));
  161. }
  162. }
  163. /**
  164. * Check if console is UTF-8 compatible
  165. *
  166. * @return bool
  167. */
  168. public function isUtf8()
  169. {
  170. // Try to retrieve it from LANG env variable
  171. if (($lang = getenv('LANG')) !== false) {
  172. return stristr($lang, 'utf-8') || stristr($lang, 'utf8');
  173. }
  174. return false;
  175. }
  176. /**
  177. * Show console cursor
  178. */
  179. public function showCursor()
  180. {
  181. echo "\x1b[?25h";
  182. }
  183. /**
  184. * Hide console cursor
  185. */
  186. public function hideCursor()
  187. {
  188. echo "\x1b[?25l";
  189. }
  190. /**
  191. * Set cursor position
  192. * @param int $x
  193. * @param int $y
  194. */
  195. public function setPos($x, $y)
  196. {
  197. echo "\x1b[" . $y . ';' . $x . 'f';
  198. }
  199. /**
  200. * Prepare a string that will be rendered in color.
  201. *
  202. * @param string $string
  203. * @param int $color
  204. * @param null|int $bgColor
  205. * @throws Exception\BadMethodCallException
  206. * @return string
  207. */
  208. public function colorize($string, $color = null, $bgColor = null)
  209. {
  210. $color = $this->getColorCode($color, 'fg');
  211. $bgColor = $this->getColorCode($bgColor, 'bg');
  212. return ($color !== null ? "\x1b[" . $color . 'm' : '')
  213. . ($bgColor !== null ? "\x1b[" . $bgColor . 'm' : '')
  214. . $string
  215. . "\x1b[22;39m\x1b[0;49m";
  216. }
  217. /**
  218. * Change current drawing color.
  219. *
  220. * @param int $color
  221. * @throws Exception\BadMethodCallException
  222. */
  223. public function setColor($color)
  224. {
  225. $color = $this->getColorCode($color, 'fg');
  226. echo "\x1b[" . $color . 'm';
  227. }
  228. /**
  229. * Change current drawing background color
  230. *
  231. * @param int $bgColor
  232. * @throws Exception\BadMethodCallException
  233. */
  234. public function setBgColor($bgColor)
  235. {
  236. $bgColor = $this->getColorCode($bgColor, 'bg');
  237. echo "\x1b[" . ($bgColor) . 'm';
  238. }
  239. /**
  240. * Reset color to console default.
  241. */
  242. public function resetColor()
  243. {
  244. echo "\x1b[0;49m"; // reset bg color
  245. echo "\x1b[22;39m"; // reset fg bold, bright and faint
  246. echo "\x1b[25;39m"; // reset fg blink
  247. echo "\x1b[24;39m"; // reset fg underline
  248. }
  249. /**
  250. * Set Console charset to use.
  251. *
  252. * @param Charset\CharsetInterface $charset
  253. */
  254. public function setCharset(Charset\CharsetInterface $charset)
  255. {
  256. $this->charset = $charset;
  257. }
  258. /**
  259. * Get charset currently in use by this adapter.
  260. *
  261. * @return Charset\CharsetInterface $charset
  262. */
  263. public function getCharset()
  264. {
  265. if ($this->charset === null) {
  266. $this->charset = $this->getDefaultCharset();
  267. }
  268. return $this->charset;
  269. }
  270. /**
  271. * @return Charset\CharsetInterface
  272. */
  273. public function getDefaultCharset()
  274. {
  275. if ($this->isUtf8()) {
  276. return new Charset\Utf8;
  277. }
  278. return new Charset\DECSG();
  279. }
  280. /**
  281. * Read a single character from the console input
  282. *
  283. * @param string|null $mask A list of allowed chars
  284. * @return string
  285. */
  286. public function readChar($mask = null)
  287. {
  288. $this->setTTYMode('-icanon -echo');
  289. $stream = fopen('php://stdin', 'rb');
  290. do {
  291. $char = fgetc($stream);
  292. } while (strlen($char) !== 1 || ($mask !== null && false === strstr($mask, $char)));
  293. fclose($stream);
  294. $this->restoreTTYMode();
  295. return $char;
  296. }
  297. /**
  298. * Reset color to console default.
  299. */
  300. public function clear()
  301. {
  302. echo "\x1b[2J"; // reset bg color
  303. $this->setPos(1, 1); // reset cursor position
  304. }
  305. /**
  306. * Restore TTY (Console) mode to previous value.
  307. *
  308. * @return void
  309. */
  310. protected function restoreTTYMode()
  311. {
  312. if ($this->lastTTYMode === null) {
  313. return;
  314. }
  315. shell_exec('stty ' . escapeshellarg($this->lastTTYMode));
  316. }
  317. /**
  318. * Change TTY (Console) mode
  319. *
  320. * @link http://en.wikipedia.org/wiki/Stty
  321. * @param string $mode
  322. */
  323. protected function setTTYMode($mode)
  324. {
  325. // Store last mode
  326. $this->lastTTYMode = trim(`stty -g`);
  327. // Set new mode
  328. shell_exec('stty '.escapeshellcmd($mode));
  329. }
  330. /**
  331. * Get the final color code and throw exception on error
  332. *
  333. * @param null|int|Xterm256 $color
  334. * @param string $type (optional) Foreground 'fg' or background 'bg'.
  335. * @throws Exception\BadMethodCallException
  336. * @return string
  337. */
  338. protected function getColorCode($color, $type = 'fg')
  339. {
  340. if ($color instanceof Xterm256) {
  341. $r = new ReflectionClass($color);
  342. $code = $r->getStaticPropertyValue('color');
  343. if ($type == 'fg') {
  344. $code = sprintf($code, $color::FOREGROUND);
  345. } else {
  346. $code = sprintf($code, $color::BACKGROUND);
  347. }
  348. return $code;
  349. }
  350. if ($color !== null) {
  351. if (!isset(static::$ansiColorMap[$type][$color])) {
  352. throw new Exception\BadMethodCallException(sprintf(
  353. 'Unknown color "%s". Please use one of the Zend\Console\ColorInterface constants '
  354. . 'or use Zend\Console\Color\Xterm256::calculate',
  355. $color
  356. ));
  357. }
  358. return static::$ansiColorMap[$type][$color];
  359. }
  360. return;
  361. }
  362. }