1 <?php
2 3 4 5 6 7 8 9 10 11 12
13 namespace Yiinitializr\Cli;
14 15 16 17 18 19 20
21 class Console
22 {
23 24 25
26 protected static $FGCOLOR = array(
27 'black' => 30,
28 'red' => 31,
29 'green' => 32,
30 'brown' => 33,
31 'blue' => 34,
32 'purple' => 35,
33 'cyan' => 36,
34 'grey' => 37,
35 'yellow' => 33,
36 );
37
38 39 40
41 protected static $STYLE = array(
42 'normal' => 0,
43 'bold' => 1,
44 'light' => 1,
45 'underscore' => 4,
46 'underline' => 4,
47 'blink' => 5,
48 'inverse' => 6,
49 'hidden' => 8,
50 'concealed' => 8,
51 );
52
53 54 55
56 protected static $BGCOLOR = array(
57 'black' => 40,
58 'red' => 41,
59 'green' => 42,
60 'brown' => 43,
61 'yellow' => 43,
62 'blue' => 44,
63 'purple' => 45,
64 'cyan' => 46,
65 'grey' => 47,
66 );
67
68 69 70
71 protected static $CONVERSIONS = array(
72 '%y' => array('yellow', null, null),
73 '%g' => array('green', null, null),
74 '%b' => array('blue', null, null),
75 '%r' => array('red', null, null),
76 '%p' => array('purple', null, null),
77 '%m' => array('purple', null, null),
78 '%c' => array('cyan', null, null),
79 '%w' => array('grey', null, null),
80 '%k' => array('black', null, null),
81 '%n' => array('reset', null, null),
82 '%Y' => array('yellow', 'light', null),
83 '%G' => array('green', 'light', null),
84 '%B' => array('blue', 'light', null),
85 '%R' => array('red', 'light', null),
86 '%P' => array('purple', 'light', null),
87 '%M' => array('purple', 'light', null),
88 '%C' => array('cyan', 'light', null),
89 '%W' => array('grey', 'light', null),
90 '%K' => array('black', 'light', null),
91 '%N' => array('reset', 'light', null),
92 '%3' => array(null, null, 'yellow'),
93 '%2' => array(null, null, 'green'),
94 '%4' => array(null, null, 'blue'),
95 '%1' => array(null, null, 'red'),
96 '%5' => array(null, null, 'purple'),
97 '%6' => array(null, null, 'cyan'),
98 '%7' => array(null, null, 'grey'),
99 '%0' => array(null, null, 'black'),
100 '%F' => array(null, 'blink', null),
101 '%U' => array(null, 'underline', null),
102 '%8' => array(null, 'inverse', null),
103 '%9' => array(null, 'bold', null),
104 '%_' => array(null, 'bold', null),
105 );
106
107 108 109 110 111 112 113 114 115 116
117 public static function color($fgcolor, $style, $bgcolor)
118 {
119 $code = array();
120 if ($fgcolor == 'reset') {
121 return "\033[0m";
122 }
123 if (isset(static::$FGCOLOR[$fgcolor])) {
124 $code[] = static::$FGCOLOR[$fgcolor];
125 }
126 if (isset(static::$STYLE[$style])) {
127 $code[] = static::$STYLE[$style];
128 }
129 if (isset(static::$BGCOLOR[$bgcolor])) {
130 $code[] = static::$BGCOLOR[$bgcolor];
131 }
132 if (empty($code)) {
133 $code[] = 0;
134 }
135 return "\033[" . implode(';', $code) . 'm';
136 }
137
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
173 public static function colorize($text, $color = true)
174 {
175 $text = str_replace('%%', '% ', $text);
176 foreach (static::$CONVERSIONS as $key => $value) {
177 list($fgcolor, $style, $bgcolor) = $value;
178 $text = str_replace(
179 $key,
180 $color ? static::color($fgcolor, $style, $bgcolor) : '',
181 $text
182 );
183 }
184 return str_replace('% ', '%', $text);
185 }
186
187 188 189 190 191 192 193
194 public static function decolorize($text)
195 {
196 return static::colorize($text, false);
197 }
198
199 200 201 202 203 204 205
206 public static function strip($text)
207 {
208 return preg_replace('/\033\[(\d+)(;\d+)*m/', '', $text);
209 }
210
211 212 213 214 215 216 217
218 public static function stdin($raw = false)
219 {
220 return $raw ? fgets(STDIN) : rtrim(fgets(STDIN), PHP_EOL);
221 }
222
223 224 225 226 227 228 229 230
231 public static function stdout($text, $raw = false)
232 {
233 if ($raw) {
234 return fwrite(STDOUT, $text);
235 } elseif (extension_loaded('posix') && posix_isatty(STDOUT)) {
236 return fwrite(STDOUT, static::colorize($text));
237 } else {
238 return fwrite(STDOUT, static::decolorize($text));
239 }
240 }
241
242 243 244 245 246 247 248 249
250 public static function stderr($text, $raw = false)
251 {
252 if ($raw) {
253 return fwrite(STDERR, $text);
254 } elseif (extension_loaded('posix') && posix_isatty(STDERR)) {
255 return fwrite(STDERR, static::colorize($text));
256 } else {
257 return fwrite(STDERR, static::decolorize($text));
258 }
259 }
260
261 262 263 264 265 266 267 268
269 public static function error($text = null, $raw = false)
270 {
271 return static::stderr($text . PHP_EOL, $raw);
272 }
273
274 275 276 277 278 279 280 281
282 public static function input($prompt = null)
283 {
284 if (isset($prompt)) {
285 static::stdout($prompt);
286 }
287 return static::stdin();
288 }
289
290 291 292 293 294 295 296 297
298 public static function output($text = null, $raw = false)
299 {
300 return static::stdout($text . PHP_EOL, $raw);
301 }
302
303 304 305 306 307 308 309 310
311 public static function prompt($text, $options = array())
312 {
313 $options = $options + array(
314 'required' => false,
315 'default' => null,
316 'pattern' => null,
317 'validator' => null,
318 'error' => 'Input unacceptable.',
319 );
320
321 top:
322 if ($options['default']) {
323 $input = static::input("$text [" . $options['default'] . ']: ');
324 } else {
325 $input = static::input("$text: ");
326 }
327
328 if (!strlen($input)) {
329 if (isset($options['default'])) {
330 $input = $options['default'];
331 } elseif ($options['required']) {
332 static::output($options['error']);
333 goto top;
334 }
335 } elseif ($options['pattern'] && !preg_match($options['pattern'], $input)) {
336 static::output($options['error']);
337 goto top;
338 } elseif ($options['validator'] &&
339 !call_user_func_array($options['validator'], array($input, &$error))) {
340 static::output(isset($error) ? $error : $options['error']);
341 goto top;
342 }
343
344 return $input;
345 }
346
347 348 349 350 351 352 353
354 public static function confirm($text)
355 {
356 top:
357 $input = strtolower(static::input("$text [y/n]: "));
358 if (!in_array($input, array('y', 'n'))) goto top;
359 return $input === 'y' ? true : false;
360 }
361
362 363 364 365 366 367 368 369 370
371 public static function select($text, $options = array())
372 {
373 top:
374 static::stdout("$text [" . implode(',', array_keys($options)) . ",?]: ");
375 $input = static::stdin();
376 if ($input === '?') {
377 foreach ($options as $key => $value) {
378 echo " $key - $value\n";
379 }
380 echo " ? - Show help\n";
381 goto top;
382 } elseif (!in_array($input, array_keys($options))) goto top;
383 return $input;
384 }
385
386 387 388 389 390 391 392 393 394 395 396 397 398
399 public static function work(\Closure $callable)
400 {
401 if (!extension_loaded('pcntl')) {
402 throw new \Exception('pcntl extension required');
403 }
404
405 if (!extension_loaded('sockets')) {
406 throw new \Exception('sockets extension required');
407 }
408
409 $spinner = array('|', '/', '-', '\\');
410 $i = 0; $l = count($spinner);
411 $delay = 100000;
412
413 $func = new \ReflectionFunction($callable);
414
415 $socket = (bool)$func->getNumberOfParameters();
416
417 if ($socket) {
418 $sockets = array();
419 if (socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets) === false) {
420 return false;
421 }
422 }
423
424 $pid = pcntl_fork();
425
426 if ($pid > 0) {
427 $done = false;
428 $retval = 0;
429 pcntl_signal(SIGCHLD, function() use ($pid, &$done, &$retval) {
430 $child_pid = pcntl_waitpid($pid, $status);
431 if (pcntl_wifexited($status)) {
432 $retval = pcntl_wexitstatus($status);
433 }
434 $done = true;
435 });
436
437 if ($socket) {
438 $text = '';
439 while (!$done) {
440 $r = array($sockets[1]);
441 $w = null;
442 $e = null;
443 if ($status = socket_select($r, $w, $e, 0)) {
444 $data = socket_read($sockets[1], 4096, PHP_NORMAL_READ);
445 if ($data === false) {
446 throw new \Exception(
447 sprintf(
448 'socket write error %s',
449 socket_strerror(socket_last_error($sockets[1]))
450 )
451 );
452 }
453 echo str_repeat(chr(8), strlen($text));
454 $text = rtrim($data, "\n");
455 Console::stdout($text);
456 } else {
457 pcntl_signal_dispatch();
458 }
459 usleep($delay);
460 }
461 echo str_repeat(chr(8), strlen($text));
462 socket_close($sockets[0]);
463 socket_close($sockets[1]);
464 } else {
465 while (!$done) {
466 pcntl_signal_dispatch();
467 echo $spinner[$i];
468 usleep($delay);
469 echo chr(8);
470 $i = $i === $l - 1 ? 0 : $i + 1;
471 }
472 }
473
474 return $retval;
475 } elseif ($pid === 0) {
476 if ($socket) {
477 call_user_func($callable, $sockets[0]);
478 } else {
479 call_user_func($callable);
480 }
481 exit;
482 } else {
483
484 return false;
485 }
486 }
487 }