Overview
  • Namespace
  • Class
  • Tree

Namespaces

  • PHP
  • Yiinitializr
    • Cli
    • Clio
    • Composer
    • Helpers

Classes

  • Console
  1 <?php
  2 /**
  3  * Console class file.
  4  *
  5  * @author Antonio Ramirez <amigo.cobos@gmail.com>
  6  * @author Nofriandi Ramenta <nramenta@gmail.com>
  7  * @link http://www.ramirezcobos.com/
  8  * @link http://www.2amigos.us/
  9  * @link https://github.com/nramenta/clio
 10  * @copyright 2013 2amigOS! Consultation Group LLC
 11  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 12  */
 13 namespace Yiinitializr\Cli;
 14 /**
 15  * Console provides a set of useful functions to work on the terminal
 16  *
 17  * @author Antonio Ramirez <amigo.cobos@gmail.com>
 18  * @package Yiinitializr.Cli
 19  * @since 1.0
 20  */
 21 class Console
 22 {
 23     /**
 24      * Text foreground colors.
 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      * Text styling.
 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      * Text background color.
 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      * Color specifier conversion table. Taken from PEAR's Console_Color.
 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      * Create ANSI-control codes for text foreground and background colors, and
109      * styling.
110      *
111      * @param string $fgcolor Text foreground color
112      * @param string $style   Text style
113      * @param string $bgcolor Text background color
114      *
115      * @return string ANSI-control code
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      * aken from PEAR's Console_Color:
140      *
141      * Converts colorcodes in the format %y (for yellow) into ansi-control
142      * codes. The conversion table is: ('bold' meaning 'light' on some
143      * terminals). It's almost the same conversion table irssi uses.
144      * <pre>
145      *                  text      text            background
146      *      ------------------------------------------------
147      *      %k %K %0    black     dark grey       black
148      *      %r %R %1    red       bold red        red
149      *      %g %G %2    green     bold green      green
150      *      %y %Y %3    yellow    bold yellow     yellow
151      *      %b %B %4    blue      bold blue       blue
152      *      %m %M %5    magenta   bold magenta    magenta
153      *      %p %P       magenta (think: purple)
154      *      %c %C %6    cyan      bold cyan       cyan
155      *      %w %W %7    white     bold white      white
156      *
157      *      %F     Blinking, Flashing
158      *      %U     Underline
159      *      %8     Reverse
160      *      %_,%9  Bold
161      *
162      *      %n     Resets the color
163      *      %%     A single %
164      * </pre>
165      * First param is the string to convert, second is an optional flag if
166      * colors should be used. It defaults to true, if set to false, the
167      * colorcodes will just be removed (And %% will be transformed into %)
168      *
169      * @param $text
170      * @param bool $color
171      * @return mixed
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      * Strips a string from color specifiers.
189      *
190      * @param string $text String to strip
191      *
192      * @return string
193      */
194     public static function decolorize($text)
195     {
196         return static::colorize($text, false);
197     }
198 
199     /**
200      * Strips a string of ansi-control codes.
201      *
202      * @param string $text String to strip
203      *
204      * @return string
205      */
206     public static function strip($text)
207     {
208         return preg_replace('/\033\[(\d+)(;\d+)*m/', '', $text);
209     }
210 
211     /**
212      * Gets input from STDIN and returns a string right-trimmed for EOLs.
213      *
214      * @param bool $raw If set to true, returns the raw string without trimming
215      *
216      * @return string
217      */
218     public static function stdin($raw = false)
219     {
220         return $raw ? fgets(STDIN) : rtrim(fgets(STDIN), PHP_EOL);
221     }
222 
223     /**
224      * Prints text to STDOUT.
225      *
226      * @param string $text
227      * @param bool   $raw
228      *
229      * @return int|false Number of bytes printed or false on error
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      * Prints text to STDERR.
244      *
245      * @param string $text
246      * @param bool   $raw
247      *
248      * @return int|false Number of bytes printed or false on error
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      * Prints text to STDERR appended with a PHP_EOL.
263      *
264      * @param string $text
265      * @param bool   $raw
266      *
267      * @return int|false Number of bytes printed or false on error
268      */
269     public static function error($text = null, $raw = false)
270     {
271         return static::stderr($text . PHP_EOL, $raw);
272     }
273 
274     /**
275      * Asks the user for input. Ends when the user types a PHP_EOL. Optionally
276      * provide a prompt.
277      *
278      * @param string $prompt String prompt (optional)
279      *
280      * @return string User input
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      * Prints text to STDOUT appended with a PHP_EOL.
292      *
293      * @param string $text
294      * @param bool   $raw
295      *
296      * @return int|false Number of bytes printed or false on error
297      */
298     public static function output($text = null, $raw = false)
299     {
300         return static::stdout($text . PHP_EOL, $raw);
301     }
302 
303     /**
304      * Prompts the user for input
305      *
306      * @param string $text    Prompt string
307      * @param array  $options Set of options
308      *
309      * @return string
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      * Asks the user for a simple yes/no confirmation.
349      *
350      * @param string $text    Prompt string
351      *
352      * @return bool Either true or false
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      * Gives the user an option to choose from. Giving '?' as an input will show
364      * a list of options to choose from and their explanations.
365      *
366      * @param string $text    Prompt string
367      * @param array  $options Key-value array of options to choose from
368      *
369      * @return string An option character the user chose
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      * Execute a Closure as another process in the background while showing a
388      * status update. The status update can be an indefinite spinner or a string
389      * periodically sent from the background process, depending on whether the
390      * provided Closure object has a $socket parameter or not. Messaging to the
391      * main process is done by socket_* functions. The return value is either
392      * the return value of the background process, or false if the process fork
393      * failed.
394      *
395      * @param callable $callable Closure object
396      * @return bool|int
397      * @throws \Exception
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             // Unable to fork process.
484             return false;
485         }
486     }
487 }
API documentation generated by ApiGen 2.8.0