You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
533 lines
12 KiB
533 lines
12 KiB
7 years ago
|
<?php
|
||
|
/**
|
||
|
* Part of the Fuel framework.
|
||
|
*
|
||
|
* @package Fuel
|
||
|
* @version 1.8
|
||
|
* @author Fuel Development Team
|
||
|
* @license MIT License
|
||
|
* @copyright 2010 - 2016 Fuel Development Team
|
||
|
* @link http://fuelphp.com
|
||
|
*/
|
||
|
|
||
|
namespace Fuel\Core;
|
||
|
|
||
|
/**
|
||
|
* Cli class
|
||
|
*
|
||
|
* Interact with the command line by accepting input options, parameters and output text
|
||
|
*
|
||
|
* @package Fuel
|
||
|
* @category Core
|
||
|
* @author Phil Sturgeon
|
||
|
* @link http://docs.fuelphp.com/classes/cli.html
|
||
|
*/
|
||
|
class Cli
|
||
|
{
|
||
|
public static $readline_support = false;
|
||
|
|
||
|
public static $wait_msg = 'Press any key to continue...';
|
||
|
|
||
|
public static $nocolor = false;
|
||
|
|
||
|
protected static $args = array();
|
||
|
|
||
|
protected static $foreground_colors = array(
|
||
|
'black' => '0;30',
|
||
|
'dark_gray' => '1;30',
|
||
|
'blue' => '0;34',
|
||
|
'dark_blue' => '1;34',
|
||
|
'light_blue' => '1;34',
|
||
|
'green' => '0;32',
|
||
|
'light_green' => '1;32',
|
||
|
'cyan' => '0;36',
|
||
|
'light_cyan' => '1;36',
|
||
|
'red' => '0;31',
|
||
|
'light_red' => '1;31',
|
||
|
'purple' => '0;35',
|
||
|
'light_purple' => '1;35',
|
||
|
'light_yellow' => '0;33',
|
||
|
'yellow' => '1;33',
|
||
|
'light_gray' => '0;37',
|
||
|
'white' => '1;37',
|
||
|
);
|
||
|
|
||
|
protected static $background_colors = array(
|
||
|
'black' => '40',
|
||
|
'red' => '41',
|
||
|
'green' => '42',
|
||
|
'yellow' => '43',
|
||
|
'blue' => '44',
|
||
|
'magenta' => '45',
|
||
|
'cyan' => '46',
|
||
|
'light_gray' => '47',
|
||
|
);
|
||
|
|
||
|
protected static $STDOUT;
|
||
|
protected static $STDERR;
|
||
|
|
||
|
/**
|
||
|
* Static constructor. Parses all the CLI params.
|
||
|
*/
|
||
|
public static function _init()
|
||
|
{
|
||
|
if ( ! \Fuel::$is_cli)
|
||
|
{
|
||
|
throw new \Exception('Cli class cannot be used outside of the command line.');
|
||
|
}
|
||
|
for ($i = 1; $i < $_SERVER['argc']; $i++)
|
||
|
{
|
||
|
$arg = explode('=', $_SERVER['argv'][$i]);
|
||
|
|
||
|
static::$args[$i] = $arg[0];
|
||
|
|
||
|
if (count($arg) > 1 || strncmp($arg[0], '-', 1) === 0)
|
||
|
{
|
||
|
static::$args[ltrim($arg[0], '-')] = isset($arg[1]) ? $arg[1] : true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Readline is an extension for PHP that makes interactive with PHP much more bash-like
|
||
|
// http://www.php.net/manual/en/readline.installation.php
|
||
|
static::$readline_support = extension_loaded('readline');
|
||
|
|
||
|
static::$STDERR = STDERR;
|
||
|
static::$STDOUT = STDOUT;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the option with the given name. You can also give the option
|
||
|
* number.
|
||
|
*
|
||
|
* Named options must be in the following formats:
|
||
|
* php index.php user -v --v -name=John --name=John
|
||
|
*
|
||
|
* @param string|int $name the name of the option (int if unnamed)
|
||
|
* @param mixed $default value to return if the option is not defined
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public static function option($name, $default = null)
|
||
|
{
|
||
|
if ( ! isset(static::$args[$name]))
|
||
|
{
|
||
|
return \Fuel::value($default);
|
||
|
}
|
||
|
return static::$args[$name];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Allows you to set a commandline option from code
|
||
|
*
|
||
|
* @param string|int $name the name of the option (int if unnamed)
|
||
|
* @param mixed|null $value value to set, or null to delete the option
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public static function set_option($name, $value = null)
|
||
|
{
|
||
|
if ($value === null)
|
||
|
{
|
||
|
if (isset(static::$args[$name]))
|
||
|
{
|
||
|
unset(static::$args[$name]);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
static::$args[$name] = $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get input from the shell, using readline or the standard STDIN
|
||
|
*
|
||
|
* Named options must be in the following formats:
|
||
|
* php index.php user -v --v -name=John --name=John
|
||
|
*
|
||
|
* @param string|int $prefix the name of the option (int if unnamed)
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function input($prefix = '')
|
||
|
{
|
||
|
if (static::$readline_support)
|
||
|
{
|
||
|
return readline($prefix);
|
||
|
}
|
||
|
|
||
|
echo $prefix;
|
||
|
return fgets(STDIN);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Asks the user for input. This can have either 1 or 2 arguments.
|
||
|
*
|
||
|
* Usage:
|
||
|
*
|
||
|
* // Waits for any key press
|
||
|
* CLI::prompt();
|
||
|
*
|
||
|
* // Takes any input
|
||
|
* $color = CLI::prompt('What is your favorite color?');
|
||
|
*
|
||
|
* // Takes any input, but offers default
|
||
|
* $color = CLI::prompt('What is your favourite color?', 'white');
|
||
|
*
|
||
|
* // Will only accept the options in the array
|
||
|
* $ready = CLI::prompt('Are you ready?', array('y','n'));
|
||
|
*
|
||
|
* @return string the user input
|
||
|
*/
|
||
|
public static function prompt()
|
||
|
{
|
||
|
$args = func_get_args();
|
||
|
|
||
|
$options = array();
|
||
|
$output = '';
|
||
|
$default = null;
|
||
|
|
||
|
// How many we got
|
||
|
$arg_count = count($args);
|
||
|
|
||
|
// Is the last argument a boolean? True means required
|
||
|
$required = end($args) === true;
|
||
|
|
||
|
// Reduce the argument count if required was passed, we don't care about that anymore
|
||
|
$required === true and --$arg_count;
|
||
|
|
||
|
// This method can take a few crazy combinations of arguments, so lets work it out
|
||
|
switch ($arg_count)
|
||
|
{
|
||
|
case 2:
|
||
|
|
||
|
// E.g: $ready = CLI::prompt('Are you ready?', array('y','n'));
|
||
|
if (is_array($args[1]))
|
||
|
{
|
||
|
list($output, $options)=$args;
|
||
|
}
|
||
|
|
||
|
// E.g: $color = CLI::prompt('What is your favourite color?', 'white');
|
||
|
elseif (is_string($args[1]))
|
||
|
{
|
||
|
list($output, $default)=$args;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
|
||
|
// No question (probably been asked already) so just show options
|
||
|
// E.g: $ready = CLI::prompt(array('y','n'));
|
||
|
if (is_array($args[0]))
|
||
|
{
|
||
|
$options = $args[0];
|
||
|
}
|
||
|
|
||
|
// Question without options
|
||
|
// E.g: $ready = CLI::prompt('What did you do today?');
|
||
|
elseif (is_string($args[0]))
|
||
|
{
|
||
|
$output = $args[0];
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// If a question has been asked with the read
|
||
|
if ($output !== '')
|
||
|
{
|
||
|
$extra_output = '';
|
||
|
|
||
|
if ($default !== null)
|
||
|
{
|
||
|
$extra_output = ' [ Default: "'.$default.'" ]';
|
||
|
}
|
||
|
|
||
|
elseif ($options !== array())
|
||
|
{
|
||
|
$extra_output = ' [ '.implode(', ', $options).' ]';
|
||
|
}
|
||
|
|
||
|
fwrite(static::$STDOUT, $output.$extra_output.': ');
|
||
|
}
|
||
|
|
||
|
// Read the input from keyboard.
|
||
|
$input = trim(static::input()) ?: $default;
|
||
|
|
||
|
// No input provided and we require one (default will stop this being called)
|
||
|
if (empty($input) and $required === true)
|
||
|
{
|
||
|
static::write('This is required.');
|
||
|
static::new_line();
|
||
|
|
||
|
$input = forward_static_call_array(array(__CLASS__, 'prompt'), $args);
|
||
|
}
|
||
|
|
||
|
// If options are provided and the choice is not in the array, tell them to try again
|
||
|
if ( ! empty($options) and ! in_array($input, $options))
|
||
|
{
|
||
|
static::write('This is not a valid option. Please try again.');
|
||
|
static::new_line();
|
||
|
|
||
|
$input = forward_static_call_array(array(__CLASS__, 'prompt'), $args);
|
||
|
}
|
||
|
|
||
|
return $input;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Outputs a string to the cli. If you send an array it will implode them
|
||
|
* with a line break.
|
||
|
*
|
||
|
* @param string|array $text the text to output, or array of lines
|
||
|
* @param string $foreground the foreground color
|
||
|
* @param string $background the foreground color
|
||
|
* @throws \FuelException
|
||
|
*/
|
||
|
public static function write($text = '', $foreground = null, $background = null)
|
||
|
{
|
||
|
if (is_array($text))
|
||
|
{
|
||
|
$text = implode(PHP_EOL, $text);
|
||
|
}
|
||
|
|
||
|
if ($foreground or $background)
|
||
|
{
|
||
|
$text = static::color($text, $foreground, $background);
|
||
|
}
|
||
|
|
||
|
fwrite(static::$STDOUT, $text.PHP_EOL);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Outputs an error to the CLI using STDERR instead of STDOUT
|
||
|
*
|
||
|
* @param string|array $text the text to output, or array of errors
|
||
|
* @param string $foreground the foreground color
|
||
|
* @param string $background the foreground color
|
||
|
* @throws \FuelException
|
||
|
*/
|
||
|
public static function error($text = '', $foreground = 'light_red', $background = null)
|
||
|
{
|
||
|
if (is_array($text))
|
||
|
{
|
||
|
$text = implode(PHP_EOL, $text);
|
||
|
}
|
||
|
|
||
|
if ($foreground OR $background)
|
||
|
{
|
||
|
$text = static::color($text, $foreground, $background);
|
||
|
}
|
||
|
|
||
|
fwrite(static::$STDERR, $text.PHP_EOL);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Beeps a certain number of times.
|
||
|
*
|
||
|
* @param int $num the number of times to beep
|
||
|
*/
|
||
|
public static function beep($num = 1)
|
||
|
{
|
||
|
echo str_repeat("\x07", $num);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Waits a certain number of seconds, optionally showing a wait message and
|
||
|
* waiting for a key press.
|
||
|
*
|
||
|
* @param int $seconds number of seconds
|
||
|
* @param bool $countdown show a countdown or not
|
||
|
*/
|
||
|
public static function wait($seconds = 0, $countdown = false)
|
||
|
{
|
||
|
if ($countdown === true)
|
||
|
{
|
||
|
$time = $seconds;
|
||
|
|
||
|
while ($time > 0)
|
||
|
{
|
||
|
fwrite(static::$STDOUT, $time.'... ');
|
||
|
sleep(1);
|
||
|
$time--;
|
||
|
}
|
||
|
static::write();
|
||
|
}
|
||
|
|
||
|
else
|
||
|
{
|
||
|
if ($seconds > 0)
|
||
|
{
|
||
|
sleep($seconds);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
static::write(static::$wait_msg);
|
||
|
static::input();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* if operating system === windows
|
||
|
*/
|
||
|
public static function is_windows()
|
||
|
{
|
||
|
return 'win' === strtolower(substr(php_uname("s"), 0, 3));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enter a number of empty lines
|
||
|
*
|
||
|
* @param integer Number of lines to output
|
||
|
* @return void
|
||
|
*/
|
||
|
public static function new_line($num = 1)
|
||
|
{
|
||
|
// Do it once or more, write with empty string gives us a new line
|
||
|
for($i = 0; $i < $num; $i++)
|
||
|
{
|
||
|
static::write();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clears the screen of output
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public static function clear_screen()
|
||
|
{
|
||
|
static::is_windows()
|
||
|
|
||
|
// Windows is a bit crap at this, but their terminal is tiny so shove this in
|
||
|
? static::new_line(40)
|
||
|
|
||
|
// Anything with a flair of Unix will handle these magic characters
|
||
|
: fwrite(static::$STDOUT, chr(27)."[H".chr(27)."[2J");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the given text with the correct color codes for a foreground and
|
||
|
* optionally a background color.
|
||
|
*
|
||
|
* @param string $text the text to color
|
||
|
* @param string $foreground the foreground color
|
||
|
* @param string $background the background color
|
||
|
* @param string $format other formatting to apply. Currently only 'underline' is understood
|
||
|
* @return string the color coded string
|
||
|
* @throws \FuelException
|
||
|
*/
|
||
|
public static function color($text, $foreground, $background = null, $format=null)
|
||
|
{
|
||
|
if (static::is_windows() and ! \Input::server('ANSICON'))
|
||
|
{
|
||
|
return $text;
|
||
|
}
|
||
|
|
||
|
if (static::$nocolor)
|
||
|
{
|
||
|
return $text;
|
||
|
}
|
||
|
|
||
|
if ( ! array_key_exists($foreground, static::$foreground_colors))
|
||
|
{
|
||
|
throw new \FuelException('Invalid CLI foreground color: '.$foreground);
|
||
|
}
|
||
|
|
||
|
if ( $background !== null and ! array_key_exists($background, static::$background_colors))
|
||
|
{
|
||
|
throw new \FuelException('Invalid CLI background color: '.$background);
|
||
|
}
|
||
|
|
||
|
$string = "\033[".static::$foreground_colors[$foreground]."m";
|
||
|
|
||
|
if ($background !== null)
|
||
|
{
|
||
|
$string .= "\033[".static::$background_colors[$background]."m";
|
||
|
}
|
||
|
|
||
|
if ($format === 'underline')
|
||
|
{
|
||
|
$string .= "\033[4m";
|
||
|
}
|
||
|
|
||
|
$string .= $text."\033[0m";
|
||
|
|
||
|
return $string;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Spawn Background Process
|
||
|
*
|
||
|
* Launches a background process (note, provides no security itself, $call must be sanitised prior to use)
|
||
|
* @param string $call the system call to make
|
||
|
* @param string $output
|
||
|
* @return void
|
||
|
* @author raccettura
|
||
|
* @link http://robert.accettura.com/blog/2006/09/14/asynchronous-processing-with-php/
|
||
|
*/
|
||
|
public static function spawn($call, $output = '/dev/null')
|
||
|
{
|
||
|
// Windows
|
||
|
if(static::is_windows())
|
||
|
{
|
||
|
pclose(popen('start /b '.$call, 'r'));
|
||
|
}
|
||
|
|
||
|
// Some sort of UNIX
|
||
|
else
|
||
|
{
|
||
|
pclose(popen($call.' > '.$output.' &', 'r'));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Redirect STDERR writes to this file or fh
|
||
|
*
|
||
|
* Call with no argument to retrieve the current filehandle.
|
||
|
*
|
||
|
* Is not smart about opening the file if it's a string. Existing files will be truncated.
|
||
|
*
|
||
|
* @param resource|string $fh Opened filehandle or string filename.
|
||
|
*
|
||
|
* @return resource
|
||
|
*/
|
||
|
public static function stderr($fh = null)
|
||
|
{
|
||
|
$orig = static::$STDERR;
|
||
|
|
||
|
if (! is_null($fh)) {
|
||
|
if (is_string($fh)) {
|
||
|
$fh = fopen($fh, "w");
|
||
|
}
|
||
|
static::$STDERR = $fh;
|
||
|
}
|
||
|
|
||
|
return $orig;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Redirect STDOUT writes to this file or fh
|
||
|
*
|
||
|
* Call with no argument to retrieve the current filehandle.
|
||
|
*
|
||
|
* Is not smart about opening the file if it's a string. Existing files will be truncated.
|
||
|
*
|
||
|
* @param resource|string|null $fh Opened filehandle or string filename.
|
||
|
*
|
||
|
* @return resource
|
||
|
*/
|
||
|
public static function stdout($fh = null)
|
||
|
{
|
||
|
$orig = static::$STDOUT;
|
||
|
|
||
|
if (! is_null($fh)) {
|
||
|
if (is_string($fh)) {
|
||
|
$fh = fopen($fh, "w");
|
||
|
}
|
||
|
static::$STDOUT = $fh;
|
||
|
}
|
||
|
|
||
|
return $orig;
|
||
|
}
|
||
|
}
|