Initial Commit
This commit is contained in:
487
app/lib/Yiinitializr/Cli/Console.php
Normal file
487
app/lib/Yiinitializr/Cli/Console.php
Normal file
@@ -0,0 +1,487 @@
|
||||
<?php
|
||||
/**
|
||||
* Console class file.
|
||||
*
|
||||
* @author Antonio Ramirez <amigo.cobos@gmail.com>
|
||||
* @author Nofriandi Ramenta <nramenta@gmail.com>
|
||||
* @link http://www.ramirezcobos.com/
|
||||
* @link http://www.2amigos.us/
|
||||
* @link https://github.com/nramenta/clio
|
||||
* @copyright 2013 2amigOS! Consultation Group LLC
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
||||
*/
|
||||
namespace Yiinitializr\Cli;
|
||||
/**
|
||||
* Console provides a set of useful functions to work on the terminal
|
||||
*
|
||||
* @author Antonio Ramirez <amigo.cobos@gmail.com>
|
||||
* @package Yiinitializr.Cli
|
||||
* @since 1.0
|
||||
*/
|
||||
class Console
|
||||
{
|
||||
/**
|
||||
* Text foreground colors.
|
||||
*/
|
||||
protected static $FGCOLOR = array(
|
||||
'black' => 30,
|
||||
'red' => 31,
|
||||
'green' => 32,
|
||||
'brown' => 33,
|
||||
'blue' => 34,
|
||||
'purple' => 35,
|
||||
'cyan' => 36,
|
||||
'grey' => 37,
|
||||
'yellow' => 33,
|
||||
);
|
||||
|
||||
/**
|
||||
* Text styling.
|
||||
*/
|
||||
protected static $STYLE = array(
|
||||
'normal' => 0,
|
||||
'bold' => 1,
|
||||
'light' => 1,
|
||||
'underscore' => 4,
|
||||
'underline' => 4,
|
||||
'blink' => 5,
|
||||
'inverse' => 6,
|
||||
'hidden' => 8,
|
||||
'concealed' => 8,
|
||||
);
|
||||
|
||||
/**
|
||||
* Text background color.
|
||||
*/
|
||||
protected static $BGCOLOR = array(
|
||||
'black' => 40,
|
||||
'red' => 41,
|
||||
'green' => 42,
|
||||
'brown' => 43,
|
||||
'yellow' => 43,
|
||||
'blue' => 44,
|
||||
'purple' => 45,
|
||||
'cyan' => 46,
|
||||
'grey' => 47,
|
||||
);
|
||||
|
||||
/**
|
||||
* Color specifier conversion table. Taken from PEAR's Console_Color.
|
||||
*/
|
||||
protected static $CONVERSIONS = array(
|
||||
'%y' => array('yellow', null, null),
|
||||
'%g' => array('green', null, null),
|
||||
'%b' => array('blue', null, null),
|
||||
'%r' => array('red', null, null),
|
||||
'%p' => array('purple', null, null),
|
||||
'%m' => array('purple', null, null),
|
||||
'%c' => array('cyan', null, null),
|
||||
'%w' => array('grey', null, null),
|
||||
'%k' => array('black', null, null),
|
||||
'%n' => array('reset', null, null),
|
||||
'%Y' => array('yellow', 'light', null),
|
||||
'%G' => array('green', 'light', null),
|
||||
'%B' => array('blue', 'light', null),
|
||||
'%R' => array('red', 'light', null),
|
||||
'%P' => array('purple', 'light', null),
|
||||
'%M' => array('purple', 'light', null),
|
||||
'%C' => array('cyan', 'light', null),
|
||||
'%W' => array('grey', 'light', null),
|
||||
'%K' => array('black', 'light', null),
|
||||
'%N' => array('reset', 'light', null),
|
||||
'%3' => array(null, null, 'yellow'),
|
||||
'%2' => array(null, null, 'green'),
|
||||
'%4' => array(null, null, 'blue'),
|
||||
'%1' => array(null, null, 'red'),
|
||||
'%5' => array(null, null, 'purple'),
|
||||
'%6' => array(null, null, 'cyan'),
|
||||
'%7' => array(null, null, 'grey'),
|
||||
'%0' => array(null, null, 'black'),
|
||||
'%F' => array(null, 'blink', null),
|
||||
'%U' => array(null, 'underline', null),
|
||||
'%8' => array(null, 'inverse', null),
|
||||
'%9' => array(null, 'bold', null),
|
||||
'%_' => array(null, 'bold', null),
|
||||
);
|
||||
|
||||
/**
|
||||
* Create ANSI-control codes for text foreground and background colors, and
|
||||
* styling.
|
||||
*
|
||||
* @param string $fgcolor Text foreground color
|
||||
* @param string $style Text style
|
||||
* @param string $bgcolor Text background color
|
||||
*
|
||||
* @return string ANSI-control code
|
||||
*/
|
||||
public static function color($fgcolor, $style, $bgcolor)
|
||||
{
|
||||
$code = array();
|
||||
if ($fgcolor == 'reset') {
|
||||
return "\033[0m";
|
||||
}
|
||||
if (isset(static::$FGCOLOR[$fgcolor])) {
|
||||
$code[] = static::$FGCOLOR[$fgcolor];
|
||||
}
|
||||
if (isset(static::$STYLE[$style])) {
|
||||
$code[] = static::$STYLE[$style];
|
||||
}
|
||||
if (isset(static::$BGCOLOR[$bgcolor])) {
|
||||
$code[] = static::$BGCOLOR[$bgcolor];
|
||||
}
|
||||
if (empty($code)) {
|
||||
$code[] = 0;
|
||||
}
|
||||
return "\033[" . implode(';', $code) . 'm';
|
||||
}
|
||||
|
||||
/**
|
||||
* aken from PEAR's Console_Color:
|
||||
*
|
||||
* Converts colorcodes in the format %y (for yellow) into ansi-control
|
||||
* codes. The conversion table is: ('bold' meaning 'light' on some
|
||||
* terminals). It's almost the same conversion table irssi uses.
|
||||
* <pre>
|
||||
* text text background
|
||||
* ------------------------------------------------
|
||||
* %k %K %0 black dark grey black
|
||||
* %r %R %1 red bold red red
|
||||
* %g %G %2 green bold green green
|
||||
* %y %Y %3 yellow bold yellow yellow
|
||||
* %b %B %4 blue bold blue blue
|
||||
* %m %M %5 magenta bold magenta magenta
|
||||
* %p %P magenta (think: purple)
|
||||
* %c %C %6 cyan bold cyan cyan
|
||||
* %w %W %7 white bold white white
|
||||
*
|
||||
* %F Blinking, Flashing
|
||||
* %U Underline
|
||||
* %8 Reverse
|
||||
* %_,%9 Bold
|
||||
*
|
||||
* %n Resets the color
|
||||
* %% A single %
|
||||
* </pre>
|
||||
* First param is the string to convert, second is an optional flag if
|
||||
* colors should be used. It defaults to true, if set to false, the
|
||||
* colorcodes will just be removed (And %% will be transformed into %)
|
||||
*
|
||||
* @param $text
|
||||
* @param bool $color
|
||||
* @return mixed
|
||||
*/
|
||||
public static function colorize($text, $color = true)
|
||||
{
|
||||
$text = str_replace('%%', '% ', $text);
|
||||
foreach (static::$CONVERSIONS as $key => $value) {
|
||||
list($fgcolor, $style, $bgcolor) = $value;
|
||||
$text = str_replace(
|
||||
$key,
|
||||
$color ? static::color($fgcolor, $style, $bgcolor) : '',
|
||||
$text
|
||||
);
|
||||
}
|
||||
return str_replace('% ', '%', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips a string from color specifiers.
|
||||
*
|
||||
* @param string $text String to strip
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function decolorize($text)
|
||||
{
|
||||
return static::colorize($text, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips a string of ansi-control codes.
|
||||
*
|
||||
* @param string $text String to strip
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function strip($text)
|
||||
{
|
||||
return preg_replace('/\033\[(\d+)(;\d+)*m/', '', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets input from STDIN and returns a string right-trimmed for EOLs.
|
||||
*
|
||||
* @param bool $raw If set to true, returns the raw string without trimming
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function stdin($raw = false)
|
||||
{
|
||||
return $raw ? fgets(STDIN) : rtrim(fgets(STDIN), PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints text to STDOUT.
|
||||
*
|
||||
* @param string $text
|
||||
* @param bool $raw
|
||||
*
|
||||
* @return int|false Number of bytes printed or false on error
|
||||
*/
|
||||
public static function stdout($text, $raw = false)
|
||||
{
|
||||
if ($raw) {
|
||||
return fwrite(STDOUT, $text);
|
||||
} elseif (extension_loaded('posix') && posix_isatty(STDOUT)) {
|
||||
return fwrite(STDOUT, static::colorize($text));
|
||||
} else {
|
||||
return fwrite(STDOUT, static::decolorize($text));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints text to STDERR.
|
||||
*
|
||||
* @param string $text
|
||||
* @param bool $raw
|
||||
*
|
||||
* @return int|false Number of bytes printed or false on error
|
||||
*/
|
||||
public static function stderr($text, $raw = false)
|
||||
{
|
||||
if ($raw) {
|
||||
return fwrite(STDERR, $text);
|
||||
} elseif (extension_loaded('posix') && posix_isatty(STDERR)) {
|
||||
return fwrite(STDERR, static::colorize($text));
|
||||
} else {
|
||||
return fwrite(STDERR, static::decolorize($text));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints text to STDERR appended with a PHP_EOL.
|
||||
*
|
||||
* @param string $text
|
||||
* @param bool $raw
|
||||
*
|
||||
* @return int|false Number of bytes printed or false on error
|
||||
*/
|
||||
public static function error($text = null, $raw = false)
|
||||
{
|
||||
return static::stderr($text . PHP_EOL, $raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the user for input. Ends when the user types a PHP_EOL. Optionally
|
||||
* provide a prompt.
|
||||
*
|
||||
* @param string $prompt String prompt (optional)
|
||||
*
|
||||
* @return string User input
|
||||
*/
|
||||
public static function input($prompt = null)
|
||||
{
|
||||
if (isset($prompt)) {
|
||||
static::stdout($prompt);
|
||||
}
|
||||
return static::stdin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints text to STDOUT appended with a PHP_EOL.
|
||||
*
|
||||
* @param string $text
|
||||
* @param bool $raw
|
||||
*
|
||||
* @return int|false Number of bytes printed or false on error
|
||||
*/
|
||||
public static function output($text = null, $raw = false)
|
||||
{
|
||||
return static::stdout($text . PHP_EOL, $raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user for input
|
||||
*
|
||||
* @param string $text Prompt string
|
||||
* @param array $options Set of options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function prompt($text, $options = array())
|
||||
{
|
||||
$options = $options + array(
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'pattern' => null,
|
||||
'validator' => null,
|
||||
'error' => 'Input unacceptable.',
|
||||
);
|
||||
|
||||
top:
|
||||
if ($options['default']) {
|
||||
$input = static::input("$text [" . $options['default'] . ']: ');
|
||||
} else {
|
||||
$input = static::input("$text: ");
|
||||
}
|
||||
|
||||
if (!strlen($input)) {
|
||||
if (isset($options['default'])) {
|
||||
$input = $options['default'];
|
||||
} elseif ($options['required']) {
|
||||
static::output($options['error']);
|
||||
goto top;
|
||||
}
|
||||
} elseif ($options['pattern'] && !preg_match($options['pattern'], $input)) {
|
||||
static::output($options['error']);
|
||||
goto top;
|
||||
} elseif ($options['validator'] &&
|
||||
!call_user_func_array($options['validator'], array($input, &$error))) {
|
||||
static::output(isset($error) ? $error : $options['error']);
|
||||
goto top;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the user for a simple yes/no confirmation.
|
||||
*
|
||||
* @param string $text Prompt string
|
||||
*
|
||||
* @return bool Either true or false
|
||||
*/
|
||||
public static function confirm($text)
|
||||
{
|
||||
top:
|
||||
$input = strtolower(static::input("$text [y/n]: "));
|
||||
if (!in_array($input, array('y', 'n'))) goto top;
|
||||
return $input === 'y' ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the user an option to choose from. Giving '?' as an input will show
|
||||
* a list of options to choose from and their explanations.
|
||||
*
|
||||
* @param string $text Prompt string
|
||||
* @param array $options Key-value array of options to choose from
|
||||
*
|
||||
* @return string An option character the user chose
|
||||
*/
|
||||
public static function select($text, $options = array())
|
||||
{
|
||||
top:
|
||||
static::stdout("$text [" . implode(',', array_keys($options)) . ",?]: ");
|
||||
$input = static::stdin();
|
||||
if ($input === '?') {
|
||||
foreach ($options as $key => $value) {
|
||||
echo " $key - $value\n";
|
||||
}
|
||||
echo " ? - Show help\n";
|
||||
goto top;
|
||||
} elseif (!in_array($input, array_keys($options))) goto top;
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Closure as another process in the background while showing a
|
||||
* status update. The status update can be an indefinite spinner or a string
|
||||
* periodically sent from the background process, depending on whether the
|
||||
* provided Closure object has a $socket parameter or not. Messaging to the
|
||||
* main process is done by socket_* functions. The return value is either
|
||||
* the return value of the background process, or false if the process fork
|
||||
* failed.
|
||||
*
|
||||
* @param callable $callable Closure object
|
||||
* @return bool|int
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function work(\Closure $callable)
|
||||
{
|
||||
if (!extension_loaded('pcntl')) {
|
||||
throw new \Exception('pcntl extension required');
|
||||
}
|
||||
|
||||
if (!extension_loaded('sockets')) {
|
||||
throw new \Exception('sockets extension required');
|
||||
}
|
||||
|
||||
$spinner = array('|', '/', '-', '\\');
|
||||
$i = 0; $l = count($spinner);
|
||||
$delay = 100000;
|
||||
|
||||
$func = new \ReflectionFunction($callable);
|
||||
|
||||
$socket = (bool)$func->getNumberOfParameters();
|
||||
|
||||
if ($socket) {
|
||||
$sockets = array();
|
||||
if (socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$pid = pcntl_fork();
|
||||
|
||||
if ($pid > 0) {
|
||||
$done = false;
|
||||
$retval = 0;
|
||||
pcntl_signal(SIGCHLD, function() use ($pid, &$done, &$retval) {
|
||||
$child_pid = pcntl_waitpid($pid, $status);
|
||||
if (pcntl_wifexited($status)) {
|
||||
$retval = pcntl_wexitstatus($status);
|
||||
}
|
||||
$done = true;
|
||||
});
|
||||
|
||||
if ($socket) {
|
||||
$text = '';
|
||||
while (!$done) {
|
||||
$r = array($sockets[1]);
|
||||
$w = null;
|
||||
$e = null;
|
||||
if ($status = socket_select($r, $w, $e, 0)) {
|
||||
$data = socket_read($sockets[1], 4096, PHP_NORMAL_READ);
|
||||
if ($data === false) {
|
||||
throw new \Exception(
|
||||
sprintf(
|
||||
'socket write error %s',
|
||||
socket_strerror(socket_last_error($sockets[1]))
|
||||
)
|
||||
);
|
||||
}
|
||||
echo str_repeat(chr(8), strlen($text));
|
||||
$text = rtrim($data, "\n");
|
||||
Console::stdout($text);
|
||||
} else {
|
||||
pcntl_signal_dispatch();
|
||||
}
|
||||
usleep($delay);
|
||||
}
|
||||
echo str_repeat(chr(8), strlen($text));
|
||||
socket_close($sockets[0]);
|
||||
socket_close($sockets[1]);
|
||||
} else {
|
||||
while (!$done) {
|
||||
pcntl_signal_dispatch();
|
||||
echo $spinner[$i];
|
||||
usleep($delay);
|
||||
echo chr(8);
|
||||
$i = $i === $l - 1 ? 0 : $i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $retval;
|
||||
} elseif ($pid === 0) {
|
||||
if ($socket) {
|
||||
call_user_func($callable, $sockets[0]);
|
||||
} else {
|
||||
call_user_func($callable);
|
||||
}
|
||||
exit;
|
||||
} else {
|
||||
// Unable to fork process.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user