Update to Drupal 8.1.0. For more information, see https://www.drupal.org/drupal-8.1.0-release-notes

This commit is contained in:
Pantheon Automation 2016-04-20 09:56:34 -07:00 committed by Greg Anderson
parent b11a755ba8
commit c0a0d5a94c
6920 changed files with 64395 additions and 57312 deletions

View file

@ -13,6 +13,7 @@ namespace Symfony\Component\Console;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Helper\DebugFormatterHelper;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Helper\QuestionHelper;
@ -38,6 +39,8 @@ use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
@ -309,8 +312,12 @@ class Application
*/
public function getLongVersion()
{
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
if ('UNKNOWN' !== $this->getName()) {
if ('UNKNOWN' !== $this->getVersion()) {
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
}
return sprintf('<info>%s</info>', $this->getName());
}
return '<info>Console Tool</info>';
@ -360,7 +367,7 @@ class Application
}
if (null === $command->getDefinition()) {
throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
}
$this->commands[$command->getName()] = $command;
@ -379,12 +386,12 @@ class Application
*
* @return Command A Command object
*
* @throws \InvalidArgumentException When command name given does not exist
* @throws CommandNotFoundException When command name given does not exist
*/
public function get($name)
{
if (!isset($this->commands[$name])) {
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}
$command = $this->commands[$name];
@ -423,7 +430,7 @@ class Application
public function getNamespaces()
{
$namespaces = array();
foreach ($this->commands as $command) {
foreach ($this->all() as $command) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
foreach ($command->getAliases() as $alias) {
@ -441,7 +448,7 @@ class Application
*
* @return string A registered namespace
*
* @throws \InvalidArgumentException When namespace is incorrect or ambiguous
* @throws CommandNotFoundException When namespace is incorrect or ambiguous
*/
public function findNamespace($namespace)
{
@ -462,12 +469,12 @@ class Application
$message .= implode("\n ", $alternatives);
}
throw new \InvalidArgumentException($message);
throw new CommandNotFoundException($message, $alternatives);
}
$exact = in_array($namespace, $namespaces, true);
if (count($namespaces) > 1 && !$exact) {
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
}
return $exact ? $namespace : reset($namespaces);
@ -483,7 +490,7 @@ class Application
*
* @return Command A Command instance
*
* @throws \InvalidArgumentException When command name is incorrect or ambiguous
* @throws CommandNotFoundException When command name is incorrect or ambiguous
*/
public function find($name)
{
@ -508,7 +515,7 @@ class Application
$message .= implode("\n ", $alternatives);
}
throw new \InvalidArgumentException($message);
throw new CommandNotFoundException($message, $alternatives);
}
// filter out aliases for commands which are already on the list
@ -525,7 +532,7 @@ class Application
if (count($commands) > 1 && !$exact) {
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands));
}
return $this->get($exact ? $name : reset($commands));
@ -631,6 +638,8 @@ class Application
*/
public function renderException($e, $output)
{
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
do {
$title = sprintf(' [%s] ', get_class($e));
@ -653,7 +662,7 @@ class Application
}
}
$messages = array('', '');
$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 - $this->stringWidth($title)))));
foreach ($lines as $line) {
@ -661,12 +670,11 @@ class Application
}
$messages[] = $emptyLine;
$messages[] = '';
$messages[] = '';
$output->writeln($messages, OutputInterface::OUTPUT_RAW);
$output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET);
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$output->writeln('<comment>Exception trace:</comment>');
$output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);
// exception related properties
$trace = $e->getTrace();
@ -684,18 +692,16 @@ class Application
$file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
$output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line));
$output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET);
}
$output->writeln('');
$output->writeln('');
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
} 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(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
}
@ -838,6 +844,14 @@ class Application
return $command->run($input, $output);
}
// bind before the console.command event, so the listeners have access to input options/arguments
try {
$command->mergeApplicationDefinition();
$input->bind($command->getDefinition());
} catch (ExceptionInterface $e) {
// ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
}
$event = new ConsoleCommandEvent($command, $input, $output);
$this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
@ -1066,11 +1080,7 @@ class Application
private function stringWidth($string)
{
if (!function_exists('mb_strwidth')) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return strlen($string);
}
@ -1082,12 +1092,7 @@ class Application
// 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)) {
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return str_split($string, $width);
}

View file

@ -1,6 +1,17 @@
CHANGELOG
=========
2.8.3
-----
* remove readline support from the question helper as it caused issues
2.8.0
-----
* use readline for user input in the question helper when available to allow
the use of arrow keys
2.6.0
-----

View file

@ -13,6 +13,7 @@ namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
@ -21,6 +22,8 @@ use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Base class for all commands.
@ -49,7 +52,7 @@ class Command
*
* @param string|null $name The name of the command; passing null means it must be set in configure()
*
* @throws \LogicException When the command name is empty
* @throws LogicException When the command name is empty
*/
public function __construct($name = null)
{
@ -62,7 +65,7 @@ class Command
$this->configure();
if (!$this->name) {
throw new \LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
}
}
@ -154,13 +157,13 @@ class Command
*
* @return null|int null or 0 if everything went fine, or an error code
*
* @throws \LogicException When this abstract method is not implemented
* @throws LogicException When this abstract method is not implemented
*
* @see setCode()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
throw new \LogicException('You must override the execute() method in the concrete command class.');
throw new LogicException('You must override the execute() method in the concrete command class.');
}
/**
@ -219,7 +222,7 @@ class Command
// bind the input against the command specific arguments/options
try {
$input->bind($this->definition);
} catch (\Exception $e) {
} catch (ExceptionInterface $e) {
if (!$this->ignoreValidationErrors) {
throw $e;
}
@ -269,14 +272,21 @@ class Command
*
* @return Command The current instance
*
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*
* @see execute()
*/
public function setCode($code)
{
if (!is_callable($code)) {
throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.');
throw new InvalidArgumentException('Invalid callable provided to Command::setCode.');
}
if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) {
$r = new \ReflectionFunction($code);
if (null === $r->getClosureThis()) {
$code = \Closure::bind($code, $this);
}
}
$this->code = $code;
@ -380,7 +390,7 @@ class Command
* @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)
* @param mixed $default The default value (must be null for InputOption::VALUE_NONE)
*
* @return Command The current instance
*/
@ -403,7 +413,7 @@ class Command
*
* @return Command The current instance
*
* @throws \InvalidArgumentException When the name is invalid
* @throws InvalidArgumentException When the name is invalid
*/
public function setName($name)
{
@ -520,12 +530,12 @@ class Command
*
* @return Command The current instance
*
* @throws \InvalidArgumentException When an alias is invalid
* @throws InvalidArgumentException When an alias is invalid
*/
public function setAliases($aliases)
{
if (!is_array($aliases) && !$aliases instanceof \Traversable) {
throw new \InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
}
foreach ($aliases as $alias) {
@ -598,7 +608,7 @@ class Command
*
* @return mixed The helper value
*
* @throws \InvalidArgumentException if the helper is not defined
* @throws InvalidArgumentException if the helper is not defined
*/
public function getHelper($name)
{
@ -655,12 +665,12 @@ class Command
*
* @param string $name
*
* @throws \InvalidArgumentException When the name is invalid
* @throws InvalidArgumentException When the name is invalid
*/
private function validateName($name)
{
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
}
}
}

View file

@ -42,7 +42,7 @@ class HelpCommand extends Command
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
))
->setDescription('Displays help for a command')
->setHelp(<<<EOF
->setHelp(<<<'EOF'
The <info>%command.name%</info> command displays help for a given command:
<info>php %command.full_name% list</info>

View file

@ -34,7 +34,7 @@ class ListCommand extends Command
->setName('list')
->setDefinition($this->createDefinition())
->setDescription('Lists commands')
->setHelp(<<<EOF
->setHelp(<<<'EOF'
The <info>%command.name%</info> command lists all commands:
<info>php %command.full_name%</info>

View file

@ -13,6 +13,7 @@ namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
@ -89,12 +90,12 @@ class ApplicationDescription
*
* @return Command
*
* @throws \InvalidArgumentException
* @throws CommandNotFoundException
*/
public function getCommand($name)
{
if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
throw new \InvalidArgumentException(sprintf('Command %s does not exist.', $name));
throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name));
}
return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name];

View file

@ -17,6 +17,7 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
@ -54,7 +55,7 @@ abstract class Descriptor implements DescriptorInterface
$this->describeApplication($object, $options);
break;
default:
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
}
}

