update Piwik to version 2.16 (fixes #91)

This commit is contained in:
oliver 2016-04-10 18:55:57 +02:00
commit d885a4baa9
5833 changed files with 418860 additions and 226988 deletions

View file

@ -1,3 +0,0 @@
vendor/
composer.lock
phpunit.xml

View file

@ -13,6 +13,9 @@ namespace Symfony\Component\Console;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Helper\DebugFormatterHelper;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
@ -67,6 +70,7 @@ class Application
private $helperSet;
private $dispatcher;
private $terminalDimensions;
private $defaultCommand;
/**
* Constructor.
@ -80,6 +84,7 @@ class Application
{
$this->name = $name;
$this->version = $version;
$this->defaultCommand = 'list';
$this->helperSet = $this->getDefaultHelperSet();
$this->definition = $this->getDefaultInputDefinition();
@ -99,7 +104,7 @@ class Application
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
* @return integer 0 if everything went fine, or an error code
* @return int 0 if everything went fine, or an error code
*
* @throws \Exception When doRun returns Exception
*
@ -145,9 +150,8 @@ class Application
if ($exitCode > 255) {
$exitCode = 255;
}
// @codeCoverageIgnoreStart
exit($exitCode);
// @codeCoverageIgnoreEnd
}
return $exitCode;
@ -159,7 +163,7 @@ class Application
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
* @return integer 0 if everything went fine, or an error code
* @return int 0 if everything went fine, or an error code
*/
public function doRun(InputInterface $input, OutputInterface $output)
{
@ -180,8 +184,8 @@ class Application
}
if (!$name) {
$name = 'list';
$input = new ArrayInput(array('command' => 'list'));
$name = $this->defaultCommand;
$input = new ArrayInput(array('command' => $this->defaultCommand));
}
// the command name MUST be the first element of the input
@ -219,7 +223,7 @@ class Application
}
/**
* Set an input definition set to be used with this application
* Set an input definition set to be used with this application.
*
* @param InputDefinition $definition The input definition
*
@ -247,48 +251,31 @@ class Application
*/
public function getHelp()
{
$messages = array(
$this->getLongVersion(),
'',
'<comment>Usage:</comment>',
' [options] command [arguments]',
'',
'<comment>Options:</comment>',
);
foreach ($this->getDefinition()->getOptions() as $option) {
$messages[] = sprintf(' %-29s %s %s',
'<info>--'.$option->getName().'</info>',
$option->getShortcut() ? '<info>-'.$option->getShortcut().'</info>' : ' ',
$option->getDescription()
);
}
return implode(PHP_EOL, $messages);
return $this->getLongVersion();
}
/**
* Sets whether to catch exceptions or not during commands execution.
*
* @param Boolean $boolean Whether to catch exceptions or not during commands execution
* @param bool $boolean Whether to catch exceptions or not during commands execution
*
* @api
*/
public function setCatchExceptions($boolean)
{
$this->catchExceptions = (Boolean) $boolean;
$this->catchExceptions = (bool) $boolean;
}
/**
* Sets whether to automatically exit after a command execution or not.
*
* @param Boolean $boolean Whether to automatically exit after a command execution or not
* @param bool $boolean Whether to automatically exit after a command execution or not
*
* @api
*/
public function setAutoExit($boolean)
{
$this->autoExit = (Boolean) $boolean;
$this->autoExit = (bool) $boolean;
}
/**
@ -453,7 +440,7 @@ class Application
*
* @param string $name The command name or alias
*
* @return Boolean true if the command exists, false otherwise
* @return bool true if the command exists, false otherwise
*
* @api
*/
@ -473,10 +460,10 @@ class Application
{
$namespaces = array();
foreach ($this->commands as $command) {
$namespaces[] = $this->extractNamespace($command->getName());
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
foreach ($command->getAliases() as $alias) {
$namespaces[] = $this->extractNamespace($alias);
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
}
}
@ -501,7 +488,7 @@ class Application
if (empty($namespaces)) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces, array())) {
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
if (1 == count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
@ -550,7 +537,7 @@ class Application
$message = sprintf('Command "%s" is not defined.', $name);
if ($alternatives = $this->findAlternatives($name, $allCommands, array())) {
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
if (1 == count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
@ -632,8 +619,8 @@ class Application
/**
* Returns a text representation of the Application.
*
* @param string $namespace An optional namespace name
* @param boolean $raw Whether to return raw command list
* @param string $namespace An optional namespace name
* @param bool $raw Whether to return raw command list
*
* @return string A string representing the Application
*
@ -651,8 +638,8 @@ class Application
/**
* Returns an XML representation of the Application.
*
* @param string $namespace An optional namespace name
* @param Boolean $asDom Whether to return a DOM or an XML string
* @param string $namespace An optional namespace name
* @param bool $asDom Whether to return a DOM or an XML string
*
* @return string|\DOMDocument An XML string representing the Application
*
@ -675,34 +662,27 @@ class Application
/**
* Renders a caught exception.
*
* @param \Exception $e An exception instance
* @param \Exception $e An exception instance
* @param OutputInterface $output An OutputInterface instance
*/
public function renderException($e, $output)
{
$strlen = function ($string) {
if (!function_exists('mb_strlen')) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
return strlen($string);
}
return mb_strlen($string, $encoding);
};
do {
$title = sprintf(' [%s] ', get_class($e));
$len = $strlen($title);
$len = $this->stringWidth($title);
$width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
// HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327
$width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : (defined('HHVM_VERSION') ? 1 << 31 : PHP_INT_MAX);
if (defined('HHVM_VERSION') && $width > 1 << 31) {
$width = 1 << 31;
}
$formatter = $output->getFormatter();
$lines = array();
foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
foreach (str_split($line, $width - 4) as $line) {
foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
// pre-format lines to get the right string length
$lineLength = $strlen(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
$lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
$lines[] = array($line, $lineLength);
$len = max($lineLength, $len);
@ -711,7 +691,7 @@ class Application
$messages = array('', '');
$messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $strlen($title)))));
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
foreach ($lines as $line) {
$messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
}
@ -728,12 +708,12 @@ class Application
$trace = $e->getTrace();
array_unshift($trace, array(
'function' => '',
'file' => $e->getFile() != null ? $e->getFile() : 'n/a',
'line' => $e->getLine() != null ? $e->getLine() : 'n/a',
'args' => array(),
'file' => $e->getFile() !== null ? $e->getFile() : 'n/a',
'line' => $e->getLine() !== null ? $e->getLine() : 'n/a',
'args' => array(),
));
for ($i = 0, $count = count($trace); $i < $count; $i++) {
for ($i = 0, $count = count($trace); $i < $count; ++$i) {
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
$function = $trace[$i]['function'];
@ -743,20 +723,20 @@ class Application
$output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line));
}
$output->writeln("");
$output->writeln("");
$output->writeln('');
$output->writeln('');
}
} while ($e = $e->getPrevious());
if (null !== $this->runningCommand) {
$output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())));
$output->writeln("");
$output->writeln("");
$output->writeln('');
$output->writeln('');
}
}
/**
* Tries to figure out the terminal width in which this application runs
* Tries to figure out the terminal width in which this application runs.
*
* @return int|null
*/
@ -768,7 +748,7 @@ class Application
}
/**
* Tries to figure out the terminal height in which this application runs
* Tries to figure out the terminal height in which this application runs.
*
* @return int|null
*/
@ -780,7 +760,7 @@ class Application
}
/**
* Tries to figure out the terminal dimensions based on the current environment
* Tries to figure out the terminal dimensions based on the current environment.
*
* @return array Array containing width and height
*/
@ -790,7 +770,7 @@ class Application
return $this->terminalDimensions;
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if ('\\' === DIRECTORY_SEPARATOR) {
// extract [w, H] from "wxh (WxH)"
if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
return array((int) $matches[1], (int) $matches[2]);
@ -820,8 +800,8 @@ class Application
*
* Can be useful to force terminal dimensions for functional tests.
*
* @param integer $width The width
* @param integer $height The height
* @param int $width The width
* @param int $height The height
*
* @return Application The current application
*/
@ -848,9 +828,9 @@ class Application
if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) {
$input->setInteractive(false);
} elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) {
$inputStream = $this->getHelperSet()->get('dialog')->getInputStream();
if (!@posix_isatty($inputStream)) {
} elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) {
$inputStream = $this->getHelperSet()->get('question')->getInputStream();
if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
$input->setInteractive(false);
}
}
@ -878,7 +858,9 @@ class Application
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
* @return integer 0 if everything went fine, or an error code
* @return int 0 if everything went fine, or an error code
*
* @throws \Exception when the command being run threw an exception
*/
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
@ -895,16 +877,20 @@ class Application
$event = new ConsoleCommandEvent($command, $input, $output);
$this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
try {
$exitCode = $command->run($input, $output);
} catch (\Exception $e) {
$event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
$this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
if ($event->commandShouldRun()) {
try {
$exitCode = $command->run($input, $output);
} catch (\Exception $e) {
$event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
$this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
$event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode());
$this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
$event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode());
$this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
throw $event->getException();
throw $event->getException();
}
} else {
$exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
}
$event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
@ -935,13 +921,13 @@ class Application
return new InputDefinition(array(
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'),
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'),
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'),
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'),
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
));
}
@ -967,11 +953,14 @@ class Application
new DialogHelper(),
new ProgressHelper(),
new TableHelper(),
new DebugFormatterHelper(),
new ProcessHelper(),
new QuestionHelper(),
));
}
/**
* Runs and parses stty -a if it's available, suppressing any error output
* Runs and parses stty -a if it's available, suppressing any error output.
*
* @return string
*/
@ -994,7 +983,7 @@ class Application
}
/**
* Runs and parses mode CON if it's available, suppressing any error output
* Runs and parses mode CON if it's available, suppressing any error output.
*
* @return string <width>x<height> or null if it could not be parsed
*/
@ -1050,10 +1039,10 @@ class Application
/**
* Finds alternative of $name among $collection,
* if nothing is found in $collection, try in $abbrevs
* if nothing is found in $collection, try in $abbrevs.
*
* @param string $name The string
* @param array|\Traversable $collection The collection
* @param string $name The string
* @param array|\Traversable $collection The collection
*
* @return array A sorted array of similar string
*/
@ -1078,7 +1067,7 @@ class Application
}
$lev = levenshtein($subname, $parts[$i]);
if ($lev <= strlen($subname) / 3 || false !== strpos($parts[$i], $subname)) {
if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
} elseif ($exists) {
$alternatives[$collectionName] += $threshold;
@ -1093,9 +1082,92 @@ class Application
}
}
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2*$threshold; });
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
asort($alternatives);
return array_keys($alternatives);
}
/**
* Sets the default Command name.
*
* @param string $commandName The Command name
*/
public function setDefaultCommand($commandName)
{
$this->defaultCommand = $commandName;
}
private function stringWidth($string)
{
if (!function_exists('mb_strwidth')) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
return strlen($string);
}
return mb_strwidth($string, $encoding);
}
private function splitStringByWidth($string, $width)
{
// str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
// additionally, array_slice() is not enough as some character has doubled width.
// we need a function to split string not by character count but by string width
if (!function_exists('mb_strwidth')) {
return str_split($string, $width);
}
if (false === $encoding = mb_detect_encoding($string)) {
return str_split($string, $width);
}
$utf8String = mb_convert_encoding($string, 'utf8', $encoding);
$lines = array();
$line = '';
foreach (preg_split('//u', $utf8String) as $char) {
// test if $char could be appended to current line
if (mb_strwidth($line.$char, 'utf8') <= $width) {
$line .= $char;
continue;
}
// if not, push current line to array and make new line
$lines[] = str_pad($line, $width);
$line = $char;
}
if ('' !== $line) {
$lines[] = count($lines) ? str_pad($line, $width) : $line;
}
mb_convert_variables($encoding, 'utf8', $lines);
return $lines;
}
/**
* Returns all namespaces of the command name.
*
* @param string $name The full name of the command
*
* @return array The namespaces of the command
*/
private function extractAllNamespaces($name)
{
// -1 as third argument is needed to skip the command short name when exploding
$parts = explode(':', $name, -1);
$namespaces = array();
foreach ($parts as $part) {
if (count($namespaces)) {
$namespaces[] = end($namespaces).':'.$part;
} else {
$namespaces[] = $part;
}
}
return $namespaces;
}
}

View file

@ -1,6 +1,23 @@
CHANGELOG
=========
2.6.0
-----
* added a Process helper
* added a DebugFormatter helper
2.5.0
-----
* deprecated the dialog helper (use the question helper instead)
* deprecated TableHelper in favor of Table
* deprecated ProgressHelper in favor of ProgressBar
* added ConsoleLogger
* added a question helper
* added a way to set the process name of a command
* added a way to set a default command instead of `ListCommand`
2.4.0
-----

View file

