Upgrade to KH 3.3.0

This commit is contained in:
Deon George
2012-11-22 14:25:06 +11:00
parent e5e67a59bb
commit 5bd1841571
1455 changed files with 114353 additions and 9466 deletions

View File

@@ -0,0 +1,315 @@
<?php defined('SYSPATH') or die('No direct script access.');
class Kohana_Minion_CLI {
public static $wait_msg = 'Press any key to continue...';
protected static $foreground_colors = array(
'black' => '0;30',
'dark_gray' => '1;30',
'blue' => '0;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',
'brown' => '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',
);
/**
* Returns one or more command-line options. Options are specified using
* standard CLI syntax:
*
* php index.php --username=john.smith --password=secret --var="some value with spaces"
*
* // Get the values of "username" and "password"
* $auth = Minion_CLI::options('username', 'password');
*
* @param string $options,... option name
* @return array
*/
public static function options($options = NULL)
{
// Get all of the requested options
$options = func_get_args();
// Found option values
$values = array();
// Skip the first option, it is always the file executed
for ($i = 1; $i < $_SERVER['argc']; $i++)
{
if ( ! isset($_SERVER['argv'][$i]))
{
// No more args left
break;
}
// Get the option
$opt = $_SERVER['argv'][$i];
if (substr($opt, 0, 2) !== '--')
{
// This is a positional argument
$values[] = $opt;
continue;
}
// Remove the "--" prefix
$opt = substr($opt, 2);
if (strpos($opt, '='))
{
// Separate the name and value
list ($opt, $value) = explode('=', $opt, 2);
}
else
{
$value = NULL;
}
$values[$opt] = $value;
}
if ($options)
{
foreach ($values as $opt => $value)
{
if ( ! in_array($opt, $options))
{
// Set the given value
unset($values[$opt]);
}
}
}
return count($options) == 1 ? array_pop($values) : $values;
}
/**
* Reads input from the user. This can have either 1 or 2 arguments.
*
* Usage:
*
* // Waits for any key press
* Minion_CLI::read();
*
* // Takes any input
* $color = Minion_CLI::read('What is your favorite color?');
*
* // Will only accept the options in the array
* $ready = Minion_CLI::read('Are you ready?', array('y','n'));
*
* @param string $text text to show user before waiting for input
* @param array $options array of options the user is shown
* @return string the user input
*/
public static function read($text = '', array $options = NULL)
{
// If a question has been asked with the read
$options_output = '';
if ( ! empty($options))
{
$options_output = ' [ '.implode(', ', $options).' ]';
}
fwrite(STDOUT, $text.$options_output.': ');
// Read the input from keyboard.
$input = trim(fgets(STDIN));
// If options are provided and the choice is not in the array, tell them to try again
if ( ! empty($options) && ! in_array($input, $options))
{
Minion_CLI::write('This is not a valid option. Please try again.');
$input = Minion_CLI::read($text, $options);
}
// Read the input
return $input;
}
/**
* Experimental feature.
*
* Reads hidden input from the user
*
* Usage:
*
* $password = Minion_CLI::password('Enter your password');
*
* @author Mathew Davies.
* @return string
*/
public static function password($text = '')
{
$text .= ': ';
if (Kohana::$is_windows)
{
$vbscript = sys_get_temp_dir().'Minion_CLI_Password.vbs';
// Create temporary file
file_put_contents($vbscript, 'wscript.echo(InputBox("'.addslashes($text).'"))');
$password = shell_exec('cscript //nologo '.escapeshellarg($command));
// Remove temporary file.
unlink($vbscript);
}
else
{
$password = shell_exec('/usr/bin/env bash -c \'read -s -p "'.escapeshellcmd($text).'" var && echo $var\'');
}
Minion_CLI::write();
return trim($password);
}
/**
* 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
*/
public static function write($text = '')
{
if (is_array($text))
{
foreach ($text as $line)
{
Minion_CLI::write($line);
}
}
else
{
fwrite(STDOUT, $text.PHP_EOL);
}
}
/**
* Outputs a replacable line to the cli. You can continue replacing the
* line until `TRUE` is passed as the second parameter in order to indicate
* you are done modifying the line.
*
* // Sample progress indicator
* Minion_CLI::write_replace('0%');
* Minion_CLI::write_replace('25%');
* Minion_CLI::write_replace('50%');
* Minion_CLI::write_replace('75%');
* // Done writing this line
* Minion_CLI::write_replace('100%', TRUE);
*
* @param string $text the text to output
* @param boolean $end_line whether the line is done being replaced
*/
public static function write_replace($text = '', $end_line = FALSE)
{
// Append a newline if $end_line is TRUE
$text = $end_line ? $text.PHP_EOL : $text;
fwrite(STDOUT, "\r\033[K".$text);
}
/**
* Waits a certain number of seconds, optionally showing a wait message and
* waiting for a key press.
*
* @author Fuel Development Team
* @license MIT License
* @copyright 2010 - 2011 Fuel Development Team
* @link http://fuelphp.com
* @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(STDOUT, $time.'... ');
sleep(1);
$time--;
}
Minion_CLI::write();
}
else
{
if ($seconds > 0)
{
sleep($seconds);
}
else
{
Minion_CLI::write(Minion_CLI::$wait_msg);
Minion_CLI::read();
}
}
}
/**
* Returns the given text with the correct color codes for a foreground and
* optionally a background color.
*
* @author Fuel Development Team
* @license MIT License
* @copyright 2010 - 2011 Fuel Development Team
* @link http://fuelphp.com
* @param string $text the text to color
* @param atring $foreground the foreground color
* @param string $background the background color
* @return string the color coded string
*/
public static function color($text, $foreground, $background = null)
{
if (Kohana::$is_windows)
{
return $text;
}
if (!array_key_exists($foreground, Minion_CLI::$foreground_colors))
{
throw new Kohana_Exception('Invalid CLI foreground color: '.$foreground);
}
if ($background !== null and !array_key_exists($background, Minion_CLI::$background_colors))
{
throw new Kohana_Exception('Invalid CLI background color: '.$background);
}
$string = "\033[".Minion_CLI::$foreground_colors[$foreground]."m";
if ($background !== null)
{
$string .= "\033[".Minion_CLI::$background_colors[$background]."m";
}
$string .= $text."\033[0m";
return $string;
}
}