View file

@ -173,7 +173,7 @@ class TextDescriptor extends Descriptor
$width = $this->getColumnWidth($description->getCommands());
foreach ($description->getCommands() as $command) {
$this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options);
$this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
$this->writeText("\n");
}
} else {
@ -236,10 +236,10 @@ class TextDescriptor extends Descriptor
private function formatDefaultValue($default)
{
if (PHP_VERSION_ID < 50400) {
return str_replace('\/', '/', json_encode($default));
return str_replace(array('\/', '\\\\'), array('/', '\\'), json_encode($default));
}
return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
}
/**

View file

@ -0,0 +1,43 @@
<?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\Exception;
/**
* Represents an incorrect command name typed in the console.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface
{
private $alternatives;
/**
* @param string $message Exception message to throw.
* @param array $alternatives List of similar defined names.
* @param int $code Exception code.
* @param Exception $previous previous exception used for the exception chaining.
*/
public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->alternatives = $alternatives;
}
/**
* @return array A list of similar defined names.
*/
public function getAlternatives()
{
return $this->alternatives;
}
}

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\Exception;
/**
* ExceptionInterface.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
interface ExceptionInterface
{
}

View file

@ -0,0 +1,19 @@
<?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\Exception;
/**
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

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\Exception;
/**
* Represents an incorrect option name typed in the console.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface
{
}

View file

@ -0,0 +1,19 @@
<?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\Exception;
/**
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class LogicException extends \LogicException implements ExceptionInterface
{
}

View file

@ -0,0 +1,19 @@
<?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\Exception;
/**
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* Formatter class for console output.
*
@ -31,7 +33,15 @@ class OutputFormatter implements OutputFormatterInterface
*/
public static function escape($text)
{
return preg_replace('/([^\\\\]?)</', '$1\\<', $text);
$text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);
if ('\\' === substr($text, -1)) {
$len = strlen($text);
$text = rtrim($text, '\\');
$text .= str_repeat('<<', $len - strlen($text));
}
return $text;
}
/**
@ -106,12 +116,12 @@ class OutputFormatter implements OutputFormatterInterface
*
* @return OutputFormatterStyleInterface
*
* @throws \InvalidArgumentException When style isn't defined
* @throws InvalidArgumentException When style isn't defined
*/
public function getStyle($name)
{
if (!$this->hasStyle($name)) {
throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name));
throw new InvalidArgumentException(sprintf('Undefined style: %s', $name));
}
return $this->styles[strtolower($name)];
@ -129,7 +139,7 @@ class OutputFormatter implements OutputFormatterInterface
$message = (string) $message;
$offset = 0;
$output = '';
$tagRegex = '[a-z][a-z0-9_=;-]*';
$tagRegex = '[a-z][a-z0-9_=;-]*+';
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
@ -164,6 +174,10 @@ class OutputFormatter implements OutputFormatterInterface
$output .= $this->applyCurrentStyle(substr($message, $offset));
if (false !== strpos($output, '<<')) {
return strtr($output, array('\\<' => '<', '<<' => '\\'));
}
return str_replace('\\<', '<', $output);
}

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* Formatter style class for defining styles.
*
@ -77,7 +79,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
*
* @param string|null $color The color name
*
* @throws \InvalidArgumentException When the color name isn't defined
* @throws InvalidArgumentException When the color name isn't defined
*/
public function setForeground($color = null)
{
@ -88,7 +90,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
}
if (!isset(static::$availableForegroundColors[$color])) {
throw new \InvalidArgumentException(sprintf(
throw new InvalidArgumentException(sprintf(
'Invalid foreground color specified: "%s". Expected one of (%s)',
$color,
implode(', ', array_keys(static::$availableForegroundColors))
@ -103,7 +105,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
*
* @param string|null $color The color name
*
* @throws \InvalidArgumentException When the color name isn't defined
* @throws InvalidArgumentException When the color name isn't defined
*/
public function setBackground($color = null)
{
@ -114,7 +116,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
}
if (!isset(static::$availableBackgroundColors[$color])) {
throw new \InvalidArgumentException(sprintf(
throw new InvalidArgumentException(sprintf(
'Invalid background color specified: "%s". Expected one of (%s)',
$color,
implode(', ', array_keys(static::$availableBackgroundColors))
@ -129,12 +131,12 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
*
* @param string $option The option name
*
* @throws \InvalidArgumentException When the option name isn't defined
* @throws InvalidArgumentException When the option name isn't defined
*/
public function setOption($option)
{
if (!isset(static::$availableOptions[$option])) {
throw new \InvalidArgumentException(sprintf(
throw new InvalidArgumentException(sprintf(
'Invalid option specified: "%s". Expected one of (%s)',
$option,
implode(', ', array_keys(static::$availableOptions))
@ -151,12 +153,12 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
*
* @param string $option The option name
*
* @throws \InvalidArgumentException When the option name isn't defined
* @throws InvalidArgumentException When the option name isn't defined
*/
public function unsetOption($option)
{
if (!isset(static::$availableOptions[$option])) {
throw new \InvalidArgumentException(sprintf(
throw new InvalidArgumentException(sprintf(
'Invalid option specified: "%s". Expected one of (%s)',
$option,
implode(', ', array_keys(static::$availableOptions))

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
@ -62,7 +64,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)
{
@ -82,7 +84,7 @@ class OutputFormatterStyleStack
}
}
throw new \InvalidArgumentException('Incorrectly nested style tag found.');
throw new InvalidArgumentException('Incorrectly nested style tag found.');
}
/**

View file

@ -17,6 +17,7 @@ use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* This class adds helper method to describe objects in various formats.
@ -54,7 +55,7 @@ class DescriptorHelper extends Helper
* @param object $object
* @param array $options
*
* @throws \InvalidArgumentException when the given format is not supported
* @throws InvalidArgumentException when the given format is not supported
*/
public function describe(OutputInterface $output, $object, array $options = array())
{
@ -64,7 +65,7 @@ class DescriptorHelper extends Helper
), $options);
if (!isset($this->descriptors[$options['format']])) {
throw new \InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
}
$descriptor = $this->descriptors[$options['format']];

View file

@ -11,6 +11,9 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
@ -48,15 +51,19 @@ class DialogHelper extends InputAwareHelper
*
* @return int|string|array The selected value or values (the key of the choices array)
*
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$width = max(array_map('strlen', array_keys($choices)));
$messages = (array) $question;
foreach ($choices as $key => $value) {
$messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
$messages[] = sprintf(" [<info>%-{$width}s</info>] %s", $key, $value);
}
$output->writeln($messages);
@ -68,7 +75,7 @@ class DialogHelper extends InputAwareHelper
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));
throw new InvalidArgumentException(sprintf($errorMessage, $picked));
}
$selectedChoices = explode(',', $selectedChoices);
} else {
@ -79,7 +86,7 @@ class DialogHelper extends InputAwareHelper
foreach ($selectedChoices as $value) {
if (empty($choices[$value])) {
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
throw new InvalidArgumentException(sprintf($errorMessage, $value));
}
$multiselectChoices[] = $value;
}
@ -104,7 +111,7 @@ class DialogHelper extends InputAwareHelper
*
* @return string The user answer
*
* @throws \RuntimeException If there is no data to read in the input stream
* @throws RuntimeException If there is no data to read in the input stream
*/
public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null)
{
@ -112,6 +119,10 @@ class DialogHelper extends InputAwareHelper
return $default;
}
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$output->write($question);
$inputStream = $this->inputStream ?: STDIN;
@ -119,7 +130,7 @@ class DialogHelper extends InputAwareHelper
if (null === $autocomplete || !$this->hasSttyAvailable()) {
$ret = fgets($inputStream, 4096);
if (false === $ret) {
throw new \RuntimeException('Aborted');
throw new RuntimeException('Aborted');
}
$ret = trim($ret);
} else {
@ -265,10 +276,14 @@ class DialogHelper extends InputAwareHelper
*
* @return string The answer
*
* @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
* @throws RuntimeException In case the fallback is deactivated and the response can not be hidden
*/
public function askHiddenResponse(OutputInterface $output, $question, $fallback = true)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
if ('\\' === DIRECTORY_SEPARATOR) {
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
@ -300,7 +315,7 @@ class DialogHelper extends InputAwareHelper
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new \RuntimeException('Aborted');
throw new RuntimeException('Aborted');
}
$value = trim($value);
@ -323,7 +338,7 @@ class DialogHelper extends InputAwareHelper
return $this->ask($output, $question);
}
throw new \RuntimeException('Unable to hide the response');
throw new RuntimeException('Unable to hide the response');
}
/**
@ -370,8 +385,8 @@ class DialogHelper extends InputAwareHelper
*
* @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
* @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)
{
@ -458,7 +473,7 @@ class DialogHelper extends InputAwareHelper
* @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
* @param int|false $attempts Max number of times to ask before giving up; false will ask infinitely
*
* @return string The validated response
*
@ -466,6 +481,10 @@ class DialogHelper extends InputAwareHelper
*/
private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$e = null;
while (false === $attempts || $attempts--) {
if (null !== $e) {

View file

@ -51,11 +51,7 @@ abstract class Helper implements HelperInterface
*/
public static function strlen($string)
{
if (!function_exists('mb_strwidth')) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return strlen($string);
}

View file

@ -12,6 +12,7 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* HelperSet represents a set of helpers to be used with a command.
@ -70,12 +71,12 @@ class HelperSet implements \IteratorAggregate
*
* @return HelperInterface The helper instance
*
* @throws \InvalidArgumentException if the helper is not defined
* @throws InvalidArgumentException if the helper is not defined
*/
public function get($name)
{
if (!$this->has($name)) {
throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}
if ('dialog' === $name && $this->helpers[$name] instanceof DialogHelper) {

View file

@ -13,6 +13,7 @@ namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\LogicException;
/**
* The ProgressBar provides helpers to display progress output.
@ -67,10 +68,8 @@ class ProgressBar
// 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);
}
// set a reasonable redraw frequency so output isn't flooded
$this->setRedrawFrequency($max / 10);
}
$this->startTime = time();
@ -316,11 +315,11 @@ class ProgressBar
/**
* Sets the redraw frequency.
*
* @param int $freq The frequency in steps
* @param int|float $freq The frequency in steps
*/
public function setRedrawFrequency($freq)
{
$this->redrawFreq = (int) $freq;
$this->redrawFreq = max((int) $freq, 1);
}
/**
@ -346,7 +345,7 @@ class ProgressBar
*
* @param int $step Number of steps to advance
*
* @throws \LogicException
* @throws LogicException
*/
public function advance($step = 1)
{
@ -360,7 +359,7 @@ class ProgressBar
*
* @param int $step The current progress
*
* @throws \LogicException
* @throws LogicException
*/
public function setCurrent($step)
{
@ -384,13 +383,13 @@ class ProgressBar
*
* @param int $step The current progress
*
* @throws \LogicException
* @throws LogicException
*/
public function setProgress($step)
{
$step = (int) $step;
if ($step < $this->step) {
throw new \LogicException('You can\'t regress the progress bar.');
throw new LogicException('You can\'t regress the progress bar.');
}
if ($this->max && $step > $this->max) {
@ -580,7 +579,7 @@ class ProgressBar
},
'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.');
throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
}
if (!$bar->getProgress()) {
@ -593,7 +592,7 @@ class ProgressBar
},
'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.');
throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
}
if (!$bar->getProgress()) {

View file

@ -12,7 +12,9 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\LogicException;
/**
* The Progress class provides helpers to display progress output.
@ -193,6 +195,10 @@ class ProgressHelper extends Helper
*/
public function start(OutputInterface $output, $max = null)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$this->startTime = time();
$this->current = 0;
$this->max = (int) $max;
@ -236,7 +242,7 @@ class ProgressHelper extends Helper
* @param int $step Number of steps to advance
* @param bool $redraw Whether to redraw or not
*
* @throws \LogicException
* @throws LogicException
*/
public function advance($step = 1, $redraw = false)
{
@ -249,18 +255,18 @@ class ProgressHelper extends Helper
* @param int $current The current progress
* @param bool $redraw Whether to redraw or not
*
* @throws \LogicException
* @throws LogicException
*/
public function setCurrent($current, $redraw = false)
{
if (null === $this->startTime) {
throw new \LogicException('You must start the progress bar before calling setCurrent().');
throw new LogicException('You must start the progress bar before calling setCurrent().');
}
$current = (int) $current;
if ($current < $this->current) {
throw new \LogicException('You can\'t regress the progress bar');
throw new LogicException('You can\'t regress the progress bar');
}
if (0 === $this->current) {
@ -282,12 +288,12 @@ class ProgressHelper extends Helper
*
* @param bool $finish Forces the end result
*
* @throws \LogicException
* @throws LogicException
*/
public function display($finish = false)
{
if (null === $this->startTime) {
throw new \LogicException('You must start the progress bar before calling display().');
throw new LogicException('You must start the progress bar before calling display().');
}
$message = $this->format;
@ -315,7 +321,7 @@ class ProgressHelper extends Helper
public function finish()
{
if (null === $this->startTime) {
throw new \LogicException('You must start the progress bar before calling finish().');
throw new LogicException('You must start the progress bar before calling finish().');
}
if (null !== $this->startTime) {

View file

@ -0,0 +1,322 @@
<?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;
/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
class ProgressIndicator
{
private $output;
private $startTime;
private $format;
private $message;
private $indicatorValues;
private $indicatorCurrent;
private $indicatorChangeInterval;
private $indicatorUpdateTime;
private $lastMessagesLength;
private $started = false;
private static $formatters;
private static $formats;
/**
* @param OutputInterface $output
* @param string|null $format Indicator format
* @param int $indicatorChangeInterval Change interval in milliseconds
* @param array|null $indicatorValues Animated indicator characters
*/
public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null)
{
$this->output = $output;
if (null === $format) {
$format = $this->determineBestFormat();
}
if (null === $indicatorValues) {
$indicatorValues = array('-', '\\', '|', '/');
}
$indicatorValues = array_values($indicatorValues);
if (2 > count($indicatorValues)) {
throw new \InvalidArgumentException('Must have at least 2 indicator value characters.');
}
$this->format = self::getFormatDefinition($format);
$this->indicatorChangeInterval = $indicatorChangeInterval;
$this->indicatorValues = $indicatorValues;
$this->startTime = time();
}
/**
* Sets the current indicator message.
*
* @param string|null $message
*/
public function setMessage($message)
{
$this->message = $message;
$this->display();
}
/**
* Gets the current indicator message.
*
* @return string|null
*
* @internal for PHP 5.3 compatibility
*/
public function getMessage()
{
return $this->message;
}
/**
* Gets the progress bar start time.
*
* @return int The progress bar start time
*
* @internal for PHP 5.3 compatibility
*/
public function getStartTime()
{
return $this->startTime;
}
/**
* Gets the current animated indicator character.
*
* @return string
*
* @internal for PHP 5.3 compatibility
*/
public function getCurrentValue()
{
return $this->indicatorValues[$this->indicatorCurrent % count($this->indicatorValues)];
}
/**
* Starts the indicator output.
*
* @param $message
*/
public function start($message)
{
if ($this->started) {
throw new \LogicException('Progress indicator already started.');
}
$this->message = $message;
$this->started = true;
$this->lastMessagesLength = 0;
$this->startTime = time();
$this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;
$this->indicatorCurrent = 0;
$this->display();
}
/**
* Advances the indicator.
*/
public function advance()
{
if (!$this->started) {
throw new \LogicException('Progress indicator has not yet been started.');
}
if (!$this->output->isDecorated()) {
return;
}
$currentTime = $this->getCurrentTimeInMilliseconds();
if ($currentTime < $this->indicatorUpdateTime) {
return;
}
$this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;
++$this->indicatorCurrent;
$this->display();
}
/**
* Finish the indicator with message.
*
* @param $message
*/
public function finish($message)
{
if (!$this->started) {
throw new \LogicException('Progress indicator has not yet been started.');
}
$this->message = $message;
$this->display();
$this->output->writeln('');
$this->started = false;
}
/**
* 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;
}
/**
* 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;
}
private function display()
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}
$self = $this;
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) {
if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
return call_user_func($formatter, $self);
}
return $matches[0];
}, $this->format));
}
private function determineBestFormat()
{
switch ($this->output->getVerbosity()) {
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
case OutputInterface::VERBOSITY_VERBOSE:
return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';
case OutputInterface::VERBOSITY_VERY_VERBOSE:
case OutputInterface::VERBOSITY_DEBUG:
return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi';
default:
return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi';
}
}
/**
* Overwrites a previous message to the output.
*
* @param string $message The message
*/
private function overwrite($message)
{
// append whitespace to match the line's length
if (null !== $this->lastMessagesLength) {
if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $message)) {
$message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
}
}
if ($this->output->isDecorated()) {
$this->output->write("\x0D");
$this->output->write($message);
} else {
$this->output->writeln($message);
}
$this->lastMessagesLength = 0;
$len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $message);
if ($len > $this->lastMessagesLength) {
$this->lastMessagesLength = $len;
}
}
private function getCurrentTimeInMilliseconds()
{
return round(microtime(true) * 1000);
}
private static function initPlaceholderFormatters()
{
return array(
'indicator' => function (ProgressIndicator $indicator) {
return $indicator->getCurrentValue();
},
'message' => function (ProgressIndicator $indicator) {
return $indicator->getMessage();
},
'elapsed' => function (ProgressIndicator $indicator) {
return Helper::formatTime(time() - $indicator->getStartTime());
},
'memory' => function () {
return Helper::formatMemory(memory_get_usage(true));
},
);
}
private static function initFormats()
{
return array(
'normal' => ' %indicator% %message%',
'normal_no_ansi' => ' %message%',
'verbose' => ' %indicator% %message% (%elapsed:6s%)',
'verbose_no_ansi' => ' %message% (%elapsed:6s%)',
'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
);
}
}

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -38,7 +40,7 @@ class QuestionHelper extends Helper
*
* @return string The user answer
*
* @throws \RuntimeException If there is no data to read in the input stream
* @throws RuntimeException If there is no data to read in the input stream
*/
public function ask(InputInterface $input, OutputInterface $output, Question $question)
{
@ -70,12 +72,12 @@ class QuestionHelper extends Helper
*
* @param resource $stream The input stream
*
* @throws \InvalidArgumentException In case the stream is not a resource
* @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.');
throw new InvalidArgumentException('Input stream must be a valid resource.');
}
$this->inputStream = $stream;
@ -162,11 +164,12 @@ class QuestionHelper extends Helper
$message = $question->getQuestion();
if ($question instanceof ChoiceQuestion) {
$width = max(array_map('strlen', array_keys($question->getChoices())));
$maxWidth = max(array_map(array($this, 'strlen'), array_keys($question->getChoices())));
$messages = (array) $question->getQuestion();
foreach ($question->getChoices() as $key => $value) {
$messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
$width = $maxWidth - $this->strlen($key);
$messages[] = ' [<info>'.$key.str_repeat(' ', $width).'</info>] '.$value;
}
$output->writeln($messages);
@ -319,7 +322,7 @@ class QuestionHelper extends Helper
*
* @return string The answer
*
* @throws \RuntimeException In case the fallback is deactivated and the response cannot be hidden
* @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
*/
private function getHiddenResponse(OutputInterface $output, $inputStream)
{
@ -351,7 +354,7 @@ class QuestionHelper extends Helper
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new \RuntimeException('Aborted');
throw new RuntimeException('Aborted');
}
$value = trim($value);
@ -369,7 +372,7 @@ class QuestionHelper extends Helper
return $value;
}
throw new \RuntimeException('Unable to hide the response.');
throw new RuntimeException('Unable to hide the response.');
}
/**

View file

@ -11,6 +11,7 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
@ -32,13 +33,13 @@ class SymfonyQuestionHelper extends QuestionHelper
{
$validator = $question->getValidator();
$question->setValidator(function ($value) use ($validator) {
if (null !== $validator && is_callable($validator)) {
if (null !== $validator) {
$value = $validator($value);
}
// make required
if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) {
throw new \Exception('A value is required.');
throw new LogicException('A value is required.');
}
return $value;

View file

@ -12,6 +12,7 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* Provides helpers to display a table.
@ -19,6 +20,7 @@ use Symfony\Component\Console\Output\OutputInterface;
* @author Fabien Potencier <fabien@symfony.com>
* @author Саша Стаменковић <umpirsky@gmail.com>
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
* @author Max Grigorian <maxakawizard@gmail.com>
*/
class Table
{
@ -60,6 +62,11 @@ class Table
*/
private $style;
/**
* @var array
*/
private $columnStyles = array();
private static $styles;
public function __construct(OutputInterface $output)
@ -102,7 +109,7 @@ class Table
}
if (!self::$styles[$name]) {
throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
return self::$styles[$name];
@ -122,7 +129,7 @@ class Table
} elseif (isset(self::$styles[$name])) {
$this->style = self::$styles[$name];
} else {
throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
return $this;
@ -138,6 +145,47 @@ class Table
return $this->style;
}
/**
* Sets table column style.
*
* @param int $columnIndex Column index
* @param TableStyle|string $name The style name or a TableStyle instance
*
* @return Table
*/
public function setColumnStyle($columnIndex, $name)
{
$columnIndex = intval($columnIndex);
if ($name instanceof TableStyle) {
$this->columnStyles[$columnIndex] = $name;
} elseif (isset(self::$styles[$name])) {
$this->columnStyles[$columnIndex] = self::$styles[$name];
} else {
throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
return $this;
}
/**
* Gets the current style for a column.
*
* If style was not set, it returns the global table style.
*
* @param int $columnIndex Column index
*
* @return TableStyle
*/
public function getColumnStyle($columnIndex)
{
if (isset($this->columnStyles[$columnIndex])) {
return $this->columnStyles[$columnIndex];
}
return $this->getStyle();
}
public function setHeaders(array $headers)
{
$headers = array_values($headers);
@ -175,7 +223,7 @@ class Table
}
if (!is_array($row)) {
throw new \InvalidArgumentException('A row must be an array or a TableSeparator instance.');
throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
}
$this->rows[] = array_values($row);
@ -205,24 +253,26 @@ class Table
public function render()
{
$this->calculateNumberOfColumns();
$this->rows = $this->buildTableRows($this->rows);
$this->headers = $this->buildTableRows($this->headers);
$rows = $this->buildTableRows($this->rows);
$headers = $this->buildTableRows($this->headers);
$this->calculateColumnsWidth(array_merge($headers, $rows));
$this->renderRowSeparator();
if (!empty($this->headers)) {
foreach ($this->headers as $header) {
if (!empty($headers)) {
foreach ($headers as $header) {
$this->renderRow($header, $this->style->getCellHeaderFormat());
$this->renderRowSeparator();
}
}
foreach ($this->rows as $row) {
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
$this->renderRowSeparator();
} else {
$this->renderRow($row, $this->style->getCellRowFormat());
}
}
if (!empty($this->rows)) {
if (!empty($rows)) {
$this->renderRowSeparator();
}
@ -246,7 +296,7 @@ class Table
$markup = $this->style->getCrossingChar();
for ($column = 0; $column < $count; ++$column) {
$markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->getColumnWidth($column)).$this->style->getCrossingChar();
$markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar();
}
$this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
@ -292,25 +342,27 @@ class Table
private function renderCell(array $row, $column, $cellFormat)
{
$cell = isset($row[$column]) ? $row[$column] : '';
$width = $this->getColumnWidth($column);
$width = $this->columnWidths[$column];
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
// add the width of the following columns(numbers of colspan).
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
$width += $this->getColumnSeparatorWidth() + $this->getColumnWidth($nextColumn);
$width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn];
}
}
// 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)) {
if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
$width += strlen($cell) - mb_strwidth($cell, $encoding);
}
$style = $this->getColumnStyle($column);
if ($cell instanceof TableSeparator) {
$this->output->write(sprintf($this->style->getBorderFormat(), str_repeat($this->style->getHorizontalBorderChar(), $width)));
$this->output->write(sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)));
} else {
$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())));
$content = sprintf($style->getCellRowContentFormat(), $cell);
$this->output->write(sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())));
}
}
@ -332,7 +384,7 @@ class Table
$columns[] = $this->getNumberOfColumns($row);
}
return $this->numberOfColumns = max($columns);
$this->numberOfColumns = max($columns);
}
private function buildTableRows($rows)
@ -343,7 +395,6 @@ class Table
// Remove any new line breaks and replace it with a new line
foreach ($rows[$rowKey] as $column => $cell) {
$rows[$rowKey] = $this->fillCells($rows[$rowKey], $column);
if (!strstr($cell, "\n")) {
continue;
}
@ -363,7 +414,7 @@ class Table
$tableRows = array();
foreach ($rows as $rowKey => $row) {
$tableRows[] = $row;
$tableRows[] = $this->fillCells($row);
if (isset($unmergedRows[$rowKey])) {
$tableRows = array_merge($tableRows, $unmergedRows[$rowKey]);
}
@ -429,21 +480,23 @@ class Table
* fill cells for a row that contains colspan > 1.
*
* @param array $row
* @param int $column
*
* @return array
*/
private function fillCells($row, $column)
private function fillCells($row)
{
$cell = $row[$column];
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
// insert empty value into rows at column position
array_splice($row, $position, 0, '');
$newRow = array();
foreach ($row as $column => $cell) {
$newRow[] = $cell;
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
// insert empty value at column position
$newRow[] = '';
}
}
}
return $row;
return $newRow ?: $row;
}
/**
@ -487,7 +540,7 @@ class Table
*
* @param array $row
*
* @return array()
* @return array
*/
private function getRowColumns($row)
{
@ -503,34 +556,29 @@ class Table
}
/**
* Gets column width.
* Calculates columns widths.
*
* @param int $column
*
* @return int
* @param array $rows
*/
private function getColumnWidth($column)
private function calculateColumnsWidth($rows)
{
if (isset($this->columnWidths[$column])) {
return $this->columnWidths[$column];
}
for ($column = 0; $column < $this->numberOfColumns; ++$column) {
$lengths = array();
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
foreach (array_merge($this->headers, $this->rows) as $row) {
if ($row instanceof TableSeparator) {
continue;
$lengths[] = $this->getCellWidth($row, $column);
}
$lengths[] = $this->getCellWidth($row, $column);
$this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2;
}
return $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2;
}
/**
* Gets column width.
*
* @param int $column
*
* @return int
*/
private function getColumnSeparatorWidth()

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
@ -39,7 +41,7 @@ class TableCell
// check option names
if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
throw new \InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff)));
throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff)));
}
$this->options = array_merge($this->options, $options);

View file

@ -13,6 +13,7 @@ namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* Provides helpers to display table output.
@ -50,7 +51,7 @@ class TableHelper extends Helper
*
* @return TableHelper
*
* @throws \InvalidArgumentException when the table layout is not known
* @throws InvalidArgumentException when the table layout is not known
*/
public function setLayout($layout)
{
@ -68,8 +69,8 @@ class TableHelper extends Helper
break;
default:
throw new \InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout));
};
throw new InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout));
}
return $this;
}