@ -33,6 +33,7 @@ class Command
{
private $application;
private $name;
private $processTitle;
private $aliases = array();
private $definition;
private $help;
@ -64,7 +65,7 @@ class Command
$this->configure();
if (!$this->name) {
throw new \LogicException('The command name cannot be empty.');
throw new \LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
}
}
@ -128,12 +129,12 @@ class Command
}
/**
* Checks whether the command is enabled or not in the current environment
* Checks whether the command is enabled or not in the current environment.
*
* Override this to check for x or y and return false if the command can not
* run properly under the current conditions.
*
* @return Boolean
* @return bool
*/
public function isEnabled()
{
@ -158,10 +159,11 @@ class Command
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return null|integer null or 0 if everything went fine, or an error code
* @return null|int null or 0 if everything went fine, or an error code
*
* @throws \LogicException When this abstract method is not implemented
* @see setCode()
*
* @see setCode()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@ -171,6 +173,10 @@ class Command
/**
* Interacts with the user.
*
* This method is executed before the InputDefinition is validated.
* This means that this is the only place where the command can
* interactively ask for values of missing required arguments.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
@ -201,7 +207,7 @@ class Command
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return integer The command exit code
* @return int The command exit code
*
* @throws \Exception
*
@ -229,6 +235,16 @@ class Command
$this->initialize($input, $output);
if (null !== $this->processTitle) {
if (function_exists('cli_set_process_title')) {
cli_set_process_title($this->processTitle);
} elseif (function_exists('setproctitle')) {
setproctitle($this->processTitle);
} elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
$output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
}
}
if ($input->isInteractive()) {
$this->interact($input, $output);
}
@ -276,7 +292,7 @@ class Command
*
* This method is not part of public API and should not be used directly.
*
* @param Boolean $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
* @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
*/
public function mergeApplicationDefinition($mergeArgs = true)
{
@ -350,10 +366,10 @@ class Command
/**
* Adds an argument.
*
* @param string $name The argument name
* @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
* @param string $name The argument name
* @param int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
*
* @return Command The current instance
*
@ -369,11 +385,11 @@ class Command
/**
* Adds an option.
*
* @param string $name The option name
* @param string $shortcut The shortcut (can be null)
* @param integer $mode The option mode: One of the InputOption::VALUE_* constants
* @param string $description A description text
* @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE)
* @param string $name The option name
* @param string $shortcut The shortcut (can be null)
* @param int $mode The option mode: One of the InputOption::VALUE_* constants
* @param string $description A description text
* @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE)
*
* @return Command The current instance
*
@ -411,6 +427,25 @@ class Command
return $this;
}
/**
* Sets the process title of the command.
*
* This feature should be used only when creating a long process command,
* like a daemon.
*
* PHP 5.5+ or the proctitle PECL library is required
*
* @param string $title The process title
*
* @return Command The current instance
*/
public function setProcessTitle($title)
{
$this->processTitle = $title;
return $this;
}
/**
* Returns the command name.
*
@ -483,7 +518,7 @@ class Command
* Returns the processed help for the command replacing the %command.name% and
* %command.full_name% patterns with the real values dynamically.
*
* @return string The processed help for the command
* @return string The processed help for the command
*/
public function getProcessedHelp()
{
@ -491,11 +526,11 @@ class Command
$placeholders = array(
'%command.name%',
'%command.full_name%'
'%command.full_name%',
);
$replacements = array(
$name,
$_SERVER['PHP_SELF'].' '.$name
$_SERVER['PHP_SELF'].' '.$name,
);
return str_replace($placeholders, $replacements, $this->getHelp());
@ -504,7 +539,7 @@ class Command
/**
* Sets the aliases for the command.
*
* @param array $aliases An array of aliases for the command
* @param string[] $aliases An array of aliases for the command
*
* @return Command The current instance
*
@ -514,6 +549,10 @@ class Command
*/
public function setAliases($aliases)
{
if (!is_array($aliases) && !$aliases instanceof \Traversable) {
throw new \InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
}
foreach ($aliases as $alias) {
$this->validateName($alias);
}
@ -584,7 +623,7 @@ class Command
/**
* Returns an XML representation of the command.
*
* @param Boolean $asDom Whether to return a DOM or an XML string
* @param bool $asDom Whether to return a DOM or an XML string
*
* @return string|\DOMDocument An XML string representing the command
*

View file

@ -58,7 +58,7 @@ EOF
}
/**
* Sets the command
* Sets the command.
*
* @param Command $command The command to set
*/
@ -83,7 +83,7 @@ EOF
$helper = new DescriptorHelper();
$helper->describe($output, $this->command, array(
'format' => $input->getOption('format'),
'raw' => $input->getOption('raw'),
'raw_text' => $input->getOption('raw'),
));
$this->command = null;

View file

@ -74,8 +74,8 @@ EOF
$helper = new DescriptorHelper();
$helper->describe($output, $this->getApplication(), array(
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'namespace' => $input->getArgument('namespace'),
));
}

View file

@ -26,6 +26,8 @@ final class ConsoleEvents
* The event listener method receives a Symfony\Component\Console\Event\ConsoleCommandEvent
* instance.
*
* @Event
*
* @var string
*/
const COMMAND = 'console.command';
@ -37,6 +39,8 @@ final class ConsoleEvents
* The event listener method receives a Symfony\Component\Console\Event\ConsoleTerminateEvent
* instance.
*
* @Event
*
* @var string
*/
const TERMINATE = 'console.terminate';
@ -49,6 +53,8 @@ final class ConsoleEvents
* a Symfony\Component\Console\Event\ConsoleExceptionEvent
* instance.
*
* @Event
*
* @var string
*/
const EXCEPTION = 'console.exception';

View file

@ -16,6 +16,8 @@ use Symfony\Component\Console\Command\Command;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*
* @internal
*/
class ApplicationDescription
{
@ -144,9 +146,11 @@ class ApplicationDescription
}
ksort($namespacedCommands);
foreach ($namespacedCommands as &$commands) {
ksort($commands);
foreach ($namespacedCommands as &$commandsSet) {
ksort($commandsSet);
}
// unset reference to keep scope clear
unset($commandsSet);
return $namespacedCommands;
}

View file

@ -20,6 +20,8 @@ use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*
* @internal
*/
abstract class Descriptor implements DescriptorInterface
{
@ -59,8 +61,8 @@ abstract class Descriptor implements DescriptorInterface
/**
* Writes content to output.
*
* @param string $content
* @param boolean $decorated
* @param string $content
* @param bool $decorated
*/
protected function write($content, $decorated = false)
{

View file

@ -21,6 +21,8 @@ use Symfony\Component\Console\Input\InputOption;
* JSON descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @internal
*/
class JsonDescriptor extends Descriptor
{
@ -97,11 +99,11 @@ class JsonDescriptor extends Descriptor
private function getInputArgumentData(InputArgument $argument)
{
return array(
'name' => $argument->getName(),
'name' => $argument->getName(),
'is_required' => $argument->isRequired(),
'is_array' => $argument->isArray(),
'is_array' => $argument->isArray(),
'description' => $argument->getDescription(),
'default' => $argument->getDefault(),
'default' => $argument->getDefault(),
);
}
@ -113,13 +115,13 @@ class JsonDescriptor extends Descriptor
private function getInputOptionData(InputOption $option)
{
return array(
'name' => '--'.$option->getName(),
'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '',
'accept_value' => $option->acceptValue(),
'name' => '--'.$option->getName(),
'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '',
'accept_value' => $option->acceptValue(),
'is_value_required' => $option->isValueRequired(),
'is_multiple' => $option->isArray(),
'description' => $option->getDescription(),
'default' => $option->getDefault(),
'is_multiple' => $option->isArray(),
'description' => $option->getDescription(),
'default' => $option->getDefault(),
);
}
@ -154,12 +156,12 @@ class JsonDescriptor extends Descriptor
$command->mergeApplicationDefinition(false);
return array(
'name' => $command->getName(),
'usage' => $command->getSynopsis(),
'name' => $command->getName(),
'usage' => $command->getSynopsis(),
'description' => $command->getDescription(),
'help' => $command->getProcessedHelp(),
'aliases' => $command->getAliases(),
'definition' => $this->getInputDefinitionData($command->getNativeDefinition()),
'help' => $command->getProcessedHelp(),
'aliases' => $command->getAliases(),
'definition' => $this->getInputDefinitionData($command->getNativeDefinition()),
);
}
}

View file

@ -21,6 +21,8 @@ use Symfony\Component\Console\Input\InputOption;
* Markdown descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @internal
*/
class MarkdownDescriptor extends Descriptor
{
@ -103,7 +105,7 @@ class MarkdownDescriptor extends Descriptor
$this->write($help);
}
if ($definition = $command->getNativeDefinition()) {
if ($command->getNativeDefinition()) {
$this->write("\n\n");
$this->describeInputDefinition($command->getNativeDefinition());
}
@ -128,7 +130,7 @@ class MarkdownDescriptor extends Descriptor
$this->write("\n\n");
$this->write(implode("\n", array_map(function ($commandName) {
return '* '.$commandName;
} , $namespace['commands'])));
}, $namespace['commands'])));
}
foreach ($description->getCommands() as $command) {

View file

@ -21,6 +21,8 @@ use Symfony\Component\Console\Input\InputOption;
* Text descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @internal
*/
class TextDescriptor extends Descriptor
{
@ -157,13 +159,37 @@ class TextDescriptor extends Descriptor
$this->writeText("\n");
}
} else {
if ('' != $help = $application->getHelp()) {
$this->writeText("$help\n\n", $options);
}
$this->writeText("<comment>Usage:</comment>\n", $options);
$this->writeText(" command [options] [arguments]\n\n", $options);
$this->writeText('<comment>Options:</comment>', $options);
$inputOptions = $application->getDefinition()->getOptions();
$width = 0;
foreach ($inputOptions as $option) {
$nameLength = strlen($option->getName()) + 2;
if ($option->getShortcut()) {
$nameLength += strlen($option->getShortcut()) + 3;
}
$width = max($width, $nameLength);
}
++$width;
foreach ($inputOptions as $option) {
$this->writeText("\n", $options);
$this->describeInputOption($option, array_merge($options, array('name_width' => $width)));
}
$this->writeText("\n\n", $options);
$width = $this->getColumnWidth($description->getCommands());
$this->writeText($application->getHelp(), $options);
$this->writeText("\n\n");
if ($describedNamespace) {
$this->writeText(sprintf("<comment>Available commands for the \"%s\" namespace:</comment>", $describedNamespace), $options);
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
} else {
$this->writeText('<comment>Available commands:</comment>', $options);
}
@ -177,7 +203,7 @@ class TextDescriptor extends Descriptor
foreach ($namespace['commands'] as $name) {
$this->writeText("\n");
$this->writeText(sprintf(" <info>%-${width}s</info> %s", $name, $description->getCommand($name)->getDescription()), $options);
$this->writeText(sprintf(" <info>%-${width}s</info> %s", $name, $description->getCommand($name)->getDescription()), $options);
}
}
@ -205,7 +231,7 @@ class TextDescriptor extends Descriptor
*/
private function formatDefaultValue($default)
{
if (version_compare(PHP_VERSION, '5.4', '<')) {
if (PHP_VERSION_ID < 50400) {
return str_replace('\/', '/', json_encode($default));
}

View file

@ -21,6 +21,8 @@ use Symfony\Component\Console\Input\InputOption;
* XML descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @internal
*/
class XmlDescriptor extends Descriptor
{

View file

@ -12,10 +12,51 @@
namespace Symfony\Component\Console\Event;
/**
* Allows to do things before the command is executed.
* Allows to do things before the command is executed, like skipping the command or changing the input.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConsoleCommandEvent extends ConsoleEvent
{
/**
* The return code for skipped commands, this will also be passed into the terminate event
*/
const RETURN_CODE_DISABLED = 113;
/**
* Indicates if the command should be run or skipped
*
* @var bool
*/
private $commandShouldRun = true;
/**
* Disables the command, so it won't be run
*
* @return bool
*/
public function disableCommand()
{
return $this->commandShouldRun = false;
}
/**
* Enables the command
*
* @return bool
*/
public function enableCommand()
{
return $this->commandShouldRun = true;
}
/**
* Returns true if the command is runnable, false otherwise
*
* @return bool
*/
public function commandShouldRun()
{
return $this->commandShouldRun;
}
}

View file

@ -58,7 +58,7 @@ class ConsoleExceptionEvent extends ConsoleEvent
/**
* Gets the exit code.
*
* @return integer The command exit code
* @return int The command exit code
*/
public function getExitCode()
{

View file

@ -25,7 +25,7 @@ class ConsoleTerminateEvent extends ConsoleEvent
/**
* The exit code of the command.
*
* @var integer
* @var int
*/
private $exitCode;
@ -39,7 +39,7 @@ class ConsoleTerminateEvent extends ConsoleEvent
/**
* Sets the exit code.
*
* @param integer $exitCode The command exit code
* @param int $exitCode The command exit code
*/
public function setExitCode($exitCode)
{
@ -49,7 +49,7 @@ class ConsoleTerminateEvent extends ConsoleEvent
/**
* Gets the exit code.
*
* @return integer The command exit code
* @return int The command exit code
*/
public function getExitCode()
{

View file

@ -33,20 +33,20 @@ class OutputFormatter implements OutputFormatterInterface
*/
public static function escape($text)
{
return preg_replace('/([^\\\\]?)</is', '$1\\<', $text);
return preg_replace('/([^\\\\]?)</', '$1\\<', $text);
}
/**
* Initializes console output formatter.
*
* @param Boolean $decorated Whether this formatter should actually decorate strings
* @param bool $decorated Whether this formatter should actually decorate strings
* @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances
*
* @api
*/
public function __construct($decorated = false, array $styles = array())
{
$this->decorated = (Boolean) $decorated;
$this->decorated = (bool) $decorated;
$this->setStyle('error', new OutputFormatterStyle('white', 'red'));
$this->setStyle('info', new OutputFormatterStyle('green'));
@ -63,19 +63,19 @@ class OutputFormatter implements OutputFormatterInterface
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorate the messages or not
* @param bool $decorated Whether to decorate the messages or not
*
* @api
*/
public function setDecorated($decorated)
{
$this->decorated = (Boolean) $decorated;
$this->decorated = (bool) $decorated;
}
/**
* Gets the decorated flag.
*
* @return Boolean true if the output will decorate messages, false otherwise
* @return bool true if the output will decorate messages, false otherwise
*
* @api
*/
@ -102,7 +102,7 @@ class OutputFormatter implements OutputFormatterInterface
*
* @param string $name
*
* @return Boolean
* @return bool
*
* @api
*/
@ -142,14 +142,19 @@ class OutputFormatter implements OutputFormatterInterface
*/
public function format($message)
{
$message = (string) $message;
$offset = 0;
$output = '';
$tagRegex = '[a-z][a-z0-9_=;-]*';
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE);
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
$text = $match[0];
if (0 != $pos && '\\' == $message[$pos - 1]) {
continue;
}
// add the text up to the next tag
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
$offset = $pos + strlen($text);
@ -164,9 +169,6 @@ class OutputFormatter implements OutputFormatterInterface
if (!$open && !$tag) {
// </>
$this->styleStack->pop();
} elseif ($pos && '\\' == $message[$pos - 1]) {
// escaped tag
$output .= $this->applyCurrentStyle($text);
} elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
$output .= $this->applyCurrentStyle($text);
} elseif ($open) {
@ -194,7 +196,7 @@ class OutputFormatter implements OutputFormatterInterface
*
* @param string $string
*
* @return OutputFormatterStyle|Boolean false if string is not format string
* @return OutputFormatterStyle|bool false if string is not format string
*/
private function createStyleFromString($string)
{
@ -215,7 +217,11 @@ class OutputFormatter implements OutputFormatterInterface
} elseif ('bg' == $match[0]) {
$style->setBackground($match[1]);
} else {
$style->setOption($match[1]);
try {
$style->setOption($match[1]);
} catch (\InvalidArgumentException $e) {
return false;
}
}
}

View file

@ -23,7 +23,7 @@ interface OutputFormatterInterface
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorate the messages or not
* @param bool $decorated Whether to decorate the messages or not
*
* @api
*/
@ -32,7 +32,7 @@ interface OutputFormatterInterface
/**
* Gets the decorated flag.
*
* @return Boolean true if the output will decorate messages, false otherwise
* @return bool true if the output will decorate messages, false otherwise
*
* @api
*/
@ -53,7 +53,7 @@ interface OutputFormatterInterface
*
* @param string $name
*
* @return Boolean
* @return bool
*
* @api
*/

View file

@ -21,31 +21,33 @@ namespace Symfony\Component\Console\Formatter;
class OutputFormatterStyle implements OutputFormatterStyleInterface
{
private static $availableForegroundColors = array(
'black' => 30,
'red' => 31,
'green' => 32,
'yellow' => 33,
'blue' => 34,
'magenta' => 35,
'cyan' => 36,
'white' => 37
'black' => array('set' => 30, 'unset' => 39),
'red' => array('set' => 31, 'unset' => 39),
'green' => array('set' => 32, 'unset' => 39),
'yellow' => array('set' => 33, 'unset' => 39),
'blue' => array('set' => 34, 'unset' => 39),
'magenta' => array('set' => 35, 'unset' => 39),
'cyan' => array('set' => 36, 'unset' => 39),
'white' => array('set' => 37, 'unset' => 39),
'default' => array('set' => 39, 'unset' => 39),
);
private static $availableBackgroundColors = array(
'black' => 40,
'red' => 41,
'green' => 42,
'yellow' => 43,
'blue' => 44,
'magenta' => 45,
'cyan' => 46,
'white' => 47
'black' => array('set' => 40, 'unset' => 49),
'red' => array('set' => 41, 'unset' => 49),
'green' => array('set' => 42, 'unset' => 49),
'yellow' => array('set' => 43, 'unset' => 49),
'blue' => array('set' => 44, 'unset' => 49),
'magenta' => array('set' => 45, 'unset' => 49),
'cyan' => array('set' => 46, 'unset' => 49),
'white' => array('set' => 47, 'unset' => 49),
'default' => array('set' => 49, 'unset' => 49),
);
private static $availableOptions = array(
'bold' => 1,
'underscore' => 4,
'blink' => 5,
'reverse' => 7,
'conceal' => 8
'bold' => array('set' => 1, 'unset' => 22),
'underscore' => array('set' => 4, 'unset' => 24),
'blink' => array('set' => 5, 'unset' => 25),
'reverse' => array('set' => 7, 'unset' => 27),
'conceal' => array('set' => 8, 'unset' => 28),
);
private $foreground;
@ -149,7 +151,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
));
}
if (false === array_search(static::$availableOptions[$option], $this->options)) {
if (!in_array(static::$availableOptions[$option], $this->options)) {
$this->options[] = static::$availableOptions[$option];
}
}
@ -160,7 +162,6 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
* @param string $option The option name
*
* @throws \InvalidArgumentException When the option name isn't defined
*
*/
public function unsetOption($option)
{
@ -201,22 +202,28 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
*/
public function apply($text)
{
$codes = array();
$setCodes = array();
$unsetCodes = array();
if (null !== $this->foreground) {
$codes[] = $this->foreground;
$setCodes[] = $this->foreground['set'];
$unsetCodes[] = $this->foreground['unset'];
}
if (null !== $this->background) {
$codes[] = $this->background;
$setCodes[] = $this->background['set'];
$unsetCodes[] = $this->background['unset'];
}
if (count($this->options)) {
$codes = array_merge($codes, $this->options);
foreach ($this->options as $option) {
$setCodes[] = $option['set'];
$unsetCodes[] = $option['unset'];
}
}
if (0 === count($codes)) {
if (0 === count($setCodes)) {
return $text;
}
return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text);
return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes));
}
}

View file

@ -62,7 +62,7 @@ class OutputFormatterStyleStack
*
* @return OutputFormatterStyleInterface
*
* @throws \InvalidArgumentException When style tags incorrectly nested
* @throws \InvalidArgumentException When style tags incorrectly nested
*/
public function pop(OutputFormatterStyleInterface $style = null)
{
@ -96,7 +96,7 @@ class OutputFormatterStyleStack
return $this->emptyStyle;
}
return $this->styles[count($this->styles)-1];
return $this->styles[count($this->styles) - 1];
}
/**

View file

@ -0,0 +1,127 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
/**
* Helps outputting debug information when running an external program from a command.
*
* An external program can be a Process, an HTTP request, or anything else.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DebugFormatterHelper extends Helper
{
private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default');
private $started = array();
private $count = -1;
/**
* Starts a debug formatting session
*
* @param string $id The id of the formatting session
* @param string $message The message to display
* @param string $prefix The prefix to use
*
* @return string
*/
public function start($id, $message, $prefix = 'RUN')
{
$this->started[$id] = array('border' => ++$this->count % count($this->colors));
return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message);
}
/**
* Adds progress to a formatting session
*
* @param string $id The id of the formatting session
* @param string $buffer The message to display
* @param bool $error Whether to consider the buffer as error
* @param string $prefix The prefix for output
* @param string $errorPrefix The prefix for error output
*
* @return string
*/
public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR')
{
$message = '';
if ($error) {
if (isset($this->started[$id]['out'])) {
$message .= "\n";
unset($this->started[$id]['out']);
}
if (!isset($this->started[$id]['err'])) {
$message .= sprintf("%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix);
$this->started[$id]['err'] = true;
}
$message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer);
} else {
if (isset($this->started[$id]['err'])) {
$message .= "\n";
unset($this->started[$id]['err']);
}
if (!isset($this->started[$id]['out'])) {
$message .= sprintf("%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix);
$this->started[$id]['out'] = true;
}
$message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer);
}
return $message;
}
/**
* Stops a formatting session
*
* @param string $id The id of the formatting session
* @param string $message The message to display
* @param bool $successful Whether to consider the result as success
* @param string $prefix The prefix for the end output
*
* @return string
*/
public function stop($id, $message, $successful, $prefix = 'RES')
{
$trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';
if ($successful) {
return sprintf("%s%s<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
}
$message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
unset($this->started[$id]['out'], $this->started[$id]['err']);
return $message;
}
/**
* @param string $id The id of the formatting session
*
* @return string
*/
private function getBorder($id)
{
return sprintf('<bg=%s> </>', $this->colors[$this->started[$id]['border']]);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'debug_formatter';
}
}

