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.
PlexShare/fuel/core/classes/cli.php

533 lines
12 KiB

<?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;
}
}