View File

@@ -0,0 +1,65 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Minipn exception
*
* @package Kohana
* @category Minion
* @author Kohana Team
* @copyright (c) 2009-2011 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Minion_Exception extends Kohana_Exception {
/**
* Inline exception handler, displays the error message, source of the
* exception, and the stack trace of the error.
*
* Should this display a stack trace? It's useful.
*
* Should this still log? Maybe not as useful since we'll see the error on the screen.
*
* @uses Kohana_Exception::text
* @param Exception $e
* @return boolean
*/
public static function handler(Exception $e)
{
try
{
if ($e instanceof Minion_Exception)
{
echo $e->format_for_cli();
}
else
{
echo Kohana_Exception::text($e);
}
$exit_code = $e->getCode();
// Never exit "0" after an exception.
if ($exit_code == 0)
{
$exit_code = 1;
}
exit($exit_code);
}
catch (Exception $e)
{
// Clean the output buffer if one exists
ob_get_level() and ob_clean();
// Display the exception text
echo Kohana_Exception::text($e), "\n";
// Exit with an error status
exit(1);
}
}
public function format_for_cli()
{
//@todo Log Kohana Bug, $e not defined.
return Kohana_Exception::text($e);
}
}

View File

@@ -0,0 +1,18 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Invalid Task Exception
*
* @package Kohana
* @category Minion
* @author Kohana Team
* @copyright (c) 2009-2011 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Minion_Exception_InvalidTask extends Minion_Exception {
public function format_for_cli()
{
return 'ERROR: '. $this->getMessage().PHP_EOL;
}
}

View File