View file

@ -36,10 +36,10 @@ class DescriptorHelper extends Helper
public function __construct()
{
$this
->register('txt', new TextDescriptor())
->register('xml', new XmlDescriptor())
->register('txt', new TextDescriptor())
->register('xml', new XmlDescriptor())
->register('json', new JsonDescriptor())
->register('md', new MarkdownDescriptor())
->register('md', new MarkdownDescriptor())
;
}
@ -54,13 +54,13 @@ class DescriptorHelper extends Helper
* @param object $object
* @param array $options
*
* @throws \InvalidArgumentException
* @throws \InvalidArgumentException when the given format is not supported
*/
public function describe(OutputInterface $output, $object, array $options = array())
{
$options = array_merge(array(
'raw_text' => false,
'format' => 'txt',
'raw_text' => false,
'format' => 'txt',
), $options);
if (!isset($this->descriptors[$options['format']])) {

View file

@ -18,6 +18,9 @@ use Symfony\Component\Console\Formatter\OutputFormatterStyle;
* The Dialog class provides helpers to interact with the user.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated Deprecated since version 2.5, to be removed in 3.0.
* Use the question helper instead.
*/
class DialogHelper extends InputAwareHelper
{
@ -31,12 +34,12 @@ class DialogHelper extends InputAwareHelper
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param array $choices List of choices to pick from
* @param Boolean|string $default The default answer if the user enters nothing
* @param Boolean|integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param bool|string $default The default answer if the user enters nothing
* @param bool|int $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param string $errorMessage Message which will be shown if invalid value from choice list would be picked
* @param Boolean $multiselect Select more than one value separated by comma
* @param bool $multiselect Select more than one value separated by comma
*
* @return integer|string|array The selected value or values (the key of the choices array)
* @return int|string|array The selected value or values (the key of the choices array)
*
* @throws \InvalidArgumentException
*/
@ -53,14 +56,14 @@ class DialogHelper extends InputAwareHelper
$result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) {
// Collapse all spaces.
$selectedChoices = str_replace(" ", "", $picked);
$selectedChoices = str_replace(' ', '', $picked);
if ($multiselect) {
// Check for a separated comma values
if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
throw new \InvalidArgumentException(sprintf($errorMessage, $picked));
}
$selectedChoices = explode(",", $selectedChoices);
$selectedChoices = explode(',', $selectedChoices);
} else {
$selectedChoices = array($picked);
}
@ -71,7 +74,7 @@ class DialogHelper extends InputAwareHelper
if (empty($choices[$value])) {
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
}
array_push($multiselectChoices, $value);
$multiselectChoices[] = $value;
}
if ($multiselect) {
@ -135,7 +138,7 @@ class DialogHelper extends InputAwareHelper
// Backspace Character
if ("\177" === $c) {
if (0 === $numMatches && 0 !== $i) {
$i--;
--$i;
// Move cursor backwards
$output->write("\033[1D");
}
@ -150,11 +153,12 @@ class DialogHelper extends InputAwareHelper
// Pop the last character off the end of our string
$ret = substr($ret, 0, $i);
} elseif ("\033" === $c) { // Did we read an escape sequence?
} elseif ("\033" === $c) {
// Did we read an escape sequence?
$c .= fread($inputStream, 2);
// A = Up Arrow. B = Down Arrow
if ('A' === $c[2] || 'B' === $c[2]) {
if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
if ('A' === $c[2] && -1 === $ofs) {
$ofs = 0;
}
@ -187,7 +191,7 @@ class DialogHelper extends InputAwareHelper
} else {
$output->write($c);
$ret .= $c;
$i++;
++$i;
$numMatches = 0;
$ofs = 0;
@ -227,9 +231,9 @@ class DialogHelper extends InputAwareHelper
*
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param Boolean $default The default answer if the user enters nothing
* @param bool $default The default answer if the user enters nothing
*
* @return Boolean true if the user has confirmed, false otherwise
* @return bool true if the user has confirmed, false otherwise
*/
public function askConfirmation(OutputInterface $output, $question, $default = true)
{
@ -246,19 +250,19 @@ class DialogHelper extends InputAwareHelper
}
/**
* Asks a question to the user, the response is hidden
* Asks a question to the user, the response is hidden.
*
* @param OutputInterface $output An Output instance
* @param string|array $question The question
* @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
* @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
*
* @return string The answer
* @return string The answer
*
* @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
*/
public function askHiddenResponse(OutputInterface $output, $question, $fallback = true)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if ('\\' === DIRECTORY_SEPARATOR) {
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
// handle code running from a phar
@ -325,7 +329,7 @@ class DialogHelper extends InputAwareHelper
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param callable $validator A PHP callback
* @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param int|false $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param string $default The default answer if none is given by the user
* @param array $autocomplete List of values to autocomplete
*
@ -354,14 +358,13 @@ class DialogHelper extends InputAwareHelper
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param callable $validator A PHP callback
* @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
* @param int|false $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
*
* @return string The response
* @return string The response
*
* @throws \Exception When any of the validators return an error
* @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
*
*/
public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true)
{
@ -387,7 +390,7 @@ class DialogHelper extends InputAwareHelper
}
/**
* Returns the helper's input stream
* Returns the helper's input stream.
*
* @return string
*/
@ -397,7 +400,7 @@ class DialogHelper extends InputAwareHelper
}
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getName()
{
@ -405,9 +408,9 @@ class DialogHelper extends InputAwareHelper
}
/**
* Return a valid Unix shell
* Return a valid Unix shell.
*
* @return string|Boolean The valid shell name, false in case no valid shell is found
* @return string|bool The valid shell name, false in case no valid shell is found
*/
private function getShell()
{
@ -443,31 +446,31 @@ class DialogHelper extends InputAwareHelper
}
/**
* Validate an attempt
* Validate an attempt.
*
* @param callable $interviewer A callable that will ask for a question and return the result
* @param OutputInterface $output An Output instance
* @param callable $validator A PHP callback
* @param integer $attempts Max number of times to ask before giving up ; false will ask infinitely
* @param callable $interviewer A callable that will ask for a question and return the result
* @param OutputInterface $output An Output instance
* @param callable $validator A PHP callback
* @param int|false $attempts Max number of times to ask before giving up ; false will ask infinitely
*
* @return string The validated response
* @return string The validated response
*
* @throws \Exception In case the max number of attempts has been reached and no valid response has been given
*/
private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts)
{
$error = null;
$e = null;
while (false === $attempts || $attempts--) {
if (null !== $error) {
$output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
if (null !== $e) {
$output->writeln($this->getHelperSet()->get('formatter')->formatBlock($e->getMessage(), 'error'));
}
try {
return call_user_func($validator, $interviewer());
} catch (\Exception $error) {
} catch (\Exception $e) {
}
}
throw $error;
throw $e;
}
}

View file

@ -39,13 +39,15 @@ class FormatterHelper extends Helper
*
* @param string|array $messages The message to write in the block
* @param string $style The style to apply to the whole block
* @param Boolean $large Whether to return a large block
* @param bool $large Whether to return a large block
*
* @return string The formatter message
*/
public function formatBlock($messages, $style, $large = false)
{
$messages = (array) $messages;
if (!is_array($messages)) {
$messages = array($messages);
}
$len = 0;
$lines = array();
@ -56,22 +58,22 @@ class FormatterHelper extends Helper
}
$messages = $large ? array(str_repeat(' ', $len)) : array();
foreach ($lines as $line) {
$messages[] = $line.str_repeat(' ', $len - $this->strlen($line));
for ($i = 0; isset($lines[$i]); ++$i) {
$messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i]));
}
if ($large) {
$messages[] = str_repeat(' ', $len);
}
foreach ($messages as &$message) {
$message = sprintf('<%s>%s</%s>', $style, $message, $style);
for ($i = 0; isset($messages[$i]); ++$i) {
$messages[$i] = sprintf('<%s>%s</%s>', $style, $messages[$i], $style);
}
return implode("\n", $messages);
}
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getName()
{

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* Helper is the base class for all helper classes.
*
@ -41,15 +43,15 @@ abstract class Helper implements HelperInterface
}
/**
* Returns the length of a string, using mb_strlen if it is available.
* Returns the length of a string, using mb_strwidth if it is available.
*
* @param string $string The string to check its length
*
* @return integer The length of the string
* @return int The length of the string
*/
protected function strlen($string)
public static function strlen($string)
{
if (!function_exists('mb_strlen')) {
if (!function_exists('mb_strwidth')) {
return strlen($string);
}
@ -57,6 +59,63 @@ abstract class Helper implements HelperInterface
return strlen($string);
}
return mb_strlen($string, $encoding);
return mb_strwidth($string, $encoding);
}
public static function formatTime($secs)
{
static $timeFormats = array(
array(0, '< 1 sec'),
array(2, '1 sec'),
array(59, 'secs', 1),
array(60, '1 min'),
array(3600, 'mins', 60),
array(5400, '1 hr'),
array(86400, 'hrs', 3600),
array(129600, '1 day'),
array(604800, 'days', 86400),
);
foreach ($timeFormats as $format) {
if ($secs >= $format[0]) {
continue;
}
if (2 == count($format)) {
return $format[1];
}
return ceil($secs / $format[2]).' '.$format[1];
}
}
public static function formatMemory($memory)
{
if ($memory >= 1024 * 1024 * 1024) {
return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
}
if ($memory >= 1024 * 1024) {
return sprintf('%.1f MiB', $memory / 1024 / 1024);
}
if ($memory >= 1024) {
return sprintf('%d KiB', $memory / 1024);
}
return sprintf('%d B', $memory);
}
public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string)
{
$isDecorated = $formatter->isDecorated();
$formatter->setDecorated(false);
// remove <...> formatting
$string = $formatter->format($string);
// remove already formatted characters
$string = preg_replace("/\033\[[^m]*m/", '', $string);
$formatter->setDecorated($isDecorated);
return self::strlen($string);
}
}

View file