View file

@ -11,6 +11,9 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Defines the styles for a Table.
*
@ -39,7 +42,7 @@ class TableStyle
public function setPaddingChar($paddingChar)
{
if (!$paddingChar) {
throw new \LogicException('The padding char must not be empty');
throw new LogicException('The padding char must not be empty');
}
$this->paddingChar = $paddingChar;
@ -235,7 +238,7 @@ class TableStyle
public function setPadType($padType)
{
if (!in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) {
throw new \InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
}
$this->padType = $padType;

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\RuntimeException;
/**
* ArgvInput represents an input coming from the CLI arguments.
*
@ -114,14 +116,14 @@ class ArgvInput extends Input
*
* @param string $name The current token
*
* @throws \RuntimeException When option given doesn't exist
* @throws RuntimeException When option given doesn't exist
*/
private function parseShortOptionSet($name)
{
$len = strlen($name);
for ($i = 0; $i < $len; ++$i) {
if (!$this->definition->hasShortcut($name[$i])) {
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
}
$option = $this->definition->getOptionForShortcut($name[$i]);
@ -156,7 +158,7 @@ class ArgvInput extends Input
*
* @param string $token The current token
*
* @throws \RuntimeException When too many arguments are given
* @throws RuntimeException When too many arguments are given
*/
private function parseArgument($token)
{
@ -174,7 +176,7 @@ class ArgvInput extends Input
// unexpected argument
} else {
throw new \RuntimeException('Too many arguments.');
throw new RuntimeException('Too many arguments.');
}
}
@ -184,12 +186,12 @@ class ArgvInput extends Input
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws \RuntimeException When option given doesn't exist
* @throws RuntimeException When option given doesn't exist
*/
private function addShortOption($shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
@ -201,12 +203,12 @@ class ArgvInput extends Input
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws \RuntimeException When option given doesn't exist
* @throws RuntimeException When option given doesn't exist
*/
private function addLongOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
}
$option = $this->definition->getOption($name);
@ -217,7 +219,7 @@ class ArgvInput extends Input
}
if (null !== $value && !$option->acceptValue()) {
throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
if (null === $value && $option->acceptValue() && count($this->parsed)) {
@ -235,7 +237,7 @@ class ArgvInput extends Input
if (null === $value) {
if ($option->isValueRequired()) {
throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}
if (!$option->isArray()) {

View file

@ -11,6 +11,9 @@
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\InvalidOptionException;
/**
* ArrayInput represents an input provided as an array.
*
@ -149,12 +152,12 @@ class ArrayInput extends Input
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws \InvalidArgumentException When option given doesn't exist
* @throws InvalidOptionException When option given doesn't exist
*/
private function addShortOption($shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
@ -166,20 +169,20 @@ class ArrayInput extends Input
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws \InvalidArgumentException When option given doesn't exist
* @throws \InvalidArgumentException When a required value is missing
* @throws InvalidOptionException When option given doesn't exist
* @throws InvalidOptionException When a required value is missing
*/
private function addLongOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
}
$option = $this->definition->getOption($name);
if (null === $value) {
if ($option->isValueRequired()) {
throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name));
throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name));
}
$value = $option->isValueOptional() ? $option->getDefault() : true;
@ -194,12 +197,12 @@ class ArrayInput extends Input
* @param string $name The argument name
* @param mixed $value The value for the argument
*
* @throws \InvalidArgumentException When argument given doesn't exist
* @throws InvalidArgumentException When argument given doesn't exist
*/
private function addArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;

View file

@ -11,6 +11,9 @@
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
/**
* Input is the base class for all concrete Input classes.
*
@ -69,7 +72,7 @@ abstract class Input implements InputInterface
/**
* Validates the input.
*
* @throws \RuntimeException When not enough arguments are given
* @throws RuntimeException When not enough arguments are given
*/
public function validate()
{
@ -81,7 +84,7 @@ abstract class Input implements InputInterface
});
if (count($missingArguments) > 0) {
throw new \RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
}
}
@ -122,12 +125,12 @@ abstract class Input implements InputInterface
*
* @return mixed The argument value
*
* @throws \InvalidArgumentException When argument given doesn't exist
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
@ -139,12 +142,12 @@ abstract class Input implements InputInterface
* @param string $name The argument name
* @param string $value The argument value
*
* @throws \InvalidArgumentException When argument given doesn't exist
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function setArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
@ -179,12 +182,12 @@ abstract class Input implements InputInterface
*
* @return mixed The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
* @throws InvalidArgumentException When option given doesn't exist
*/
public function getOption($name)
{
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
@ -196,12 +199,12 @@ abstract class Input implements InputInterface
* @param string $name The option name
* @param string|bool $value The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
* @throws InvalidArgumentException When option given doesn't exist
*/
public function setOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
$this->options[$name] = $value;

View file

@ -11,6 +11,9 @@
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Represents a command line argument.
*
@ -35,14 +38,14 @@ class InputArgument
* @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
* @throws InvalidArgumentException When argument mode is not valid
*/
public function __construct($name, $mode = null, $description = '', $default = null)
{
if (null === $mode) {
$mode = self::OPTIONAL;
} elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}
$this->name = $name;
@ -87,19 +90,19 @@ class InputArgument
*
* @param mixed $default The default value
*
* @throws \LogicException When incorrect default value is given
* @throws LogicException When incorrect default value is given
*/
public function setDefault($default = null)
{
if (self::REQUIRED === $this->mode && null !== $default) {
throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = array();
} elseif (!is_array($default)) {
throw new \LogicException('A default value for an array argument must be an array.');
throw new LogicException('A default value for an array argument must be an array.');
}
}

View file

@ -14,6 +14,8 @@ namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* A InputDefinition represents a set of valid command line arguments and options.
@ -100,20 +102,20 @@ class InputDefinition
*
* @param InputArgument $argument An InputArgument object
*
* @throws \LogicException When incorrect argument is given
* @throws LogicException When incorrect argument is given
*/
public function addArgument(InputArgument $argument)
{
if (isset($this->arguments[$argument->getName()])) {
throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
}
if ($this->hasAnArrayArgument) {
throw new \LogicException('Cannot add an argument after an array argument.');
throw new LogicException('Cannot add an argument after an array argument.');
}
if ($argument->isRequired() && $this->hasOptional) {
throw new \LogicException('Cannot add a required argument after an optional one.');
throw new LogicException('Cannot add a required argument after an optional one.');
}
if ($argument->isArray()) {
@ -136,12 +138,12 @@ class InputDefinition
*
* @return InputArgument An InputArgument object
*
* @throws \InvalidArgumentException When argument given doesn't exist
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name)
{
if (!$this->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
@ -237,18 +239,18 @@ class InputDefinition
*
* @param InputOption $option An InputOption object
*
* @throws \LogicException When option given already exist
* @throws LogicException When option given already exist
*/
public function addOption(InputOption $option)
{
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
}
}
}
@ -268,12 +270,12 @@ class InputDefinition
*
* @return InputOption A InputOption object
*
* @throws \InvalidArgumentException When option given doesn't exist
* @throws InvalidArgumentException When option given doesn't exist
*/
public function getOption($name)
{
if (!$this->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
return $this->options[$name];
@ -347,12 +349,12 @@ class InputDefinition
*
* @return string The InputOption name
*
* @throws \InvalidArgumentException When option given does not exist
* @throws InvalidArgumentException When option given does not exist
*/
private function shortcutToName($shortcut)
{
if (!isset($this->shortcuts[$shortcut])) {
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
return $this->shortcuts[$shortcut];

View file

@ -88,7 +88,7 @@ interface InputInterface
* @param string $name The argument name
* @param string $value The argument value
*
* @throws \InvalidArgumentException When argument given doesn't exist
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function setArgument($name, $value);
@ -123,7 +123,7 @@ interface InputInterface
* @param string $name The option name
* @param string|bool $value The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
* @throws InvalidArgumentException When option given doesn't exist
*/
public function setOption($name, $value);

View file

@ -11,6 +11,9 @@
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Represents a command line option.
*
@ -36,9 +39,9 @@ class InputOption
* @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @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)
* @param mixed $default The default value (must be null for self::VALUE_NONE)
*
* @throws \InvalidArgumentException If option mode is invalid or incompatible
* @throws InvalidArgumentException If option mode is invalid or incompatible
*/
public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
@ -47,7 +50,7 @@ class InputOption
}
if (empty($name)) {
throw new \InvalidArgumentException('An option name cannot be empty.');
throw new InvalidArgumentException('An option name cannot be empty.');
}
if (empty($shortcut)) {
@ -63,14 +66,14 @@ class InputOption
$shortcut = implode('|', $shortcuts);
if (empty($shortcut)) {
throw new \InvalidArgumentException('An option shortcut cannot be empty.');
throw new InvalidArgumentException('An option shortcut cannot be empty.');
}
}
if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
$this->name = $name;
@ -79,7 +82,7 @@ class InputOption
$this->description = $description;
if ($this->isArray() && !$this->acceptValue()) {
throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
}
$this->setDefault($default);
@ -150,19 +153,19 @@ class InputOption
*
* @param mixed $default The default value
*
* @throws \LogicException When incorrect default value is given
* @throws LogicException When incorrect default value is given
*/
public function setDefault($default = null)
{
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = array();
} elseif (!is_array($default)) {
throw new \LogicException('A default value for an array option must be an array.');
throw new LogicException('A default value for an array option must be an array.');
}
}

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* StringInput represents an input provided as a string.
*
@ -55,7 +57,7 @@ class StringInput extends ArgvInput
*
* @return array An array of tokens
*
* @throws \InvalidArgumentException When unable to parse input (should never happen)
* @throws InvalidArgumentException When unable to parse input (should never happen)
*/
private function tokenize($input)
{
@ -72,7 +74,7 @@ class StringInput extends ArgvInput
$tokens[] = stripcslashes($match[1]);
} else {
// should never happen
throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
}
$cursor += strlen($match[0]);

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2015 Fabien Potencier
Copyright (c) 2004-2016 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

@ -131,7 +131,7 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
PHP_OS,
);
return false !== stristr(implode(';', $checks), 'OS400');
return false !== stripos(implode(';', $checks), 'OS400');
}
/**

View file

@ -96,7 +96,7 @@ class NullOutput implements OutputInterface
/**
* {@inheritdoc}
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL)
public function writeln($messages, $options = self::OUTPUT_NORMAL)
{
// do nothing
}
@ -104,7 +104,7 @@ class NullOutput implements OutputInterface
/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
{
// do nothing
}

View file

@ -117,22 +117,28 @@ abstract class Output implements OutputInterface
/**
* {@inheritdoc}
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL)
public function writeln($messages, $options = self::OUTPUT_NORMAL)
{
$this->write($messages, true, $type);
$this->write($messages, true, $options);
}
/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
{
if (self::VERBOSITY_QUIET === $this->verbosity) {
$messages = (array) $messages;
$types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN;
$type = $types & $options ?: self::OUTPUT_NORMAL;
$verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG;
$verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL;
if ($verbosity > $this->getVerbosity()) {
return;
}
$messages = (array) $messages;
foreach ($messages as $message) {
switch ($type) {
case OutputInterface::OUTPUT_NORMAL:
@ -143,8 +149,6 @@ abstract class Output implements OutputInterface
case OutputInterface::OUTPUT_PLAIN:
$message = strip_tags($this->formatter->format($message));
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
}
$this->doWrite($message, $newline);

View file

@ -20,36 +20,32 @@ use Symfony\Component\Console\Formatter\OutputFormatterInterface;
*/
interface OutputInterface
{
const VERBOSITY_QUIET = 0;
const VERBOSITY_NORMAL = 1;
const VERBOSITY_VERBOSE = 2;
const VERBOSITY_VERY_VERBOSE = 3;
const VERBOSITY_DEBUG = 4;
const VERBOSITY_QUIET = 16;
const VERBOSITY_NORMAL = 32;
const VERBOSITY_VERBOSE = 64;
const VERBOSITY_VERY_VERBOSE = 128;
const VERBOSITY_DEBUG = 256;
const OUTPUT_NORMAL = 0;
const OUTPUT_RAW = 1;
const OUTPUT_PLAIN = 2;
const OUTPUT_NORMAL = 1;
const OUTPUT_RAW = 2;
const OUTPUT_PLAIN = 4;
/**
* Writes a message to the output.
*
* @param string|array $messages The message as an array of lines or a single string
* @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
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL);
public function write($messages, $newline = false, $options = 0);
/**
* Writes a message to the output and adds a newline at the end.
*
* @param string|array $messages The message as an array of lines or a single string
* @param int $type The type of output (one of the OUTPUT constants)
*
* @throws \InvalidArgumentException When unknown output type is given
* @param string|array $messages The message as an array of lines of a single string
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL);
public function writeln($messages, $options = 0);
/**
* Sets the verbosity of the output.

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
@ -38,12 +40,12 @@ class StreamOutput extends Output
* @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
* @throws InvalidArgumentException When first argument is not a real stream
*/
public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
{
if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
}
$this->stream = $stream;
@ -72,7 +74,7 @@ class StreamOutput extends Output
{
if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) {
// should never happen
throw new \RuntimeException('Unable to write output.');
throw new RuntimeException('Unable to write output.');
}
fflush($this->stream);

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Console\Question;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* Represents a choice question.
*
@ -126,7 +128,7 @@ class ChoiceQuestion extends Question
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));
throw new InvalidArgumentException(sprintf($errorMessage, $selected));
}
$selectedChoices = explode(',', $selectedChoices);
} else {
@ -143,7 +145,7 @@ class ChoiceQuestion extends Question
}
if (count($results) > 1) {
throw new \InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results)));
throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results)));
}
$result = array_search($value, $choices);
@ -159,7 +161,7 @@ class ChoiceQuestion extends Question
}
if (false === $result) {
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
throw new InvalidArgumentException(sprintf($errorMessage, $value));
}
$multiselectChoices[] = (string) $result;