@@ -0,0 +1,364 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Interface that all minion tasks must implement
*
* @package Kohana
* @category Minion
* @author Kohana Team
* @copyright (c) 2009-2011 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_Minion_Task {
/**
* The separator used to separate different levels of tasks
* @var string
*/
public static $task_separator = ':';
/**
* Converts a task (e.g. db:migrate to a class name)
*
* @param string Task name
* @return string Class name
*/
public static function convert_task_to_class_name($task)
{
$task = trim($task);
if (empty($task))
return '';
return 'Task_'.implode('_', array_map('ucfirst', explode(Minion_Task::$task_separator, $task)));
}
/**
* Gets the task name of a task class / task object
*
* @param string|Minion_Task The task class / object
* @return string The task name
*/
public static function convert_class_to_task($class)
{
if (is_object($class))
{
$class = get_class($class);
}
return strtolower(str_replace('_', Minion_Task::$task_separator, substr($class, 5)));
}
/**
* Factory for loading minion tasks
*
* @param array An array of command line options. It should contain the 'task' key
* @throws Minion_Exception_InvalidTask
* @return Minion_Task The Minion task
*/
public static function factory($options)
{
if (($task = Arr::get($options, 'task')) !== NULL)
{
unset($options['task']);
}
else if (($task = Arr::get($options, 0)) !== NULL)
{
// The first positional argument (aka 0) may be the task name
unset($options[0]);
}
else
{
// If we didn't get a valid task, generate the help
$task = 'help';
}
$class = Minion_Task::convert_task_to_class_name($task);
if ( ! class_exists($class))
{
throw new Minion_Exception_InvalidTask(
"Task ':task' is not a valid minion task",
array(':task' => $class)
);
}
$class = new $class;
if ( ! $class instanceof Minion_Task)
{
throw new Minion_Exception_InvalidTask(
"Task ':task' is not a valid minion task",
array(':task' => $class)
);
}
$class->set_options($options);
// Show the help page for this task if requested
if (array_key_exists('help', $options))
{
$class->_method = '_help';
}
return $class;
}
/**
* The list of options this task accepts and their default values.
*
* protected $_options = array(
* 'limit' => 4,
* 'table' => NULL,
* );
*
* @var array
*/
protected $_options = array();
/**
* Populated with the accepted options for this task.
* This array is automatically populated based on $_options.
*
* @var array
*/
protected $_accepted_options = array();
protected $_method = '_execute';
protected function __construct()
{
// Populate $_accepted_options based on keys from $_options
$this->_accepted_options = array_keys($this->_options);
}
/**
* The file that get's passes to Validation::errors() when validation fails
* @var string|NULL
*/
protected $_errors_file = 'validation';
/**
* Gets the task name for the task
*
* @return string
*/
public function __toString()
{
static $task_name = NULL;
if ($task_name === NULL)
{
$task_name = Minion_Task::convert_class_to_task($this);
}
return $task_name;
}
/**
* Sets options for this task
*
* $param array the array of options to set
* @return this
*/
public function set_options(array $options)
{
foreach ($options as $key => $value)
{
$this->_options[$key] = $value;
}
return $this;
}
/**
* Get the options that were passed into this task with their defaults
*
* @return array
*/
public function get_options()
{
return (array) $this->_options;
}
/**
* Get a set of options that this task can accept
*
* @return array
*/
public function get_accepted_options()
{
return (array) $this->_accepted_options;
}
/**
* Adds any validation rules/labels for validating _options
*
* public function build_validation(Validation $validation)
* {
* return parent::build_validation($validation)
* ->rule('paramname', 'not_empty'); // Require this param
* }
*
* @param Validation the validation object to add rules to
*
* @return Validation
*/
public function build_validation(Validation $validation)
{
// Add a rule to each key making sure it's in the task
foreach ($validation->as_array() as $key => $value)
{
$validation->rule($key, array($this, 'valid_option'), array(':validation', ':field'));
}
return $validation;
}
/**
* Returns $_errors_file
*
* @return string
*/
public function get_errors_file()
{
return $this->_errors_file;
}
/**
* Execute the task with the specified set of options
*
* @return null
*/
public function execute()
{
$options = $this->get_options();
// Validate $options
$validation = Validation::factory($options);
$validation = $this->build_validation($validation);
if ( $this->_method != '_help' AND ! $validation->check())
{
echo View::factory('minion/error/validation')
->set('task', Minion_Task::convert_class_to_task($this))
->set('errors', $validation->errors($this->get_errors_file()));
}
else
{
// Finally, run the task
$method = $this->_method;
echo $this->{$method}($options);
}
}
abstract protected function _execute(array $params);
/**
* Outputs help for this task
*
* @return null
*/
protected function _help(array $params)
{
$tasks = $this->_compile_task_list(Kohana::list_files('classes/task'));
$inspector = new ReflectionClass($this);
list($description, $tags) = $this->_parse_doccomment($inspector->getDocComment());
$view = View::factory('minion/help/task')
->set('description', $description)
->set('tags', (array) $tags)
->set('task', Minion_Task::convert_class_to_task($this));
echo $view;
}
public function valid_option(Validation $validation, $option)
{
if ( ! in_array($option, $this->_accepted_options))
{
$validation->error($option, 'minion_option');
}
}
/**
* Parses a doccomment, extracting both the comment and any tags associated
*
* Based on the code in Kodoc::parse()
*
* @param string The comment to parse
* @return array First element is the comment, second is an array of tags
*/
protected function _parse_doccomment($comment)
{
// Normalize all new lines to \n
$comment = str_replace(array("\r\n", "\n"), "\n", $comment);
// Remove the phpdoc open/close tags and split
$comment = array_slice(explode("\n", $comment), 1, -1);
// Tag content
$tags = array();
foreach ($comment as $i => $line)
{
// Remove all leading whitespace
$line = preg_replace('/^\s*\* ?/m', '', $line);
// Search this line for a tag
if (preg_match('/^@(\S+)(?:\s*(.+))?$/', $line, $matches))
{
// This is a tag line
unset($comment[$i]);
$name = $matches[1];
$text = isset($matches[2]) ? $matches[2] : '';
$tags[$name] = $text;
}
else
{
$comment[$i] = (string) $line;
}
}
$comment = trim(implode("\n", $comment));
return array($comment, $tags);
}
/**
* Compiles a list of available tasks from a directory structure
*
* @param array Directory structure of tasks
* @param string prefix
* @return array Compiled tasks
*/
protected function _compile_task_list(array $files, $prefix = '')
{
$output = array();
foreach ($files as $file => $path)
{
$file = substr($file, strrpos($file, DIRECTORY_SEPARATOR) + 1);
if (is_array($path) AND count($path))
{
$task = $this->_compile_task_list($path, $prefix.$file.Minion_Task::$task_separator);
if ($task)
{
$output = array_merge($output, $task);
}
}
else
{
$output[] = strtolower($prefix.substr($file, 0, -strlen(EXT)));
}
}
return $output;
}
}