@ -56,7 +56,7 @@ class HelperSet implements \IteratorAggregate
*
* @param string $name The helper name
*
* @return Boolean true if the helper is defined, false otherwise
* @return bool true if the helper is defined, false otherwise
*/
public function has($name)
{

View file

@ -24,7 +24,7 @@ abstract class InputAwareHelper extends Helper implements InputAwareInterface
protected $input;
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function setInput(InputInterface $input)
{

View file

@ -0,0 +1,142 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessBuilder;
/**
* The ProcessHelper class provides helpers to run external processes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ProcessHelper extends Helper
{
/**
* Runs an external process.
*
* @param OutputInterface $output An OutputInterface instance
* @param string|array|Process $cmd An instance of Process or an array of arguments to escape and run or a command to run
* @param string|null $error An error message that must be displayed if something went wrong
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
* @param int $verbosity The threshold for verbosity
*
* @return Process The process that ran
*/
public function run(OutputInterface $output, $cmd, $error = null, $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE)
{
$formatter = $this->getHelperSet()->get('debug_formatter');
if (is_array($cmd)) {
$process = ProcessBuilder::create($cmd)->getProcess();
} elseif ($cmd instanceof Process) {
$process = $cmd;
} else {
$process = new Process($cmd);
}
if ($verbosity <= $output->getVerbosity()) {
$output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
}
if ($output->isDebug()) {
$callback = $this->wrapCallback($output, $process, $callback);
}
$process->run($callback);
if ($verbosity <= $output->getVerbosity()) {
$message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
$output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
}
if (!$process->isSuccessful() && null !== $error) {
$output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
}
return $process;
}
/**
* Runs the process.
*
* This is identical to run() except that an exception is thrown if the process
* exits with a non-zero exit code.
*
* @param OutputInterface $output An OutputInterface instance
* @param string|Process $cmd An instance of Process or a command to run
* @param string|null $error An error message that must be displayed if something went wrong
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
*
* @return Process The process that ran
*
* @throws ProcessFailedException
*
* @see run()
*/
public function mustRun(OutputInterface $output, $cmd, $error = null, $callback = null)
{
$process = $this->run($output, $cmd, $error, $callback);
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
return $process;
}
/**
* Wraps a Process callback to add debugging output.
*
* @param OutputInterface $output An OutputInterface interface
* @param Process $process The Process
* @param callable|null $callback A PHP callable
*
* @return callable
*/
public function wrapCallback(OutputInterface $output, Process $process, $callback = null)
{
$formatter = $this->getHelperSet()->get('debug_formatter');
$that = $this;
return function ($type, $buffer) use ($output, $process, $callback, $formatter, $that) {
$output->write($formatter->progress(spl_object_hash($process), $that->escapeString($buffer), Process::ERR === $type));
if (null !== $callback) {
call_user_func($callback, $type, $buffer);
}
};
}
/**
* This method is public for PHP 5.3 compatibility, it should be private.
*
* @internal
*/
public function escapeString($str)
{
return str_replace('<', '\\<', $str);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'process';
}
}

View file

@ -0,0 +1,611 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
/**
* The ProgressBar provides helpers to display progress output.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Chris Jones <leeked@gmail.com>
*/
class ProgressBar
{
// options
private $barWidth = 28;
private $barChar;
private $emptyBarChar = '-';
private $progressChar = '>';
private $format = null;
private $redrawFreq = 1;
/**
* @var OutputInterface
*/
private $output;
private $step = 0;
private $max;
private $startTime;
private $stepWidth;
private $percent = 0.0;
private $lastMessagesLength = 0;
private $formatLineCount;
private $messages;
private $overwrite = true;
private static $formatters;
private static $formats;
/**
* Constructor.
*
* @param OutputInterface $output An OutputInterface instance
* @param int $max Maximum steps (0 if unknown)
*/
public function __construct(OutputInterface $output, $max = 0)
{
$this->output = $output;
$this->setMaxSteps($max);
if (!$this->output->isDecorated()) {
// disable overwrite when output does not support ANSI codes.
$this->overwrite = false;
if ($this->max > 10) {
// set a reasonable redraw frequency so output isn't flooded
$this->setRedrawFrequency($max / 10);
}
}
$this->setFormat($this->determineBestFormat());
$this->startTime = time();
}
/**
* Sets a placeholder formatter for a given name.
*
* This method also allow you to override an existing placeholder.
*
* @param string $name The placeholder name (including the delimiter char like %)
* @param callable $callable A PHP callable
*/
public static function setPlaceholderFormatterDefinition($name, $callable)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
self::$formatters[$name] = $callable;
}
/**
* Gets the placeholder formatter for a given name.
*
* @param string $name The placeholder name (including the delimiter char like %)
*
* @return callable|null A PHP callable
*/
public static function getPlaceholderFormatterDefinition($name)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
}
/**
* Sets a format for a given name.
*
* This method also allow you to override an existing format.
*
* @param string $name The format name
* @param string $format A format string
*/
public static function setFormatDefinition($name, $format)
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
self::$formats[$name] = $format;
}
/**
* Gets the format for a given name.
*
* @param string $name The format name
*
* @return string|null A format string
*/
public static function getFormatDefinition($name)
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
}
public function setMessage($message, $name = 'message')
{
$this->messages[$name] = $message;
}
public function getMessage($name = 'message')
{
return $this->messages[$name];
}
/**
* Gets the progress bar start time.
*
* @return int The progress bar start time
*/
public function getStartTime()
{
return $this->startTime;
}
/**
* Gets the progress bar maximal steps.
*
* @return int The progress bar max steps
*/
public function getMaxSteps()
{
return $this->max;
}
/**
* Gets the progress bar step.
*
* @deprecated since 2.6, to be removed in 3.0. Use {@link getProgress()} instead.
*
* @return int The progress bar step
*/
public function getStep()
{
return $this->getProgress();
}
/**
* Gets the current step position.
*
* @return int The progress bar step
*/
public function getProgress()
{
return $this->step;
}
/**
* Gets the progress bar step width.
*
* @internal This method is public for PHP 5.3 compatibility, it should not be used.
*
* @return int The progress bar step width
*/
public function getStepWidth()
{
return $this->stepWidth;
}
/**
* Gets the current progress bar percent.
*
* @return float The current progress bar percent
*/
public function getProgressPercent()
{
return $this->percent;
}
/**
* Sets the progress bar width.
*
* @param int $size The progress bar size
*/
public function setBarWidth($size)
{
$this->barWidth = (int) $size;
}
/**
* Gets the progress bar width.
*
* @return int The progress bar size
*/
public function getBarWidth()
{
return $this->barWidth;
}
/**
* Sets the bar character.
*
* @param string $char A character
*/
public function setBarCharacter($char)
{
$this->barChar = $char;
}
/**
* Gets the bar character.
*
* @return string A character
*/
public function getBarCharacter()
{
if (null === $this->barChar) {
return $this->max ? '=' : $this->emptyBarChar;
}
return $this->barChar;
}
/**
* Sets the empty bar character.
*
* @param string $char A character
*/
public function setEmptyBarCharacter($char)
{
$this->emptyBarChar = $char;
}
/**
* Gets the empty bar character.
*
* @return string A character
*/
public function getEmptyBarCharacter()
{
return $this->emptyBarChar;
}
/**
* Sets the progress bar character.
*
* @param string $char A character
*/
public function setProgressCharacter($char)
{
$this->progressChar = $char;
}
/**
* Gets the progress bar character.
*
* @return string A character
*/
public function getProgressCharacter()
{
return $this->progressChar;
}
/**
* Sets the progress bar format.
*
* @param string $format The format
*/
public function setFormat($format)
{
// try to use the _nomax variant if available
if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
$this->format = self::getFormatDefinition($format.'_nomax');
} elseif (null !== self::getFormatDefinition($format)) {
$this->format = self::getFormatDefinition($format);
} else {
$this->format = $format;
}
$this->formatLineCount = substr_count($this->format, "\n");
}
/**
* Sets the redraw frequency.
*
* @param int $freq The frequency in steps
*/
public function setRedrawFrequency($freq)
{
$this->redrawFreq = (int) $freq;
}
/**
* Starts the progress output.
*
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
*/
public function start($max = null)
{
$this->startTime = time();
$this->step = 0;
$this->percent = 0.0;
if (null !== $max) {
$this->setMaxSteps($max);
}
$this->display();
}
/**
* Advances the progress output X steps.
*
* @param int $step Number of steps to advance
*
* @throws \LogicException
*/
public function advance($step = 1)
{
$this->setProgress($this->step + $step);
}
/**
* Sets the current progress.
*
* @deprecated since 2.6, to be removed in 3.0. Use {@link setProgress()} instead.
*
* @param int $step The current progress
*
* @throws \LogicException
*/
public function setCurrent($step)
{
$this->setProgress($step);
}
/**
* Sets whether to overwrite the progressbar, false for new line.
*
* @param bool $overwrite
*/
public function setOverwrite($overwrite)
{
$this->overwrite = (bool) $overwrite;
}
/**
* Sets the current progress.
*
* @param int $step The current progress
*
* @throws \LogicException
*/
public function setProgress($step)
{
$step = (int) $step;
if ($step < $this->step) {
throw new \LogicException('You can\'t regress the progress bar.');
}
if ($this->max && $step > $this->max) {
$this->max = $step;
}
$prevPeriod = (int) ($this->step / $this->redrawFreq);
$currPeriod = (int) ($step / $this->redrawFreq);
$this->step = $step;
$this->percent = $this->max ? (float) $this->step / $this->max : 0;
if ($prevPeriod !== $currPeriod || $this->max === $step) {
$this->display();
}
}
/**
* Finishes the progress output.
*/
public function finish()
{
if (!$this->max) {
$this->max = $this->step;
}
if ($this->step === $this->max && !$this->overwrite) {
// prevent double 100% output
return;
}
$this->setProgress($this->max);
}
/**
* Outputs the current progress string.
*/
public function display()
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}
// these 3 variables can be removed in favor of using $this in the closure when support for PHP 5.3 will be dropped.
$self = $this;
$output = $this->output;
$messages = $this->messages;
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self, $output, $messages) {
if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
$text = call_user_func($formatter, $self, $output);
} elseif (isset($messages[$matches[1]])) {
$text = $messages[$matches[1]];
} else {
return $matches[0];
}
if (isset($matches[2])) {
$text = sprintf('%'.$matches[2], $text);
}
return $text;
}, $this->format));
}
/**
* Removes the progress bar from the current line.
*
* This is useful if you wish to write some output
* while a progress bar is running.
* Call display() to show the progress bar again.
*/
public function clear()
{
if (!$this->overwrite) {
return;
}
$this->overwrite(str_repeat("\n", $this->formatLineCount));
}
/**
* Sets the progress bar maximal steps.
*
* @param int The progress bar max steps
*/
private function setMaxSteps($max)
{
$this->max = max(0, (int) $max);
$this->stepWidth = $this->max ? Helper::strlen($this->max) : 4;
}
/**
* Overwrites a previous message to the output.
*
* @param string $message The message
*/
private function overwrite($message)
{
$lines = explode("\n", $message);
// append whitespace to match the line's length
if (null !== $this->lastMessagesLength) {
foreach ($lines as $i => $line) {
if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $line)) {
$lines[$i] = str_pad($line, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
}
}
}
if ($this->overwrite) {
// move back to the beginning of the progress bar before redrawing it
$this->output->write("\x0D");
} elseif ($this->step > 0) {
// move to new line
$this->output->writeln('');
}
if ($this->formatLineCount) {
$this->output->write(sprintf("\033[%dA", $this->formatLineCount));
}
$this->output->write(implode("\n", $lines));
$this->lastMessagesLength = 0;
foreach ($lines as $line) {
$len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line);
if ($len > $this->lastMessagesLength) {
$this->lastMessagesLength = $len;
}
}
}
private function determineBestFormat()
{
switch ($this->output->getVerbosity()) {
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
case OutputInterface::VERBOSITY_VERBOSE:
return $this->max ? 'verbose' : 'verbose_nomax';
case OutputInterface::VERBOSITY_VERY_VERBOSE:
return $this->max ? 'very_verbose' : 'very_verbose_nomax';
case OutputInterface::VERBOSITY_DEBUG:
return $this->max ? 'debug' : 'debug_nomax';
default:
return $this->max ? 'normal' : 'normal_nomax';
}
}
private static function initPlaceholderFormatters()
{
return array(
'bar' => function (ProgressBar $bar, OutputInterface $output) {
$completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth());
$display = str_repeat($bar->getBarCharacter(), $completeBars);
if ($completeBars < $bar->getBarWidth()) {
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
$display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
}
return $display;
},
'elapsed' => function (ProgressBar $bar) {
return Helper::formatTime(time() - $bar->getStartTime());
},
'remaining' => function (ProgressBar $bar) {
if (!$bar->getMaxSteps()) {
throw new \LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
}
if (!$bar->getProgress()) {
$remaining = 0;
} else {
$remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress()));
}
return Helper::formatTime($remaining);
},
'estimated' => function (ProgressBar $bar) {
if (!$bar->getMaxSteps()) {
throw new \LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
}
if (!$bar->getProgress()) {
$estimated = 0;
} else {
$estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps());
}
return Helper::formatTime($estimated);
},
'memory' => function (ProgressBar $bar) {
return Helper::formatMemory(memory_get_usage(true));
},
'current' => function (ProgressBar $bar) {
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
},
'max' => function (ProgressBar $bar) {
return $bar->getMaxSteps();
},
'percent' => function (ProgressBar $bar) {
return floor($bar->getProgressPercent() * 100);
},
);
}
private static function initFormats()
{
return array(
'normal' => ' %current%/%max% [%bar%] %percent:3s%%',
'normal_nomax' => ' %current% [%bar%]',
'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%',
'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',
'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%',
'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',
'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
);
}
}

View file