View file

@ -11,6 +11,9 @@
namespace Symfony\Component\Console\Question;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Represents a Question.
*
@ -76,12 +79,12 @@ class Question
*
* @return Question The current instance
*
* @throws \LogicException In case the autocompleter is also used
* @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.');
throw new LogicException('A hidden question cannot use the autocompleter.');
}
$this->hidden = (bool) $hidden;
@ -130,23 +133,23 @@ class Question
*
* @return Question The current instance
*
* @throws \InvalidArgumentException
* @throws \LogicException
* @throws InvalidArgumentException
* @throws LogicException
*/
public function setAutocompleterValues($values)
{
if (is_array($values) && $this->isAssoc($values)) {
$values = array_merge(array_keys($values), array_values($values));
if (is_array($values)) {
$values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($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 (!$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.');
throw new LogicException('A hidden question cannot use the autocompleter.');
}
$this->autocompleterValues = $values;
@ -187,12 +190,12 @@ class Question
*
* @return Question The current instance
*
* @throws \InvalidArgumentException In case the number of attempts is invalid.
* @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.');
throw new InvalidArgumentException('Maximum number of attempts must be a positive value.');
}
$this->attempts = $attempts;

View file

@ -1,67 +1,20 @@
Console Component
=================
Console eases the creation of beautiful and testable command line interfaces.
The Application object manages the CLI application:
```php
use Symfony\Component\Console\Application;
$console = new Application();
$console->run();
```
The ``run()`` method parses the arguments and options passed on the command
line and executes the right command.
Registering a new command can easily be done via the ``register()`` method,
which returns a ``Command`` instance:
```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');
$output->writeln(sprintf('Dir listing for <info>%s</info>', $dir));
})
;
```
You can also register new commands via classes.
The component provides a lot of features like output coloring, input and
output abstractions (so that you can easily unit-test your commands),
validation, automatic help messages, ...
Tests
-----
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/Console/
$ composer install
$ phpunit
Third Party
-----------
`Resources/bin/hiddeninput.exe` is a third party binary provided within this
component. Find sources and license at https://github.com/Seldaek/hidden-input.
The Console component eases the creation of beautiful and testable command line
interfaces.
Resources
---------
[The Console Component](https://symfony.com/doc/current/components/console.html)
* [Documentation](https://symfony.com/doc/current/components/console/index.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
[How to create a Console Command](https://symfony.com/doc/current/cookbook/console/console_command.html)
Credits
-------
`Resources/bin/hiddeninput.exe` is a third party binary provided within this
component. Find sources and license at https://github.com/Seldaek/hidden-input.

View file

@ -11,6 +11,7 @@
namespace Symfony\Component\Console;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Process\ProcessBuilder;
@ -22,6 +23,8 @@ use Symfony\Component\Process\PhpExecutableFinder;
* Support for history and completion only works with a PHP compiled
* with readline support (either --with-readline or --with-libedit)
*
* @deprecated since version 2.8, to be removed in 3.0.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Martin Hasoň <martin.hason@gmail.com>
*/
@ -43,6 +46,8 @@ class Shell
*/
public function __construct(Application $application)
{
@trigger_error('The '.__CLASS__.' class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
$this->hasReadline = function_exists('readline');
$this->application = $application;
$this->history = getenv('HOME').'/.history_'.$application->getName();
@ -67,7 +72,7 @@ class Shell
if ($this->processIsolation) {
$finder = new PhpExecutableFinder();
$php = $finder->find();
$this->output->writeln(<<<EOF
$this->output->writeln(<<<'EOF'
<info>Running with process isolation, you should consider this:</info>
* each command is executed as separate process,
* commands don't support interactivity, all params must be passed explicitly,
@ -222,7 +227,7 @@ EOF;
$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.');
throw new RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.');
}
}
}

View file

@ -12,6 +12,7 @@
namespace Symfony\Component\Console\Style;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\ProgressBar;
@ -109,7 +110,7 @@ class SymfonyStyle extends OutputStyle
$this->autoPrependBlock();
$this->writeln(array(
sprintf('<comment>%s</>', $message),
sprintf('<comment>%s</>', str_repeat('=', strlen($message))),
sprintf('<comment>%s</>', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))),
));
$this->newLine();
}
@ -122,7 +123,7 @@ class SymfonyStyle extends OutputStyle
$this->autoPrependBlock();
$this->writeln(array(
sprintf('<comment>%s</>', $message),
sprintf('<comment>%s</>', str_repeat('-', strlen($message))),
sprintf('<comment>%s</>', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))),
));
$this->newLine();
}
@ -148,14 +149,22 @@ class SymfonyStyle extends OutputStyle
{
$this->autoPrependText();
if (!is_array($message)) {
$this->writeln(sprintf(' // %s', $message));
return;
$messages = is_array($message) ? array_values($message) : array($message);
foreach ($messages as $message) {
$this->writeln(sprintf(' %s', $message));
}
}
foreach ($message as $element) {
$this->text($element);
/**
* {@inheritdoc}
*/
public function comment($message)
{
$this->autoPrependText();
$messages = is_array($message) ? array_values($message) : array($message);
foreach ($messages as $message) {
$this->writeln(sprintf(' // %s', $message));
}
}
@ -294,7 +303,7 @@ class SymfonyStyle extends OutputStyle
{
$progressBar = parent::createProgressBar($max);
if ('\\' === DIRECTORY_SEPARATOR) {
if ('\\' !== DIRECTORY_SEPARATOR) {
$progressBar->setEmptyBarCharacter('░'); // light shade character \u2591
$progressBar->setProgressCharacter('');
$progressBar->setBarCharacter('▓'); // dark shade character \u2593
@ -361,7 +370,7 @@ class SymfonyStyle extends OutputStyle
private function getProgressBar()
{
if (!$this->progressBar) {
throw new \RuntimeException('The ProgressBar is not started.');
throw new RuntimeException('The ProgressBar is not started.');
}
return $this->progressBar;

View file

@ -16,11 +16,12 @@
}
],
"require": {
"php": ">=5.3.9"
"php": ">=5.3.9",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
"symfony/event-dispatcher": "~2.1",
"symfony/process": "~2.1",
"symfony/event-dispatcher": "~2.1|~3.0.0",
"symfony/process": "~2.1|~3.0.0",
"psr/log": "~1.0"
},
"suggest": {
@ -29,12 +30,15 @@
"psr/log": "For using the console logger"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Console\\": "" }
"psr-4": { "Symfony\\Component\\Console\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
"dev-master": "2.8-dev"
}
}
}

View file

@ -26,4 +26,14 @@
</exclude>
</whitelist>
</filter>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener">
<arguments>
<array>
<element key="time-sensitive"><string>Symfony\Component\Console</string></element>
</array>
</arguments>
</listener>
</listeners>
</phpunit>