@ -11,6 +11,7 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
@ -18,23 +19,25 @@ use Symfony\Component\Console\Output\OutputInterface;
*
* @author Chris Jones <leeked@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated Deprecated since 2.5, to be removed in 3.0; use ProgressBar instead.
*/
class ProgressHelper extends Helper
{
const FORMAT_QUIET = ' %percent%%';
const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%';
const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%';
const FORMAT_QUIET_NOMAX = ' %current%';
const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]';
const FORMAT_QUIET = ' %percent%%';
const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%';
const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%';
const FORMAT_QUIET_NOMAX = ' %current%';
const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]';
const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%';
// options
private $barWidth = 28;
private $barChar = '=';
private $barWidth = 28;
private $barChar = '=';
private $emptyBarChar = '-';
private $progressChar = '>';
private $format = null;
private $redrawFreq = 1;
private $format = null;
private $redrawFreq = 1;
private $lastMessagesLength;
private $barCharOriginal;
@ -45,28 +48,28 @@ class ProgressHelper extends Helper
private $output;
/**
* Current step
* Current step.
*
* @var integer
* @var int
*/
private $current;
/**
* Maximum number of steps
* Maximum number of steps.
*
* @var integer
* @var int
*/
private $max;
/**
* Start time of the progress bar
* Start time of the progress bar.
*
* @var integer
* @var int
*/
private $startTime;
/**
* List of formatting variables
* List of formatting variables.
*
* @var array
*/
@ -79,26 +82,26 @@ class ProgressHelper extends Helper
);
/**
* Available formatting variables
* Available formatting variables.
*
* @var array
*/
private $formatVars;
/**
* Stored format part widths (used for padding)
* Stored format part widths (used for padding).
*
* @var array
*/
private $widths = array(
'current' => 4,
'max' => 4,
'max' => 4,
'percent' => 3,
'elapsed' => 6,
);
/**
* Various time formats
* Various time formats.
*
* @var array
*/
@ -178,14 +181,16 @@ class ProgressHelper extends Helper
* Starts the progress output.
*
* @param OutputInterface $output An Output instance
* @param integer|null $max Maximum steps
* @param int|null $max Maximum steps
*/
public function start(OutputInterface $output, $max = null)
{
$this->startTime = time();
$this->current = 0;
$this->max = (int) $max;
$this->output = $output;
$this->current = 0;
$this->max = (int) $max;
// Disabling output when it does not support ANSI codes as it would result in a broken display anyway.
$this->output = $output->isDecorated() ? $output : new NullOutput();
$this->lastMessagesLength = 0;
$this->barCharOriginal = '';
@ -220,8 +225,8 @@ class ProgressHelper extends Helper
/**
* Advances the progress output X steps.
*
* @param integer $step Number of steps to advance
* @param Boolean $redraw Whether to redraw or not
* @param int $step Number of steps to advance
* @param bool $redraw Whether to redraw or not
*
* @throws \LogicException
*/
@ -233,8 +238,8 @@ class ProgressHelper extends Helper
/**
* Sets the current progress.
*
* @param integer $current The current progress
* @param Boolean $redraw Whether to redraw or not
* @param int $current The current progress
* @param bool $redraw Whether to redraw or not
*
* @throws \LogicException
*/
@ -254,11 +259,11 @@ class ProgressHelper extends Helper
$redraw = true;
}
$prevPeriod = intval($this->current / $this->redrawFreq);
$prevPeriod = (int) ($this->current / $this->redrawFreq);
$this->current = $current;
$currPeriod = intval($this->current / $this->redrawFreq);
$currPeriod = (int) ($this->current / $this->redrawFreq);
if ($redraw || $prevPeriod !== $currPeriod || $this->max === $this->current) {
$this->display();
}
@ -267,7 +272,7 @@ class ProgressHelper extends Helper
/**
* Outputs the current progress string.
*
* @param Boolean $finish Forces the end result
* @param bool $finish Forces the end result
*
* @throws \LogicException
*/
@ -329,24 +334,24 @@ class ProgressHelper extends Helper
}
if ($this->max > 0) {
$this->widths['max'] = $this->strlen($this->max);
$this->widths['max'] = $this->strlen($this->max);
$this->widths['current'] = $this->widths['max'];
} else {
$this->barCharOriginal = $this->barChar;
$this->barChar = $this->emptyBarChar;
$this->barChar = $this->emptyBarChar;
}
}
/**
* Generates the array map of format variables to values.
*
* @param Boolean $finish Forces the end result
* @param bool $finish Forces the end result
*
* @return array Array of format vars and values
*/
private function generate($finish = false)
{
$vars = array();
$vars = array();
$percent = 0;
if ($this->max > 0) {
$percent = (float) $this->current / $this->max;
@ -398,7 +403,7 @@ class ProgressHelper extends Helper
/**
* Converts seconds into human-readable format.
*
* @param integer $secs Number of seconds
* @param int $secs Number of seconds
*
* @return string Time in readable format
*/
@ -423,8 +428,8 @@ class ProgressHelper extends Helper
/**
* Overwrites a previous message to the output.
*
* @param OutputInterface $output An Output instance
* @param string $message The message
* @param OutputInterface $output An Output instance
* @param string $message The message
*/
private function overwrite(OutputInterface $output, $message)
{
@ -443,7 +448,7 @@ class ProgressHelper extends Helper
}
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getName()
{

View file

@ -0,0 +1,418 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Question\ChoiceQuestion;
/**
* The QuestionHelper class provides helpers to interact with the user.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class QuestionHelper extends Helper
{
private $inputStream;
private static $shell;
private static $stty;
/**
* Asks a question to the user.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
* @param Question $question The question to ask
*
* @return string The user answer
*
* @throws \RuntimeException If there is no data to read in the input stream
*/
public function ask(InputInterface $input, OutputInterface $output, Question $question)
{
if (!$input->isInteractive()) {
return $question->getDefault();
}
if (!$question->getValidator()) {
return $this->doAsk($output, $question);
}
$that = $this;
$interviewer = function () use ($output, $question, $that) {
return $that->doAsk($output, $question);
};
return $this->validateAttempts($interviewer, $output, $question);
}
/**
* Sets the input stream to read from when interacting with the user.
*
* This is mainly useful for testing purpose.
*
* @param resource $stream The input stream
*
* @throws \InvalidArgumentException In case the stream is not a resource
*/
public function setInputStream($stream)
{
if (!is_resource($stream)) {
throw new \InvalidArgumentException('Input stream must be a valid resource.');
}
$this->inputStream = $stream;
}
/**
* Returns the helper's input stream
*
* @return resource
*/
public function getInputStream()
{
return $this->inputStream;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'question';
}
/**
* Asks the question to the user.
*
* This method is public for PHP 5.3 compatibility, it should be private.
*
* @param OutputInterface $output
* @param Question $question
*
* @return bool|mixed|null|string
*
* @throws \Exception
* @throws \RuntimeException
*/
public function doAsk(OutputInterface $output, Question $question)
{
$inputStream = $this->inputStream ?: STDIN;
$message = $question->getQuestion();
if ($question instanceof ChoiceQuestion) {
$width = max(array_map('strlen', array_keys($question->getChoices())));
$messages = (array) $question->getQuestion();
foreach ($question->getChoices() as $key => $value) {
$messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
}
$output->writeln($messages);
$message = $question->getPrompt();
}
$output->write($message);
$autocomplete = $question->getAutocompleterValues();
if (null === $autocomplete || !$this->hasSttyAvailable()) {
$ret = false;
if ($question->isHidden()) {
try {
$ret = trim($this->getHiddenResponse($output, $inputStream));
} catch (\RuntimeException $e) {
if (!$question->isHiddenFallback()) {
throw $e;
}
}
}
if (false === $ret) {
$ret = fgets($inputStream, 4096);
if (false === $ret) {
throw new \RuntimeException('Aborted');
}
$ret = trim($ret);
}
} else {
$ret = trim($this->autocomplete($output, $question, $inputStream));
}
$ret = strlen($ret) > 0 ? $ret : $question->getDefault();
if ($normalizer = $question->getNormalizer()) {
return $normalizer($ret);
}
return $ret;
}
/**
* Autocompletes a question.
*
* @param OutputInterface $output
* @param Question $question
*
* @return string
*/
private function autocomplete(OutputInterface $output, Question $question, $inputStream)
{
$autocomplete = $question->getAutocompleterValues();
$ret = '';
$i = 0;
$ofs = -1;
$matches = $autocomplete;
$numMatches = count($matches);
$sttyMode = shell_exec('stty -g');
// Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
shell_exec('stty -icanon -echo');
// Add highlighted text style
$output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
// Read a keypress
while (!feof($inputStream)) {
$c = fread($inputStream, 1);
// Backspace Character
if ("\177" === $c) {
if (0 === $numMatches && 0 !== $i) {
$i--;
// Move cursor backwards
$output->write("\033[1D");
}
if ($i === 0) {
$ofs = -1;
$matches = $autocomplete;
$numMatches = count($matches);
} else {
$numMatches = 0;
}
// Pop the last character off the end of our string
$ret = substr($ret, 0, $i);
} elseif ("\033" === $c) {
// Did we read an escape sequence?
$c .= fread($inputStream, 2);
// A = Up Arrow. B = Down Arrow
if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
if ('A' === $c[2] && -1 === $ofs) {
$ofs = 0;
}
if (0 === $numMatches) {
continue;
}
$ofs += ('A' === $c[2]) ? -1 : 1;
$ofs = ($numMatches + $ofs) % $numMatches;
}
} elseif (ord($c) < 32) {
if ("\t" === $c || "\n" === $c) {
if ($numMatches > 0 && -1 !== $ofs) {
$ret = $matches[$ofs];
// Echo out remaining chars for current match
$output->write(substr($ret, $i));
$i = strlen($ret);
}
if ("\n" === $c) {
$output->write($c);
break;
}
$numMatches = 0;
}
continue;
} else {
$output->write($c);
$ret .= $c;
$i++;
$numMatches = 0;
$ofs = 0;
foreach ($autocomplete as $value) {
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
if (0 === strpos($value, $ret) && $i !== strlen($value)) {
$matches[$numMatches++] = $value;
}
}
}
// Erase characters from cursor to end of line
$output->write("\033[K");
if ($numMatches > 0 && -1 !== $ofs) {
// Save cursor position
$output->write("\0337");
// Write highlighted text
$output->write('<hl>'.substr($matches[$ofs], $i).'</hl>');
// Restore cursor position
$output->write("\0338");
}
}
// Reset stty so it behaves normally again
shell_exec(sprintf('stty %s', $sttyMode));
return $ret;
}
/**
* Gets a hidden response from user.
*
* @param OutputInterface $output An Output instance
*
* @return string The answer
*
* @throws \RuntimeException In case the fallback is deactivated and the response cannot be hidden
*/
private function getHiddenResponse(OutputInterface $output, $inputStream)
{
if ('\\' === DIRECTORY_SEPARATOR) {
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
// handle code running from a phar
if ('phar:' === substr(__FILE__, 0, 5)) {
$tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
copy($exe, $tmpExe);
$exe = $tmpExe;
}
$value = rtrim(shell_exec($exe));
$output->writeln('');
if (isset($tmpExe)) {
unlink($tmpExe);
}
return $value;
}
if ($this->hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
shell_exec('stty -echo');
$value = fgets($inputStream, 4096);
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new \RuntimeException('Aborted');
}
$value = trim($value);
$output->writeln('');
return $value;
}
if (false !== $shell = $this->getShell()) {
$readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword';
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
$value = rtrim(shell_exec($command));
$output->writeln('');
return $value;
}
throw new \RuntimeException('Unable to hide the response.');
}
/**
* Validates an attempt.
*
* @param callable $interviewer A callable that will ask for a question and return the result
* @param OutputInterface $output An Output instance
* @param Question $question A Question instance
*
* @return string The validated response
*
* @throws \Exception In case the max number of attempts has been reached and no valid response has been given
*/
private function validateAttempts($interviewer, OutputInterface $output, Question $question)
{
$error = null;
$attempts = $question->getMaxAttempts();
while (null === $attempts || $attempts--) {
if (null !== $error) {
if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
$message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
} else {
$message = '<error>'.$error->getMessage().'</error>';
}
$output->writeln($message);
}
try {
return call_user_func($question->getValidator(), $interviewer());
} catch (\Exception $error) {
}
}
throw $error;
}
/**
* Returns a valid unix shell.
*
* @return string|bool The valid shell name, false in case no valid shell is found
*/
private function getShell()
{
if (null !== self::$shell) {
return self::$shell;
}
self::$shell = false;
if (file_exists('/usr/bin/env')) {
// handle other OSs with bash/zsh/ksh/csh if available to hide the answer
$test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
self::$shell = $sh;
break;
}
}
}
return self::$shell;
}
/**
* Returns whether Stty is available or not.
*
* @return bool
*/
private function hasSttyAvailable()
{
if (null !== self::$stty) {
return self::$stty;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = $exitcode === 0;
}
}

View file

@ -0,0 +1,410 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Provides helpers to display a table.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Саша Стаменковић <umpirsky@gmail.com>
*/
class Table
{
/**
* Table headers.
*
* @var array
*/
private $headers = array();
/**
* Table rows.
*
* @var array
*/
private $rows = array();
/**
* Column widths cache.
*
* @var array
*/
private $columnWidths = array();
/**
* Number of columns cache.
*
* @var array
*/
private $numberOfColumns;
/**
* @var OutputInterface
*/
private $output;
/**
* @var TableStyle
*/
private $style;
private static $styles;
public function __construct(OutputInterface $output)
{
$this->output = $output;
if (!self::$styles) {
self::$styles = self::initStyles();
}
$this->setStyle('default');
}
/**
* Sets a style definition.
*
* @param string $name The style name
* @param TableStyle $style A TableStyle instance
*/
public static function setStyleDefinition($name, TableStyle $style)
{
if (!self::$styles) {
self::$styles = self::initStyles();
}
self::$styles[$name] = $style;
}
/**
* Gets a style definition by name.
*
* @param string $name The style name
*
* @return TableStyle A TableStyle instance
*/
public static function getStyleDefinition($name)
{
if (!self::$styles) {
self::$styles = self::initStyles();
}
if (!self::$styles[$name]) {
throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
return self::$styles[$name];
}
/**
* Sets table style.
*
* @param TableStyle|string $name The style name or a TableStyle instance
*
* @return Table
*/
public function setStyle($name)
{
if ($name instanceof TableStyle) {
$this->style = $name;
} elseif (isset(self::$styles[$name])) {
$this->style = self::$styles[$name];
} else {
throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
return $this;
}
/**
* Gets the current table style.
*
* @return TableStyle
*/
public function getStyle()
{
return $this->style;
}
public function setHeaders(array $headers)
{
$this->headers = array_values($headers);
return $this;
}
public function setRows(array $rows)
{
$this->rows = array();
return $this->addRows($rows);
}
public function addRows(array $rows)
{
foreach ($rows as $row) {
$this->addRow($row);
}
return $this;
}
public function addRow($row)
{
if ($row instanceof TableSeparator) {
$this->rows[] = $row;
return $this;
}
if (!is_array($row)) {
throw new \InvalidArgumentException('A row must be an array or a TableSeparator instance.');
}
$this->rows[] = array_values($row);
end($this->rows);
$rowKey = key($this->rows);
reset($this->rows);
foreach ($row as $key => $cellValue) {
if (false === strpos($cellValue, "\n")) {
continue;
}
$lines = explode("\n", $cellValue);
$this->rows[$rowKey][$key] = $lines[0];
unset($lines[0]);
foreach ($lines as $lineKey => $line) {
$nextRowKey = $rowKey + $lineKey + 1;
if (isset($this->rows[$nextRowKey])) {
$this->rows[$nextRowKey][$key] = $line;
} else {
$this->rows[$nextRowKey] = array($key => $line);
}
}
}
return $this;
}
public function setRow($column, array $row)
{
$this->rows[$column] = $row;
return $this;
}
/**
* Renders table to output.
*
* Example:
* +---------------+-----------------------+------------------+
* | ISBN | Title | Author |
* +---------------+-----------------------+------------------+
* | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
* | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
* +---------------+-----------------------+------------------+
*/
public function render()
{
$this->renderRowSeparator();
$this->renderRow($this->headers, $this->style->getCellHeaderFormat());
if (!empty($this->headers)) {
$this->renderRowSeparator();
}
foreach ($this->rows as $row) {
if ($row instanceof TableSeparator) {
$this->renderRowSeparator();
} else {
$this->renderRow($row, $this->style->getCellRowFormat());
}
}
if (!empty($this->rows)) {
$this->renderRowSeparator();
}
$this->cleanup();
}
/**
* Renders horizontal header separator.
*
* Example: +-----+-----------+-------+
*/
private function renderRowSeparator()
{
if (0 === $count = $this->getNumberOfColumns()) {
return;
}
if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) {
return;
}
$markup = $this->style->getCrossingChar();
for ($column = 0; $column < $count; $column++) {
$markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->getColumnWidth($column)).$this->style->getCrossingChar();
}
$this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
}
/**
* Renders vertical column separator.
*/
private function renderColumnSeparator()
{
$this->output->write(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()));
}
/**
* Renders table row.
*
* Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
*
* @param array $row
* @param string $cellFormat
*/
private function renderRow(array $row, $cellFormat)
{
if (empty($row)) {
return;
}
$this->renderColumnSeparator();
for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) {
$this->renderCell($row, $column, $cellFormat);
$this->renderColumnSeparator();
}
$this->output->writeln('');
}
/**
* Renders table cell with padding.
*
* @param array $row
* @param int $column
* @param string $cellFormat
*/
private function renderCell(array $row, $column, $cellFormat)
{
$cell = isset($row[$column]) ? $row[$column] : '';
$width = $this->getColumnWidth($column);
// str_pad won't work properly with multi-byte strings, we need to fix the padding
if (function_exists('mb_strwidth') && false !== $encoding = mb_detect_encoding($cell)) {
$width += strlen($cell) - mb_strwidth($cell, $encoding);
}
$width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
$content = sprintf($this->style->getCellRowContentFormat(), $cell);
$this->output->write(sprintf($cellFormat, str_pad($content, $width, $this->style->getPaddingChar(), $this->style->getPadType())));
}
/**
* Gets number of columns for this table.
*
* @return int
*/
private function getNumberOfColumns()
{
if (null !== $this->numberOfColumns) {
return $this->numberOfColumns;
}
$columns = array(count($this->headers));
foreach ($this->rows as $row) {
$columns[] = count($row);
}
return $this->numberOfColumns = max($columns);
}
/**
* Gets column width.
*
* @param int $column
*
* @return int
*/
private function getColumnWidth($column)
{
if (isset($this->columnWidths[$column])) {
return $this->columnWidths[$column];
}
$lengths = array($this->getCellWidth($this->headers, $column));
foreach ($this->rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
$lengths[] = $this->getCellWidth($row, $column);
}
return $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2;
}
/**
* Gets cell width.
*
* @param array $row
* @param int $column
*
* @return int
*/
private function getCellWidth(array $row, $column)
{
return isset($row[$column]) ? Helper::strlenWithoutDecoration($this->output->getFormatter(), $row[$column]) : 0;
}
/**
* Called after rendering to cleanup cache data.
*/
private function cleanup()
{
$this->columnWidths = array();
$this->numberOfColumns = null;
}
private static function initStyles()
{
$borderless = new TableStyle();
$borderless
->setHorizontalBorderChar('=')
->setVerticalBorderChar(' ')
->setCrossingChar(' ')
;
$compact = new TableStyle();
$compact
->setHorizontalBorderChar('')
->setVerticalBorderChar(' ')
->setCrossingChar('')
->setCellRowContentFormat('%s')
;
return array(
'default' => new TableStyle(),
'borderless' => $borderless,
'compact' => $compact,
);
}
}

View file

@ -12,12 +12,15 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
use InvalidArgumentException;
use Symfony\Component\Console\Output\NullOutput;
/**
* Provides helpers to display table output.
*
* @author Саша Стаменковић <umpirsky@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated Deprecated since 2.5, to be removed in 3.0; use Table instead.
*/
class TableHelper extends Helper
{
@ -26,52 +29,13 @@ class TableHelper extends Helper
const LAYOUT_COMPACT = 2;
/**
* Table headers.
*
* @var array
* @var Table
*/
private $headers = array();
/**
* Table rows.
*
* @var array
*/
private $rows = array();
// Rendering options
private $paddingChar;
private $horizontalBorderChar;
private $verticalBorderChar;
private $crossingChar;
private $cellHeaderFormat;
private $cellRowFormat;
private $cellRowContentFormat;
private $borderFormat;
private $padType;
/**
* Column widths cache.
*
* @var array
*/
private $columnWidths = array();
/**
* Number of columns cache.
*
* @var array
*/
private $numberOfColumns;
/**
* @var OutputInterface
*/
private $output;
private $table;
public function __construct()
{
$this->setLayout(self::LAYOUT_DEFAULT);
$this->table = new Table(new NullOutput());
}
/**
@ -80,55 +44,26 @@ class TableHelper extends Helper
* @param int $layout self::LAYOUT_*
*
* @return TableHelper
*
* @throws \InvalidArgumentException when the table layout is not known
*/
public function setLayout($layout)
{
switch ($layout) {
case self::LAYOUT_BORDERLESS:
$this
->setPaddingChar(' ')
->setHorizontalBorderChar('=')
->setVerticalBorderChar(' ')
->setCrossingChar(' ')
->setCellHeaderFormat('<info>%s</info>')
->setCellRowFormat('%s')
->setCellRowContentFormat(' %s ')
->setBorderFormat('%s')
->setPadType(STR_PAD_RIGHT)
;
$this->table->setStyle('borderless');
break;
case self::LAYOUT_COMPACT:
$this
->setPaddingChar(' ')
->setHorizontalBorderChar('')
->setVerticalBorderChar(' ')
->setCrossingChar('')
->setCellHeaderFormat('<info>%s</info>')
->setCellRowFormat('%s')
->setCellRowContentFormat('%s')
->setBorderFormat('%s')
->setPadType(STR_PAD_RIGHT)
;
$this->table->setStyle('compact');
break;
case self::LAYOUT_DEFAULT:
$this
->setPaddingChar(' ')
->setHorizontalBorderChar('-')
->setVerticalBorderChar('|')
->setCrossingChar('+')
->setCellHeaderFormat('<info>%s</info>')
->setCellRowFormat('%s')
->setCellRowContentFormat(' %s ')
->setBorderFormat('%s')
->setPadType(STR_PAD_RIGHT)
;
$this->table->setStyle('default');
break;
default:
throw new InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout));
break;
throw new \InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout));
};
return $this;
@ -136,60 +71,35 @@ class TableHelper extends Helper
public function setHeaders(array $headers)
{
$this->headers = array_values($headers);
$this->table->setHeaders($headers);
return $this;
}
public function setRows(array $rows)
{
$this->rows = array();
$this->table->setRows($rows);
return $this->addRows($rows);
return $this;
}
public function addRows(array $rows)
{
foreach ($rows as $row) {
$this->addRow($row);
}
$this->table->addRows($rows);
return $this;
}
public function addRow(array $row)
{
$this->rows[] = array_values($row);
$keys = array_keys($this->rows);
$rowKey = array_pop($keys);
foreach ($row as $key => $cellValue) {
if (!strstr($cellValue, "\n")) {
continue;
}
$lines = explode("\n", $cellValue);
$this->rows[$rowKey][$key] = $lines[0];
unset($lines[0]);
foreach ($lines as $lineKey => $line) {
$nextRowKey = $rowKey + $lineKey + 1;
if (isset($this->rows[$nextRowKey])) {
$this->rows[$nextRowKey][$key] = $line;
} else {
$this->rows[$nextRowKey] = array($key => $line);
}
}
}
$this->table->addRow($row);
return $this;
}
public function setRow($column, array $row)
{
$this->rows[$column] = $row;
$this->table->setRow($column, $row);
return $this;
}
@ -203,11 +113,7 @@ class TableHelper extends Helper
*/
public function setPaddingChar($paddingChar)
{
if (!$paddingChar) {
throw new \LogicException('The padding char must not be empty');
}
$this->paddingChar = $paddingChar;
$this->table->getStyle()->setPaddingChar($paddingChar);
return $this;
}
@ -221,7 +127,7 @@ class TableHelper extends Helper
*/
public function setHorizontalBorderChar($horizontalBorderChar)
{
$this->horizontalBorderChar = $horizontalBorderChar;
$this->table->getStyle()->setHorizontalBorderChar($horizontalBorderChar);
return $this;
}
@ -235,7 +141,7 @@ class TableHelper extends Helper
*/
public function setVerticalBorderChar($verticalBorderChar)
{
$this->verticalBorderChar = $verticalBorderChar;
$this->table->getStyle()->setVerticalBorderChar($verticalBorderChar);
return $this;
}
@ -249,7 +155,7 @@ class TableHelper extends Helper
*/
public function setCrossingChar($crossingChar)
{
$this->crossingChar = $crossingChar;
$this->table->getStyle()->setCrossingChar($crossingChar);
return $this;
}
@ -263,7 +169,7 @@ class TableHelper extends Helper
*/
public function setCellHeaderFormat($cellHeaderFormat)
{
$this->cellHeaderFormat = $cellHeaderFormat;
$this->table->getStyle()->setCellHeaderFormat($cellHeaderFormat);
return $this;
}
@ -277,7 +183,7 @@ class TableHelper extends Helper
*/
public function setCellRowFormat($cellRowFormat)
{
$this->cellRowFormat = $cellRowFormat;
$this->table->getStyle()->setCellHeaderFormat($cellRowFormat);
return $this;
}
@ -291,7 +197,7 @@ class TableHelper extends Helper
*/
public function setCellRowContentFormat($cellRowContentFormat)
{
$this->cellRowContentFormat = $cellRowContentFormat;
$this->table->getStyle()->setCellRowContentFormat($cellRowContentFormat);
return $this;
}
@ -305,7 +211,7 @@ class TableHelper extends Helper
*/
public function setBorderFormat($borderFormat)
{
$this->borderFormat = $borderFormat;
$this->table->getStyle()->setBorderFormat($borderFormat);
return $this;
}
@ -313,13 +219,13 @@ class TableHelper extends Helper
/**
* Sets cell padding type.
*
* @param integer $padType STR_PAD_*
* @param int $padType STR_PAD_*
*
* @return TableHelper
*/
public function setPadType($padType)
{
$this->padType = $padType;
$this->table->getStyle()->setPadType($padType);
return $this;
}
@ -340,178 +246,15 @@ class TableHelper extends Helper
*/
public function render(OutputInterface $output)
{
$this->output = $output;
$p = new \ReflectionProperty($this->table, 'output');
$p->setAccessible(true);
$p->setValue($this->table, $output);
$this->renderRowSeparator();
$this->renderRow($this->headers, $this->cellHeaderFormat);
if (!empty($this->headers)) {
$this->renderRowSeparator();
}
foreach ($this->rows as $row) {
$this->renderRow($row, $this->cellRowFormat);
}
if (!empty($this->rows)) {
$this->renderRowSeparator();
}
$this->cleanup();
$this->table->render();
}
/**
* Renders horizontal header separator.
*
* Example: +-----+-----------+-------+
*/
private function renderRowSeparator()
{
if (0 === $count = $this->getNumberOfColumns()) {
return;
}
if (!$this->horizontalBorderChar && !$this->crossingChar) {
return;
}
$markup = $this->crossingChar;
for ($column = 0; $column < $count; $column++) {
$markup .= str_repeat($this->horizontalBorderChar, $this->getColumnWidth($column)).$this->crossingChar;
}
$this->output->writeln(sprintf($this->borderFormat, $markup));
}
/**
* Renders vertical column separator.
*/
private function renderColumnSeparator()
{
$this->output->write(sprintf($this->borderFormat, $this->verticalBorderChar));
}
/**
* Renders table row.
*
* Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
*
* @param array $row
* @param string $cellFormat
*/
private function renderRow(array $row, $cellFormat)
{
if (empty($row)) {
return;
}
$this->renderColumnSeparator();
for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) {
$this->renderCell($row, $column, $cellFormat);
$this->renderColumnSeparator();
}
$this->output->writeln('');
}
/**
* Renders table cell with padding.
*
* @param array $row
* @param integer $column
* @param string $cellFormat
*/
private function renderCell(array $row, $column, $cellFormat)
{
$cell = isset($row[$column]) ? $row[$column] : '';
$width = $this->getColumnWidth($column);
// str_pad won't work properly with multi-byte strings, we need to fix the padding
if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($cell)) {
$width += strlen($cell) - mb_strlen($cell, $encoding);
}
$width += $this->strlen($cell) - $this->computeLengthWithoutDecoration($cell);
$content = sprintf($this->cellRowContentFormat, $cell);
$this->output->write(sprintf($cellFormat, str_pad($content, $width, $this->paddingChar, $this->padType)));
}
/**
* Gets number of columns for this table.
*
* @return int
*/
private function getNumberOfColumns()
{
if (null !== $this->numberOfColumns) {
return $this->numberOfColumns;
}
$columns = array(0);
$columns[] = count($this->headers);
foreach ($this->rows as $row) {
$columns[] = count($row);
}
return $this->numberOfColumns = max($columns);
}
/**
* Gets column width.
*
* @param integer $column
*
* @return int
*/
private function getColumnWidth($column)
{
if (isset($this->columnWidths[$column])) {
return $this->columnWidths[$column];
}
$lengths = array(0);
$lengths[] = $this->getCellWidth($this->headers, $column);
foreach ($this->rows as $row) {
$lengths[] = $this->getCellWidth($row, $column);
}
return $this->columnWidths[$column] = max($lengths) + strlen($this->cellRowContentFormat) - 2;
}
/**
* Gets cell width.
*
* @param array $row
* @param integer $column
*
* @return int
*/
private function getCellWidth(array $row, $column)
{
return isset($row[$column]) ? $this->computeLengthWithoutDecoration($row[$column]) : 0;
}
/**
* Called after rendering to cleanup cache data.
*/
private function cleanup()
{
$this->columnWidths = array();
$this->numberOfColumns = null;
}
private function computeLengthWithoutDecoration($string)
{
$formatter = $this->output->getFormatter();
$isDecorated = $formatter->isDecorated();
$formatter->setDecorated(false);
$string = $formatter->format($string);
$formatter->setDecorated($isDecorated);
return $this->strlen($string);
}
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getName()
{

View file

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
/**
* Marks a row as being a separator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TableSeparator
{
}

View file

@ -0,0 +1,251 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
/**
* Defines the styles for a Table.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Саша Стаменковић <umpirsky@gmail.com>
*/
class TableStyle
{
private $paddingChar = ' ';
private $horizontalBorderChar = '-';
private $verticalBorderChar = '|';
private $crossingChar = '+';
private $cellHeaderFormat = '<info>%s</info>';
private $cellRowFormat = '%s';
private $cellRowContentFormat = ' %s ';
private $borderFormat = '%s';
private $padType = STR_PAD_RIGHT;
/**
* Sets padding character, used for cell padding.
*
* @param string $paddingChar
*
* @return TableStyle
*/
public function setPaddingChar($paddingChar)
{
if (!$paddingChar) {
throw new \LogicException('The padding char must not be empty');
}
$this->paddingChar = $paddingChar;
return $this;
}
/**
* Gets padding character, used for cell padding.
*
* @return string
*/
public function getPaddingChar()
{
return $this->paddingChar;
}
/**
* Sets horizontal border character.
*
* @param string $horizontalBorderChar
*
* @return TableStyle
*/
public function setHorizontalBorderChar($horizontalBorderChar)
{
$this->horizontalBorderChar = $horizontalBorderChar;
return $this;
}
/**
* Gets horizontal border character.
*
* @return string
*/
public function getHorizontalBorderChar()
{
return $this->horizontalBorderChar;
}
/**
* Sets vertical border character.
*
* @param string $verticalBorderChar
*
* @return TableStyle
*/
public function setVerticalBorderChar($verticalBorderChar)
{
$this->verticalBorderChar = $verticalBorderChar;
return $this;
}
/**
* Gets vertical border character.
*
* @return string
*/
public function getVerticalBorderChar()
{
return $this->verticalBorderChar;
}
/**
* Sets crossing character.
*
* @param string $crossingChar
*
* @return TableStyle
*/
public function setCrossingChar($crossingChar)
{
$this->crossingChar = $crossingChar;
return $this;
}
/**
* Gets crossing character.
*
* @return string $crossingChar
*/
public function getCrossingChar()
{
return $this->crossingChar;
}
/**
* Sets header cell format.
*
* @param string $cellHeaderFormat
*
* @return TableStyle
*/
public function setCellHeaderFormat($cellHeaderFormat)
{
$this->cellHeaderFormat = $cellHeaderFormat;
return $this;
}
/**
* Gets header cell format.
*
* @return string
*/
public function getCellHeaderFormat()
{
return $this->cellHeaderFormat;
}
/**
* Sets row cell format.
*
* @param string $cellRowFormat
*
* @return TableStyle
*/
public function setCellRowFormat($cellRowFormat)
{
$this->cellRowFormat = $cellRowFormat;
return $this;
}
/**
* Gets row cell format.
*
* @return string
*/
public function getCellRowFormat()
{
return $this->cellRowFormat;
}
/**
* Sets row cell content format.
*
* @param string $cellRowContentFormat
*
* @return TableStyle
*/
public function setCellRowContentFormat($cellRowContentFormat)
{
$this->cellRowContentFormat = $cellRowContentFormat;
return $this;
}
/**
* Gets row cell content format.
*
* @return string
*/
public function getCellRowContentFormat()
{
return $this->cellRowContentFormat;
}
/**
* Sets table border format.
*
* @param string $borderFormat
*
* @return TableStyle
*/
public function setBorderFormat($borderFormat)
{
$this->borderFormat = $borderFormat;
return $this;
}
/**
* Gets table border format.
*
* @return string
*/
public function getBorderFormat()
{
return $this->borderFormat;
}
/**
* Sets cell padding type.
*
* @param int $padType STR_PAD_*
*
* @return TableStyle
*/
public function setPadType($padType)
{
$this->padType = $padType;
return $this;
}
/**
* Gets cell padding type.
*
* @return int
*/
public function getPadType()
{
return $this->padType;
}
}

View file

@ -33,8 +33,8 @@ namespace Symfony\Component\Console\Input;
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
* @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
* @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
* @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
*
* @api
*/
@ -123,7 +123,7 @@ class ArgvInput extends Input
private function parseShortOptionSet($name)
{
$len = strlen($name);
for ($i = 0; $i < $len; $i++) {
for ($i = 0; $i < $len; ++$i) {
if (!$this->definition->hasShortcut($name[$i])) {
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
}
@ -169,7 +169,7 @@ class ArgvInput extends Input
// if input is expecting another argument, add it
if ($this->definition->hasArgument($c)) {
$arg = $this->definition->getArgument($c);
$this->arguments[$arg->getName()] = $arg->isArray()? array($token) : $token;
$this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token;
// if last argument isArray(), append token to last argument
} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
@ -221,7 +221,7 @@ class ArgvInput extends Input
}
if (null !== $value && !$option->acceptValue()) {
throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
if (null === $value && $option->acceptValue() && count($this->parsed)) {
@ -278,7 +278,7 @@ class ArgvInput extends Input
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
* @return bool true if the value is contained in the raw parameters
*/
public function hasParameterOption($values)
{
@ -309,9 +309,11 @@ class ArgvInput extends Input
public function getParameterOption($values, $default = false)
{
$values = (array) $values;
$tokens = $this->tokens;
while ($token = array_shift($tokens)) {
while (0 < count($tokens)) {
$token = array_shift($tokens);
foreach ($values as $value) {
if ($token === $value || 0 === strpos($token, $value.'=')) {
if (false !== $pos = strpos($token, '=')) {
@ -327,7 +329,7 @@ class ArgvInput extends Input
}
/**
* Returns a stringified representation of the args passed to the command
* Returns a stringified representation of the args passed to the command.
*
* @return string
*/
@ -336,7 +338,7 @@ class ArgvInput extends Input
$self = $this;
$tokens = array_map(function ($token) use ($self) {
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
return $match[1] . $self->escapeToken($match[2]);
return $match[1].$self->escapeToken($match[2]);
}
if ($token && $token[0] !== '-') {

View file

@ -65,7 +65,7 @@ class ArrayInput extends Input
*
* @param string|array $values The values to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
* @return bool true if the value is contained in the raw parameters
*/
public function hasParameterOption($values)
{
@ -100,8 +100,10 @@ class ArrayInput extends Input
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if (is_int($k) && in_array($v, $values)) {
return true;
if (is_int($k)) {
if (in_array($v, $values)) {
return true;
}
} elseif (in_array($k, $values)) {
return $v;
}
@ -111,7 +113,7 @@ class ArrayInput extends Input
}
/**
* Returns a stringified representation of the args passed to the command
* Returns a stringified representation of the args passed to the command.
*
* @return string
*/
@ -120,7 +122,7 @@ class ArrayInput extends Input
$params = array();
foreach ($this->parameters as $param => $val) {
if ($param && '-' === $param[0]) {
$params[] = $param . ('' != $val ? '='.$this->escapeToken($val) : '');
$params[] = $param.('' != $val ? '='.$this->escapeToken($val) : '');
} else {
$params[] = $this->escapeToken($val);
}

View file

@ -81,7 +81,7 @@ abstract class Input implements InputInterface
/**
* Checks if the input is interactive.
*
* @return Boolean Returns true if the input is interactive
* @return bool Returns true if the input is interactive
*/
public function isInteractive()
{
@ -91,11 +91,11 @@ abstract class Input implements InputInterface
/**
* Sets the input interactivity.
*
* @param Boolean $interactive If the input should be interactive
* @param bool $interactive If the input should be interactive
*/
public function setInteractive($interactive)
{
$this->interactive = (Boolean) $interactive;
$this->interactive = (bool) $interactive;
}
/**
@ -146,9 +146,9 @@ abstract class Input implements InputInterface
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
* @param string|int $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
* @return bool true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name)
{
@ -186,8 +186,8 @@ abstract class Input implements InputInterface
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string|boolean $value The option value
* @param string $name The option name
* @param string|bool $value The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
*/
@ -205,7 +205,7 @@ abstract class Input implements InputInterface
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasOption($name)
{
@ -213,7 +213,7 @@ abstract class Input implements InputInterface
}
/**
* Escapes a token through escapeshellarg if it contains unsafe chars
* Escapes a token through escapeshellarg if it contains unsafe chars.
*
* @param string $token
*

View file

@ -32,10 +32,10 @@ class InputArgument
/**
* Constructor.
*
* @param string $name The argument name
* @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for self::OPTIONAL mode only)
* @param string $name The argument name
* @param int $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for self::OPTIONAL mode only)
*
* @throws \InvalidArgumentException When argument mode is not valid
*
@ -49,8 +49,8 @@ class InputArgument
throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->mode = $mode;
$this->name = $name;
$this->mode = $mode;
$this->description = $description;
$this->setDefault($default);
@ -69,7 +69,7 @@ class InputArgument
/**
* Returns true if the argument is required.
*
* @return Boolean true if parameter mode is self::REQUIRED, false otherwise
* @return bool true if parameter mode is self::REQUIRED, false otherwise
*/
public function isRequired()
{
@ -79,7 +79,7 @@ class InputArgument
/**
* Returns true if the argument can take multiple values.
*
* @return Boolean true if mode is self::IS_ARRAY, false otherwise
* @return bool true if mode is self::IS_ARRAY, false otherwise
*/
public function isArray()
{

View file

@ -11,11 +11,6 @@
namespace Symfony\Component\Console\Input;
if (!defined('JSON_UNESCAPED_UNICODE')) {
define('JSON_UNESCAPED_SLASHES', 64);
define('JSON_UNESCAPED_UNICODE', 256);
}
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Output\BufferedOutput;
@ -87,9 +82,9 @@ class InputDefinition
*/
public function setArguments($arguments = array())
{
$this->arguments = array();
$this->requiredCount = 0;
$this->hasOptional = false;
$this->arguments = array();
$this->requiredCount = 0;
$this->hasOptional = false;
$this->hasAnArrayArgument = false;
$this->addArguments($arguments);
}
@ -149,7 +144,7 @@ class InputDefinition
/**
* Returns an InputArgument by name or by position.
*
* @param string|integer $name The InputArgument name or position
* @param string|int $name The InputArgument name or position
*
* @return InputArgument An InputArgument object
*
@ -171,9 +166,9 @@ class InputDefinition
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
* @param string|int $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
* @return bool true if the InputArgument object exists, false otherwise
*
* @api
*/
@ -199,7 +194,7 @@ class InputDefinition
/**
* Returns the number of InputArguments.
*
* @return integer The number of InputArguments
* @return int The number of InputArguments
*/
public function getArgumentCount()
{
@ -209,7 +204,7 @@ class InputDefinition
/**
* Returns the number of required InputArguments.
*
* @return integer The number of required InputArguments
* @return int The number of required InputArguments
*/
public function getArgumentRequiredCount()
{
@ -315,7 +310,7 @@ class InputDefinition
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
* @return bool true if the InputOption object exists, false otherwise
*
* @api
*/
@ -341,7 +336,7 @@ class InputDefinition
*
* @param string $name The InputOption shortcut
*
* @return Boolean true if the InputOption object exists, false otherwise
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasShortcut($name)
{
@ -436,7 +431,7 @@ class InputDefinition
/**
* Returns an XML representation of the InputDefinition.
*
* @param Boolean $asDom Whether to return a DOM or an XML string
* @param bool $asDom Whether to return a DOM or an XML string
*
* @return string|\DOMDocument An XML string representing the InputDefinition
*

View file

@ -33,7 +33,7 @@ interface InputInterface
*
* @param string|array $values The values to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
* @return bool true if the value is contained in the raw parameters
*/
public function hasParameterOption($values);
@ -95,9 +95,9 @@ interface InputInterface
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
* @param string|int $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
* @return bool true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name);
@ -120,8 +120,8 @@ interface InputInterface
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string|boolean $value The option value
* @param string $name The option name
* @param string|bool $value The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
*/
@ -132,21 +132,21 @@ interface InputInterface
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasOption($name);
/**
* Is this input means interactive?
*
* @return Boolean
* @return bool
*/
public function isInteractive();
/**
* Sets the input interactivity.
*
* @param Boolean $interactive If the input should be interactive
* @param bool $interactive If the input should be interactive
*/
public function setInteractive($interactive);
}

View file

@ -20,7 +20,7 @@ namespace Symfony\Component\Console\Input;
*/
class InputOption
{
const VALUE_NONE = 1;
const VALUE_NONE = 1;
const VALUE_REQUIRED = 2;
const VALUE_OPTIONAL = 4;
const VALUE_IS_ARRAY = 8;
@ -36,7 +36,7 @@ class InputOption
*
* @param string $name The option name
* @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @param integer $mode The option mode: One of the VALUE_* constants
* @param int $mode The option mode: One of the VALUE_* constants
* @param string $description A description text
* @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE)
*
@ -77,9 +77,9 @@ class InputOption
throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->description = $description;
if ($this->isArray() && !$this->acceptValue()) {
@ -112,7 +112,7 @@ class InputOption
/**
* Returns true if the option accepts a value.
*
* @return Boolean true if value mode is not self::VALUE_NONE, false otherwise
* @return bool true if value mode is not self::VALUE_NONE, false otherwise
*/
public function acceptValue()
{
@ -122,7 +122,7 @@ class InputOption
/**
* Returns true if the option requires a value.
*
* @return Boolean true if value mode is self::VALUE_REQUIRED, false otherwise
* @return bool true if value mode is self::VALUE_REQUIRED, false otherwise
*/
public function isValueRequired()
{
@ -132,7 +132,7 @@ class InputOption
/**
* Returns true if the option takes an optional value.
*
* @return Boolean true if value mode is self::VALUE_OPTIONAL, false otherwise
* @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise
*/
public function isValueOptional()
{
@ -142,7 +142,7 @@ class InputOption
/**
* Returns true if the option can take multiple values.
*
* @return Boolean true if mode is self::VALUE_IS_ARRAY, false otherwise
* @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise
*/
public function isArray()
{
@ -194,10 +194,11 @@ class InputOption
}
/**
* Checks whether the given option equals this one
* Checks whether the given option equals this one.
*
* @param InputOption $option option to compare
* @return Boolean
*
* @return bool
*/
public function equals(InputOption $option)
{

View file

@ -72,9 +72,7 @@ class StringInput extends ArgvInput
$tokens[] = stripcslashes($match[1]);
} else {
// should never happen
// @codeCoverageIgnoreStart
throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
// @codeCoverageIgnoreEnd
}
$cursor += strlen($match[0]);

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2014 Fabien Potencier
Copyright (c) 2004-2015 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -0,0 +1,118 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Logger;
use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
/**
* PSR-3 compliant console logger
*
* @author Kévin Dunglas <dunglas@gmail.com>
* @link http://www.php-fig.org/psr/psr-3/
*/
class ConsoleLogger extends AbstractLogger
{
const INFO = 'info';
const ERROR = 'error';
/**
* @var OutputInterface
*/
private $output;
/**
* @var array
*/
private $verbosityLevelMap = array(
LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE,
LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE,
LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG,
);
/**
* @var array
*/
private $formatLevelMap = array(
LogLevel::EMERGENCY => self::ERROR,
LogLevel::ALERT => self::ERROR,
LogLevel::CRITICAL => self::ERROR,
LogLevel::ERROR => self::ERROR,
LogLevel::WARNING => self::INFO,
LogLevel::NOTICE => self::INFO,
LogLevel::INFO => self::INFO,
LogLevel::DEBUG => self::INFO,
);
/**
* @param OutputInterface $output
* @param array $verbosityLevelMap
* @param array $formatLevelMap
*/
public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array())
{
$this->output = $output;
$this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
$this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
}
/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = array())
{
if (!isset($this->verbosityLevelMap[$level])) {
throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
}
// Write to the error output if necessary and available
if ($this->formatLevelMap[$level] === self::ERROR && $this->output instanceof ConsoleOutputInterface) {
$output = $this->output->getErrorOutput();
} else {
$output = $this->output;
}
if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) {
$output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)));
}
}
/**
* Interpolates context values into the message placeholders
*
* @author PHP Framework Interoperability Group
*
* @param string $message
* @param array $context
*
* @return string
*/
private function interpolate($message, array $context)
{
// build a replacement array with braces around the context keys
$replace = array();
foreach ($context as $key => $val) {
if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
$replace[sprintf('{%s}', $key)] = $val;
}
}
// interpolate replacement values into the message and return
return strtr($message, $replace);
}
}

View file

@ -30,27 +30,25 @@ use Symfony\Component\Console\Formatter\OutputFormatterInterface;
*/
class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
/**
* @var StreamOutput
*/
private $stderr;
/**
* Constructor.
*
* @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param Boolean|null $decorated Whether to decorate messages (null for auto-guessing)
* @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param bool|null $decorated Whether to decorate messages (null for auto-guessing)
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*
* @api
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
{
$outputStream = 'php://stdout';
if (!$this->hasStdoutSupport()) {
$outputStream = 'php://output';
}
parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);
parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter);
$this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $formatter);
$this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter());
}
/**
@ -100,14 +98,52 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
* Returns true if current environment supports writing console output to
* STDOUT.
*
* IBM iSeries (OS400) exhibits character-encoding issues when writing to
* STDOUT and doesn't properly convert ASCII to EBCDIC, resulting in garbage
* output.
*
* @return boolean
* @return bool
*/
protected function hasStdoutSupport()
{
return ('OS400' != php_uname('s'));
return false === $this->isRunningOS400();
}
/**
* Returns true if current environment supports writing console output to
* STDERR.
*
* @return bool
*/
protected function hasStderrSupport()
{
return false === $this->isRunningOS400();
}
/**
* Checks if current executing environment is IBM iSeries (OS400), which
* doesn't properly convert character-encodings between ASCII to EBCDIC.
*
* @return bool
*/
private function isRunningOS400()
{
return 'OS400' === php_uname('s');
}
/**
* @return resource
*/
private function openOutputStream()
{
$outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output';
return @fopen($outputStream, 'w') ?: fopen('php://output', 'w');
}
/**
* @return resource
*/
private function openErrorStream()
{
$errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output';
return fopen($errorStream, 'w');
}
}

View file

@ -75,6 +75,26 @@ class NullOutput implements OutputInterface
return self::VERBOSITY_QUIET;
}
public function isQuiet()
{
return true;
}
public function isVerbose()
{
return false;
}
public function isVeryVerbose()
{
return false;
}
public function isDebug()
{
return false;
}
/**
* {@inheritdoc}
*/

View file

@ -37,8 +37,8 @@ abstract class Output implements OutputInterface
/**
* Constructor.
*
* @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param Boolean $decorated Whether to decorate messages
* @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param bool $decorated Whether to decorate messages
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*
* @api
@ -158,8 +158,8 @@ abstract class Output implements OutputInterface
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param Boolean $newline Whether to add a newline or not
* @param string $message A message to write to the output
* @param bool $newline Whether to add a newline or not
*/
abstract protected function doWrite($message, $newline);
}

View file

@ -22,22 +22,22 @@ use Symfony\Component\Console\Formatter\OutputFormatterInterface;
*/
interface OutputInterface
{
const VERBOSITY_QUIET = 0;
const VERBOSITY_NORMAL = 1;
const VERBOSITY_VERBOSE = 2;
const VERBOSITY_QUIET = 0;
const VERBOSITY_NORMAL = 1;
const VERBOSITY_VERBOSE = 2;
const VERBOSITY_VERY_VERBOSE = 3;
const VERBOSITY_DEBUG = 4;
const VERBOSITY_DEBUG = 4;
const OUTPUT_NORMAL = 0;
const OUTPUT_RAW = 1;
const OUTPUT_PLAIN = 2;
const OUTPUT_RAW = 1;
const OUTPUT_PLAIN = 2;
/**
* Writes a message to the output.
*
* @param string|array $messages The message as an array of lines or a single string
* @param Boolean $newline Whether to add a newline
* @param integer $type The type of output (one of the OUTPUT constants)
* @param bool $newline Whether to add a newline
* @param int $type The type of output (one of the OUTPUT constants)
*
* @throws \InvalidArgumentException When unknown output type is given
*
@ -49,7 +49,7 @@ interface OutputInterface
* Writes a message to the output and adds a newline at the end.
*
* @param string|array $messages The message as an array of lines of a single string
* @param integer $type The type of output (one of the OUTPUT constants)
* @param int $type The type of output (one of the OUTPUT constants)
*
* @throws \InvalidArgumentException When unknown output type is given
*
@ -60,7 +60,7 @@ interface OutputInterface
/**
* Sets the verbosity of the output.
*
* @param integer $level The level of verbosity (one of the VERBOSITY constants)
* @param int $level The level of verbosity (one of the VERBOSITY constants)
*
* @api
*/
@ -69,7 +69,7 @@ interface OutputInterface
/**
* Gets the current verbosity of the output.
*
* @return integer The current level of verbosity (one of the VERBOSITY constants)
* @return int The current level of verbosity (one of the VERBOSITY constants)
*
* @api
*/
@ -78,7 +78,7 @@ interface OutputInterface
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorate the messages
* @param bool $decorated Whether to decorate the messages
*
* @api
*/
@ -87,7 +87,7 @@ interface OutputInterface
/**
* Gets the decorated flag.
*
* @return Boolean true if the output will decorate messages, false otherwise
* @return bool true if the output will decorate messages, false otherwise
*
* @api
*/
@ -105,7 +105,7 @@ interface OutputInterface
/**
* Returns current output formatter instance.
*
* @return OutputFormatterInterface
* @return OutputFormatterInterface
*
* @api
*/

View file

@ -36,8 +36,8 @@ class StreamOutput extends Output
* Constructor.
*
* @param mixed $stream A stream resource
* @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param Boolean|null $decorated Whether to decorate messages (null for auto-guessing)
* @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param bool|null $decorated Whether to decorate messages (null for auto-guessing)
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*
* @throws \InvalidArgumentException When first argument is not a real stream
@ -75,10 +75,8 @@ class StreamOutput extends Output
protected function doWrite($message, $newline)
{
if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) {
// @codeCoverageIgnoreStart
// should never happen
throw new \RuntimeException('Unable to write output.');
// @codeCoverageIgnoreEnd
}
fflush($this->stream);
@ -92,16 +90,14 @@ class StreamOutput extends Output
* - Windows without Ansicon and ConEmu
* - non tty consoles
*
* @return Boolean true if the stream supports colorization, false otherwise
* @return bool true if the stream supports colorization, false otherwise
*/
protected function hasColorSupport()
{
// @codeCoverageIgnoreStart
if (DIRECTORY_SEPARATOR == '\\') {
if (DIRECTORY_SEPARATOR === '\\') {
return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');
}
return function_exists('posix_isatty') && @posix_isatty($this->stream);
// @codeCoverageIgnoreEnd
}
}

View file

@ -0,0 +1,151 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Question;
/**
* Represents a choice question.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ChoiceQuestion extends Question
{
private $choices;
private $multiselect = false;
private $prompt = ' > ';
private $errorMessage = 'Value "%s" is invalid';
/**
* Constructor.
*
* @param string $question The question to ask to the user
* @param array $choices The list of available choices
* @param mixed $default The default answer to return
*/
public function __construct($question, array $choices, $default = null)
{
parent::__construct($question, $default);
$this->choices = $choices;
$this->setValidator($this->getDefaultValidator());
$this->setAutocompleterValues(array_keys($choices));
}
/**
* Returns available choices.
*
* @return array
*/
public function getChoices()
{
return $this->choices;
}
/**
* Sets multiselect option.
*
* When multiselect is set to true, multiple choices can be answered.
*
* @param bool $multiselect
*
* @return ChoiceQuestion The current instance
*/
public function setMultiselect($multiselect)
{
$this->multiselect = $multiselect;
$this->setValidator($this->getDefaultValidator());
return $this;
}
/**
* Gets the prompt for choices.
*
* @return string
*/
public function getPrompt()
{
return $this->prompt;
}
/**
* Sets the prompt for choices.
*
* @param string $prompt
*
* @return ChoiceQuestion The current instance
*/
public function setPrompt($prompt)
{
$this->prompt = $prompt;
return $this;
}
/**
* Sets the error message for invalid values.
*
* The error message has a string placeholder (%s) for the invalid value.
*
* @param string $errorMessage
*
* @return ChoiceQuestion The current instance
*/
public function setErrorMessage($errorMessage)
{
$this->errorMessage = $errorMessage;
$this->setValidator($this->getDefaultValidator());
return $this;
}
/**
* Returns the default answer validator.
*
* @return callable
*/
private function getDefaultValidator()
{
$choices = $this->choices;
$errorMessage = $this->errorMessage;
$multiselect = $this->multiselect;
return function ($selected) use ($choices, $errorMessage, $multiselect) {
// Collapse all spaces.
$selectedChoices = str_replace(' ', '', $selected);
if ($multiselect) {
// Check for a separated comma values
if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
throw new \InvalidArgumentException(sprintf($errorMessage, $selected));
}
$selectedChoices = explode(',', $selectedChoices);
} else {
$selectedChoices = array($selected);
}
$multiselectChoices = array();
foreach ($selectedChoices as $value) {
if (empty($choices[$value])) {
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
}
$multiselectChoices[] = $choices[$value];
}
if ($multiselect) {
return $multiselectChoices;
}
return $choices[$selected];
};
}
}

View file

@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Question;
/**
* Represents a yes/no question.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConfirmationQuestion extends Question
{
/**
* Constructor.
*
* @param string $question The question to ask to the user
* @param bool $default The default answer to return, true or false
*/
public function __construct($question, $default = true)
{
parent::__construct($question, (bool) $default);
$this->setNormalizer($this->getDefaultNormalizer());
}
/**
* Returns the default answer normalizer.
*
* @return callable
*/
private function getDefaultNormalizer()
{
$default = $this->getDefault();
return function ($answer) use ($default) {
if (is_bool($answer)) {
return $answer;
}
if (false === $default) {
return $answer && 'y' === strtolower($answer[0]);
}
return !$answer || 'y' === strtolower($answer[0]);
};
}
}

View file

@ -0,0 +1,238 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Question;
/**
* Represents a Question.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Question
{
private $question;
private $attempts;
private $hidden = false;
private $hiddenFallback = true;
private $autocompleterValues;
private $validator;
private $default;
private $normalizer;
/**
* Constructor.
*
* @param string $question The question to ask to the user
* @param mixed $default The default answer to return if the user enters nothing
*/
public function __construct($question, $default = null)
{
$this->question = $question;
$this->default = $default;
}
/**
* Returns the question.
*
* @return string
*/
public function getQuestion()
{
return $this->question;
}
/**
* Returns the default answer.
*
* @return mixed
*/
public function getDefault()
{
return $this->default;
}
/**
* Returns whether the user response must be hidden.
*
* @return bool
*/
public function isHidden()
{
return $this->hidden;
}
/**
* Sets whether the user response must be hidden or not.
*
* @param bool $hidden
*
* @return Question The current instance
*
* @throws \LogicException In case the autocompleter is also used
*/
public function setHidden($hidden)
{
if ($this->autocompleterValues) {
throw new \LogicException('A hidden question cannot use the autocompleter.');
}
$this->hidden = (bool) $hidden;
return $this;
}
/**
* In case the response can not be hidden, whether to fallback on non-hidden question or not.
*
* @return bool
*/
public function isHiddenFallback()
{
return $this->hiddenFallback;
}
/**
* Sets whether to fallback on non-hidden question if the response can not be hidden.
*
* @param bool $fallback
*
* @return Question The current instance
*/
public function setHiddenFallback($fallback)
{
$this->hiddenFallback = (bool) $fallback;
return $this;
}
/**
* Gets values for the autocompleter.
*
* @return null|array|\Traversable
*/
public function getAutocompleterValues()
{
return $this->autocompleterValues;
}
/**
* Sets values for the autocompleter.
*
* @param null|array|\Traversable $values
*
* @return Question The current instance
*
* @throws \InvalidArgumentException
* @throws \LogicException
*/
public function setAutocompleterValues($values)
{
if (null !== $values && !is_array($values)) {
if (!$values instanceof \Traversable || $values instanceof \Countable) {
throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.');
}
}
if ($this->hidden) {
throw new \LogicException('A hidden question cannot use the autocompleter.');
}
$this->autocompleterValues = $values;
return $this;
}
/**
* Sets a validator for the question.
*
* @param null|callable $validator
*
* @return Question The current instance
*/
public function setValidator($validator)
{
$this->validator = $validator;
return $this;
}
/**
* Gets the validator for the question.
*
* @return null|callable
*/
public function getValidator()
{
return $this->validator;
}
/**
* Sets the maximum number of attempts.
*
* Null means an unlimited number of attempts.
*
* @param null|int $attempts
*
* @return Question The current instance
*
* @throws \InvalidArgumentException In case the number of attempts is invalid.
*/
public function setMaxAttempts($attempts)
{
if (null !== $attempts && $attempts < 1) {
throw new \InvalidArgumentException('Maximum number of attempts must be a positive value.');
}
$this->attempts = $attempts;
return $this;
}
/**
* Gets the maximum number of attempts.
*
* Null means an unlimited number of attempts.
*
* @return null|int
*/
public function getMaxAttempts()
{
return $this->attempts;
}
/**
* Sets a normalizer for the response.
*
* The normalizer can be a callable (a string), a closure or a class implementing __invoke.
*
* @param string|\Closure $normalizer
*
* @return Question The current instance
*/
public function setNormalizer($normalizer)
{
$this->normalizer = $normalizer;
return $this;
}
/**
* Gets the normalizer for the response.
*
* The normalizer can ba a callable (a string), a closure or a class implementing __invoke.
*
* @return string|\Closure
*/
public function getNormalizer()
{
return $this->normalizer;
}
}

View file

@ -5,10 +5,12 @@ Console eases the creation of beautiful and testable command line interfaces.
The Application object manages the CLI application:
use Symfony\Component\Console\Application;
```php
use Symfony\Component\Console\Application;
$console = new Application();
$console->run();
$console = new Application();
$console->run();
```
The ``run()`` method parses the arguments and options passed on the command
line and executes the right command.
@ -16,23 +18,25 @@ line and executes the right command.
Registering a new command can easily be done via the ``register()`` method,
which returns a ``Command`` instance:
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
```php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
$console
->register('ls')
->setDefinition(array(
new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'),
))
->setDescription('Displays the files in the given directory')
->setCode(function (InputInterface $input, OutputInterface $output) {
$dir = $input->getArgument('dir');
$console
->register('ls')
->setDefinition(array(
new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'),
))
->setDescription('Displays the files in the given directory')
->setCode(function (InputInterface $input, OutputInterface $output) {
$dir = $input->getArgument('dir');
$output->writeln(sprintf('Dir listing for <info>%s</info>', $dir));
})
;
$output->writeln(sprintf('Dir listing for <info>%s</info>', $dir));
})
;
```
You can also register new commands via classes.
@ -46,7 +50,7 @@ Tests
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/Console/
$ composer.phar install
$ composer install
$ phpunit
Third Party
@ -58,6 +62,6 @@ component. Find sources and license at https://github.com/Seldaek/hidden-input.
Resources
---------
[The Console Component](http://symfony.com/doc/current/components/console.html)
[The Console Component](https://symfony.com/doc/current/components/console.html)
[How to create a Console Command](http://symfony.com/doc/current/cookbook/console/console_command.html)
[How to create a Console Command](https://symfony.com/doc/current/cookbook/console/console_command.html)

View file

@ -163,7 +163,7 @@ EOF;
*
* @param string $text The last segment of the entered text
*
* @return Boolean|array A list of guessed strings or true
* @return bool|array A list of guessed strings or true
*/
private function autocompleter($text)
{
@ -206,7 +206,7 @@ EOF;
} else {
$this->output->write($this->getPrompt());
$line = fgets(STDIN, 1024);
$line = (!$line && strlen($line) == 0) ? false : rtrim($line);
$line = (false === $line || '' === $line) ? false : rtrim($line);
}
return $line;
@ -219,7 +219,7 @@ EOF;
public function setProcessIsolation($processIsolation)
{
$this->processIsolation = (Boolean) $processIsolation;
$this->processIsolation = (bool) $processIsolation;
if ($this->processIsolation && !class_exists('Symfony\\Component\\Process\\Process')) {
throw new \RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.');

View file

@ -56,7 +56,7 @@ class ApplicationTester
* @param array $input An array of arguments and options
* @param array $options An array of options
*
* @return integer The command exit code
* @return int The command exit code
*/
public function run(array $input, $options = array())
{
@ -79,7 +79,7 @@ class ApplicationTester
/**
* Gets the display returned by the last execution of the application.
*
* @param Boolean $normalize Whether to normalize end of lines to \n or not
* @param bool $normalize Whether to normalize end of lines to \n or not
*
* @return string The display
*/
@ -119,7 +119,7 @@ class ApplicationTester
/**
* Gets the status code returned by the last execution of the application.
*
* @return integer The status code
* @return int The status code
*/
public function getStatusCode()
{

View file

@ -42,16 +42,16 @@ class CommandTester
/**
* Executes the command.
*
* Available options:
* Available execution options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
*
* @param array $input An array of arguments and options
* @param array $options An array of options
* @param array $input An array of command arguments and options
* @param array $options An array of execution options
*
* @return integer The command exit code
* @return int The command exit code
*/
public function execute(array $input, array $options = array())
{
@ -61,7 +61,7 @@ class CommandTester
&& (null !== $application = $this->command->getApplication())
&& $application->getDefinition()->hasArgument('command')
) {
$input['command'] = $this->command->getName();
$input = array_merge(array('command' => $this->command->getName()), $input);
}
$this->input = new ArrayInput($input);
@ -83,7 +83,7 @@ class CommandTester
/**
* Gets the display returned by the last execution of the command.
*
* @param Boolean $normalize Whether to normalize end of lines to \n or not
* @param bool $normalize Whether to normalize end of lines to \n or not
*
* @return string The display
*/
@ -123,7 +123,7 @@ class CommandTester
/**
* Gets the status code returned by the last execution of the application.
*
* @return integer The status code
* @return int The status code
*/
public function getStatusCode()
{

View file

@ -3,7 +3,7 @@
"type": "library",
"description": "Symfony Console Component",
"keywords": [],
"homepage": "http://symfony.com",
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
@ -12,17 +12,22 @@
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/event-dispatcher": "~2.1"
"symfony/phpunit-bridge": "~2.7",
"symfony/event-dispatcher": "~2.1",
"symfony/process": "~2.1",
"psr/log": "~1.0"
},
"suggest": {
"symfony/event-dispatcher": ""
"symfony/event-dispatcher": "",
"symfony/process": "",
"psr/log": "For using the console logger"
},
"autoload": {
"psr-0": { "Symfony\\Component\\Console\\": "" }
@ -31,7 +36,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "2.6-dev"
}
}
}

View file

@ -1,16 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony Console Component Test Suite">
<directory>./Tests/</directory>