update 1.0.8.0

Commits for version update
This commit is contained in:
Manish Verma
2016-10-17 12:02:27 +05:30
parent dec927987b
commit 76e85db070
9674 changed files with 495757 additions and 58922 deletions

View File

@@ -210,7 +210,7 @@ class Application
}
/**
* Set an input definition set to be used with this application.
* Set an input definition to be used with this application.
*
* @param InputDefinition $definition The input definition
*/
@@ -232,7 +232,7 @@ class Application
/**
* Gets the help message.
*
* @return string A help message.
* @return string A help message
*/
public function getHelp()
{
@@ -332,6 +332,8 @@ class Application
/**
* Adds an array of command objects.
*
* If a Command is not enabled it will not be added.
*
* @param Command[] $commands An array of commands
*/
public function addCommands(array $commands)
@@ -345,10 +347,11 @@ class Application
* Adds a command object.
*
* If a command with the same name already exists, it will be overridden.
* If the command is not enabled it will not be added.
*
* @param Command $command A Command object
*
* @return Command The registered command
* @return Command|null The registered command if enabled or null
*/
public function add(Command $command)
{
@@ -417,9 +420,9 @@ class Application
/**
* Returns an array of all unique namespaces used by currently registered commands.
*
* It does not returns the global namespace which always exists.
* It does not return the global namespace which always exists.
*
* @return array An array of namespaces
* @return string[] An array of namespaces
*/
public function getNamespaces()
{
@@ -909,7 +912,7 @@ class Application
/**
* Runs and parses mode CON if it's available, suppressing any error output.
*
* @return string <width>x<height> or null if it could not be parsed
* @return string|null <width>x<height> or null if it could not be parsed
*/
private function getConsoleMode()
{
@@ -968,7 +971,7 @@ class Application
* @param string $name The string
* @param array|\Traversable $collection The collection
*
* @return array A sorted array of similar string
* @return string[] A sorted array of similar string
*/
private function findAlternatives($name, $collection)
{
@@ -1067,7 +1070,7 @@ class Application
*
* @param string $name The full name of the command
*
* @return array The namespaces of the command
* @return string[] The namespaces of the command
*/
private function extractAllNamespaces($name)
{

View File

@@ -21,10 +21,10 @@ class CommandNotFoundException extends \InvalidArgumentException implements Exce
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.
* @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)
{
@@ -34,7 +34,7 @@ class CommandNotFoundException extends \InvalidArgumentException implements Exce
}
/**
* @return array A list of similar defined names.
* @return array A list of similar defined names
*/
public function getAlternatives()
{

View File

@@ -21,13 +21,16 @@ use Symfony\Component\Console\Exception\InvalidArgumentException;
*/
class HelperSet implements \IteratorAggregate
{
/**
* @var Helper[]
*/
private $helpers = array();
private $command;
/**
* Constructor.
*
* @param Helper[] $helpers An array of helper.
* @param Helper[] $helpers An array of helper
*/
public function __construct(array $helpers = array())
{
@@ -102,6 +105,9 @@ class HelperSet implements \IteratorAggregate
return $this->command;
}
/**
* @return Helper[]
*/
public function getIterator()
{
return new \ArrayIterator($this->helpers);

View File

@@ -42,8 +42,9 @@ class ProgressBar
private $stepWidth;
private $percent = 0.0;
private $formatLineCount;
private $messages;
private $messages = array();
private $overwrite = true;
private $firstRun = true;
private static $formatters;
private static $formats;
@@ -140,6 +141,16 @@ class ProgressBar
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
}
/**
* Associates a text with a named placeholder.
*
* The text is displayed when the progress bar is rendered but only
* when the corresponding placeholder is part of the custom format line
* (by wrapping the name with %).
*
* @param string $message The text to associate with the placeholder
* @param string $name The name of the placeholder
*/
public function setMessage($message, $name = 'message')
{
$this->messages[$name] = $message;
@@ -477,20 +488,24 @@ class ProgressBar
private function overwrite($message)
{
if ($this->overwrite) {
// Move the cursor to the beginning of the line
$this->output->write("\x0D");
if (!$this->firstRun) {
// Move the cursor to the beginning of the line
$this->output->write("\x0D");
// Erase the line
$this->output->write("\x1B[2K");
// Erase the line
$this->output->write("\x1B[2K");
// Erase previous lines
if ($this->formatLineCount > 0) {
$this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount));
// Erase previous lines
if ($this->formatLineCount > 0) {
$this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount));
}
}
} elseif ($this->step > 0) {
$this->output->writeln('');
}
$this->firstRun = false;
$this->output->write($message);
}

View File

@@ -67,6 +67,18 @@ class SymfonyQuestionHelper extends QuestionHelper
break;
case $question instanceof ChoiceQuestion && $question->isMultiselect():
$choices = $question->getChoices();
$default = explode(',', $default);
foreach ($default as $key => $value) {
$default[$key] = $choices[trim($value)];
}
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, implode(', ', $default));
break;
case $question instanceof ChoiceQuestion:
$choices = $question->getChoices();
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $choices[$default]);

View File

@@ -108,11 +108,11 @@ class Table
self::$styles = self::initStyles();
}
if (!self::$styles[$name]) {
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
if (isset(self::$styles[$name])) {
return self::$styles[$name];
}
return self::$styles[$name];
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
/**
@@ -124,13 +124,7 @@ class Table
*/
public function setStyle($name)
{
if ($name instanceof TableStyle) {
$this->style = $name;
} elseif (isset(self::$styles[$name])) {
$this->style = self::$styles[$name];
} else {
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
$this->style = $this->resolveStyle($name);
return $this;
}
@@ -157,13 +151,7 @@ class Table
{
$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));
}
$this->columnStyles[$columnIndex] = $this->resolveStyle($name);
return $this;
}
@@ -448,7 +436,7 @@ class Table
}
// create a two dimensional array (rowspan x colspan)
$unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, ''), $unmergedRows);
$unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, array()), $unmergedRows);
foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
$value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : '';
$unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan()));
@@ -660,4 +648,17 @@ class Table
'symfony-style-guide' => $styleGuide,
);
}
private function resolveStyle($name)
{
if ($name instanceof TableStyle) {
return $name;
}
if (isset(self::$styles[$name])) {
return self::$styles[$name];
}
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
}

View File

@@ -19,8 +19,7 @@ namespace Symfony\Component\Console\Helper;
class TableSeparator extends TableCell
{
/**
* @param string $value
* @param array $options
* @param array $options
*/
public function __construct(array $options = array())
{

View File

@@ -46,8 +46,8 @@ class ArgvInput extends Input
/**
* Constructor.
*
* @param array $argv An array of parameters from the CLI (in the argv format)
* @param InputDefinition $definition A InputDefinition instance
* @param array|null $argv An array of parameters from the CLI (in the argv format)
* @param InputDefinition|null $definition A InputDefinition instance
*/
public function __construct(array $argv = null, InputDefinition $definition = null)
{
@@ -69,7 +69,7 @@ class ArgvInput extends Input
}
/**
* Processes command line arguments.
* {@inheritdoc}
*/
protected function parse()
{
@@ -93,7 +93,7 @@ class ArgvInput extends Input
/**
* Parses a short option.
*
* @param string $token The current token.
* @param string $token The current token
*/
private function parseShortOption($token)
{
@@ -176,7 +176,12 @@ class ArgvInput extends Input
// unexpected argument
} else {
throw new RuntimeException('Too many arguments.');
$all = $this->definition->getArguments();
if (count($all)) {
throw new RuntimeException(sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))));
}
throw new RuntimeException(sprintf('No arguments expected, got "%s".', $token));
}
}
@@ -253,9 +258,7 @@ class ArgvInput extends Input
}
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
* {@inheritdoc}
*/
public function getFirstArgument()
{
@@ -269,15 +272,7 @@ class ArgvInput extends Input
}
/**
* Returns true if the raw parameters (not parsed) contain a value.
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
*
* @return bool true if the value is contained in the raw parameters
* {@inheritdoc}
*/
public function hasParameterOption($values, $onlyParams = false)
{
@@ -298,16 +293,7 @@ class ArgvInput extends Input
}
/**
* Returns the value of a raw option (not parsed).
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param mixed $default The default value to return if no result is found
* @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
*
* @return mixed The option value
* {@inheritdoc}
*/
public function getParameterOption($values, $default = false, $onlyParams = false)
{

View File

@@ -30,8 +30,8 @@ class ArrayInput extends Input
/**
* Constructor.
*
* @param array $parameters An array of parameters
* @param InputDefinition $definition A InputDefinition instance
* @param array $parameters An array of parameters
* @param InputDefinition|null $definition A InputDefinition instance
*/
public function __construct(array $parameters, InputDefinition $definition = null)
{
@@ -41,9 +41,7 @@ class ArrayInput extends Input
}
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
* {@inheritdoc}
*/
public function getFirstArgument()
{
@@ -57,15 +55,7 @@ class ArrayInput extends Input
}
/**
* Returns true if the raw parameters (not parsed) contain a value.
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The values to look for in the raw parameters (can be an array)
* @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
*
* @return bool true if the value is contained in the raw parameters
* {@inheritdoc}
*/
public function hasParameterOption($values, $onlyParams = false)
{
@@ -89,16 +79,7 @@ class ArrayInput extends Input
}
/**
* Returns the value of a raw option (not parsed).
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param mixed $default The default value to return if no result is found
* @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
*
* @return mixed The option value
* {@inheritdoc}
*/
public function getParameterOption($values, $default = false, $onlyParams = false)
{
@@ -141,7 +122,7 @@ class ArrayInput extends Input
}
/**
* Processes command line arguments.
* {@inheritdoc}
*/
protected function parse()
{

View File

@@ -38,7 +38,7 @@ abstract class Input implements InputInterface
/**
* Constructor.
*
* @param InputDefinition $definition A InputDefinition instance
* @param InputDefinition|null $definition A InputDefinition instance
*/
public function __construct(InputDefinition $definition = null)
{
@@ -51,9 +51,7 @@ abstract class Input implements InputInterface
}
/**
* Binds the current Input instance with the given arguments and options.
*
* @param InputDefinition $definition A InputDefinition instance
* {@inheritdoc}
*/
public function bind(InputDefinition $definition)
{
@@ -70,9 +68,7 @@ abstract class Input implements InputInterface
abstract protected function parse();
/**
* Validates the input.
*
* @throws RuntimeException When not enough arguments are given
* {@inheritdoc}
*/
public function validate()
{
@@ -89,9 +85,7 @@ abstract class Input implements InputInterface
}
/**
* Checks if the input is interactive.
*
* @return bool Returns true if the input is interactive
* {@inheritdoc}
*/
public function isInteractive()
{
@@ -99,9 +93,7 @@ abstract class Input implements InputInterface
}
/**
* Sets the input interactivity.
*
* @param bool $interactive If the input should be interactive
* {@inheritdoc}
*/
public function setInteractive($interactive)
{
@@ -109,9 +101,7 @@ abstract class Input implements InputInterface
}
/**
* Returns the argument values.
*
* @return array An array of argument values
* {@inheritdoc}
*/
public function getArguments()
{
@@ -119,13 +109,7 @@ abstract class Input implements InputInterface
}
/**
* Returns the argument value for a given argument name.
*
* @param string $name The argument name
*
* @return mixed The argument value
*
* @throws InvalidArgumentException When argument given doesn't exist
* {@inheritdoc}
*/
public function getArgument($name)
{
@@ -137,12 +121,7 @@ abstract class Input implements InputInterface
}
/**
* Sets an argument value by name.
*
* @param string $name The argument name
* @param string $value The argument value
*
* @throws InvalidArgumentException When argument given doesn't exist
* {@inheritdoc}
*/
public function setArgument($name, $value)
{
@@ -154,11 +133,7 @@ abstract class Input implements InputInterface
}
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|int $name The InputArgument name or position
*
* @return bool true if the InputArgument object exists, false otherwise
* {@inheritdoc}
*/
public function hasArgument($name)
{
@@ -166,9 +141,7 @@ abstract class Input implements InputInterface
}
/**
* Returns the options values.
*
* @return array An array of option values
* {@inheritdoc}
*/
public function getOptions()
{
@@ -176,13 +149,7 @@ abstract class Input implements InputInterface
}
/**
* Returns the option value for a given option name.
*
* @param string $name The option name
*
* @return mixed The option value
*
* @throws InvalidArgumentException When option given doesn't exist
* {@inheritdoc}
*/
public function getOption($name)
{
@@ -194,12 +161,7 @@ abstract class Input implements InputInterface
}
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string|bool $value The option value
*
* @throws InvalidArgumentException When option given doesn't exist
* {@inheritdoc}
*/
public function setOption($name, $value)
{
@@ -211,11 +173,7 @@ abstract class Input implements InputInterface
}
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return bool true if the InputOption object exists, false otherwise
* {@inheritdoc}
*/
public function hasOption($name)
{

View File

@@ -11,6 +11,9 @@
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
/**
* InputInterface is the interface implemented by all input classes.
*
@@ -60,11 +63,9 @@ interface InputInterface
public function bind(InputDefinition $definition);
/**
* Validates if arguments given are correct.
* Validates the input.
*
* Throws an exception when not enough arguments are given.
*
* @throws \RuntimeException
* @throws RuntimeException When not enough arguments are given
*/
public function validate();
@@ -76,11 +77,13 @@ interface InputInterface
public function getArguments();
/**
* Gets argument by name.
* Returns the argument value for a given argument name.
*
* @param string $name The name of the argument
* @param string $name The argument name
*
* @return mixed
* @return mixed The argument value
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name);
@@ -111,11 +114,13 @@ interface InputInterface
public function getOptions();
/**
* Gets an option by name.
* Returns the option value for a given option name.
*
* @param string $name The name of the option
* @param string $name The option name
*
* @return mixed
* @return mixed The option value
*
* @throws InvalidArgumentException When option given doesn't exist
*/
public function getOption($name);

View File

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

View File

@@ -94,21 +94,33 @@ abstract class Output implements OutputInterface
return $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function isQuiet()
{
return self::VERBOSITY_QUIET === $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function isVerbose()
{
return self::VERBOSITY_VERBOSE <= $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function isVeryVerbose()
{
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function isDebug()
{
return self::VERBOSITY_DEBUG <= $this->verbosity;

View File

@@ -68,6 +68,16 @@ class ChoiceQuestion extends Question
return $this;
}
/**
* Returns whether the choices are multiselect.
*
* @return bool
*/
public function isMultiselect()
{
return $this->multiselect;
}
/**
* Gets the prompt for choices.
*

Binary file not shown.

View File

@@ -18,7 +18,6 @@ use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
@@ -66,53 +65,10 @@ class SymfonyStyle extends OutputStyle
*/
public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false)
{
$this->autoPrependBlock();
$messages = is_array($messages) ? array_values($messages) : array($messages);
$indentLength = 0;
$lines = array();
if (null !== $type) {
$typePrefix = sprintf('[%s] ', $type);
$indentLength = strlen($typePrefix);
$lineIndentation = str_repeat(' ', $indentLength);
}
// wrap and add newlines for each element
foreach ($messages as $key => $message) {
$message = OutputFormatter::escape($message);
$lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix) - $indentLength, PHP_EOL, true)));
// prefix each line with a number of spaces equivalent to the type length
if (null !== $type) {
foreach ($lines as &$line) {
$line = $lineIndentation === substr($line, 0, $indentLength) ? $line : $lineIndentation.$line;
}
}
if (count($messages) > 1 && $key < count($messages) - 1) {
$lines[] = '';
}
}
if (null !== $type) {
$lines[0] = substr_replace($lines[0], $typePrefix, 0, $indentLength);
}
if ($padding && $this->isDecorated()) {
array_unshift($lines, '');
$lines[] = '';
}
foreach ($lines as &$line) {
$line = sprintf('%s%s', $prefix, $line);
$line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line));
if ($style) {
$line = sprintf('<%s>%s</>', $style, $line);
}
}
$this->writeln($lines);
$this->autoPrependBlock();
$this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, true));
$this->newLine();
}
@@ -177,11 +133,10 @@ class SymfonyStyle extends OutputStyle
public function comment($message)
{
$messages = is_array($message) ? array_values($message) : array($message);
foreach ($messages as &$message) {
$message = $this->getFormatter()->format($message);
}
$this->block($messages, null, null, ' // ');
$this->autoPrependBlock();
$this->writeln($this->createBlock($messages, null, null, '<fg=default;bg=default> // </>'));
$this->newLine();
}
/**
@@ -229,21 +184,13 @@ class SymfonyStyle extends OutputStyle
*/
public function table(array $headers, array $rows)
{
array_walk_recursive($headers, function (&$value) {
if ($value instanceof TableCell) {
$value = new TableCell(sprintf('<info>%s</>', $value), array(
'colspan' => $value->getColspan(),
'rowspan' => $value->getRowspan(),
));
} else {
$value = sprintf('<info>%s</>', $value);
}
});
$style = clone Table::getStyleDefinition('symfony-style-guide');
$style->setCellHeaderFormat('<info>%s</info>');
$table = new Table($this);
$table->setHeaders($headers);
$table->setRows($rows);
$table->setStyle('symfony-style-guide');
$table->setStyle($style);
$table->render();
$this->newLine();
@@ -437,4 +384,52 @@ class SymfonyStyle extends OutputStyle
return substr($value, -4);
}, array_merge(array($this->bufferedOutput->fetch()), (array) $messages));
}
private function createBlock($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = false)
{
$indentLength = 0;
$prefixLength = Helper::strlenWithoutDecoration($this->getFormatter(), $prefix);
$lines = array();
if (null !== $type) {
$type = sprintf('[%s] ', $type);
$indentLength = strlen($type);
$lineIndentation = str_repeat(' ', $indentLength);
}
// wrap and add newlines for each element
foreach ($messages as $key => $message) {
if ($escape) {
$message = OutputFormatter::escape($message);
}
$lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - $prefixLength - $indentLength, PHP_EOL, true)));
if (count($messages) > 1 && $key < count($messages) - 1) {
$lines[] = '';
}
}
$firstLineIndex = 0;
if ($padding && $this->isDecorated()) {
$firstLineIndex = 1;
array_unshift($lines, '');
$lines[] = '';
}
foreach ($lines as $i => &$line) {
if (null !== $type) {
$line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line;
}
$line = $prefix.$line;
$line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line));
if ($style) {
$line = sprintf('<%s>%s</>', $style, $line);
}
}
return $lines;
}
}

View File

@@ -37,7 +37,7 @@ class ApplicationTester
/**
* Constructor.
*
* @param Application $application An Application instance to test.
* @param Application $application An Application instance to test
*/
public function __construct(Application $application)
{

View File

@@ -32,7 +32,7 @@ class CommandTester
/**
* Constructor.
*
* @param Command $command A Command instance to test.
* @param Command $command A Command instance to test
*/
public function __construct(Command $command)
{

View File

@@ -425,7 +425,7 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$application->add(new \FooCommand());
$application->add(new \Foo1Command());
$application->add(new \Foo2Command());
$application->add(new \foo3Command());
$application->add(new \Foo3Command());
try {
$application->find('Unknown-namespace:Unknown-command');

View File

@@ -4,10 +4,9 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength;
//Ensure that all lines are aligned to the begin of the first one and start with '//' in a very long line comment
// ensure long words are properly wrapped in blocks
return function (InputInterface $input, OutputInterface $output) {
$output = new SymfonyStyleWithForcedLineLength($input, $output);
$output->comment(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum'
);
$word = 'Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon';
$sfStyle = new SymfonyStyleWithForcedLineLength($input, $output);
$sfStyle->block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false);
};

View File

@@ -0,0 +1,13 @@
<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength;
// ensure that all lines are aligned to the begin of the first one and start with '//' in a very long line comment
return function (InputInterface $input, OutputInterface $output) {
$output = new SymfonyStyleWithForcedLineLength($input, $output);
$output->comment(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum'
);
};

View File

@@ -0,0 +1,15 @@
<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength;
use Symfony\Component\Console\Style\SymfonyStyle;
// ensure that nested tags have no effect on the color of the '//' prefix
return function (InputInterface $input, OutputInterface $output) {
$output->setDecorated(true);
$output = new SymfonyStyle($input, $output);
$output->comment(
'Lorem ipsum dolor sit <comment>amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</comment> Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum'
);
};

View File

@@ -0,0 +1,17 @@
<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength;
// ensure that block() behaves properly with a prefix and without type
return function (InputInterface $input, OutputInterface $output) {
$output = new SymfonyStyleWithForcedLineLength($input, $output);
$output->block(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum',
null,
null,
'$ ',
true
);
};

View File

@@ -0,0 +1,14 @@
<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength;
// ensure that block() behaves properly with a type and without prefix
return function (InputInterface $input, OutputInterface $output) {
$output = new SymfonyStyleWithForcedLineLength($input, $output);
$output->block(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum',
'TEST'
);
};

View File

@@ -0,0 +1,15 @@
<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength;
// ensure that block() output is properly formatted (even padding lines)
return function (InputInterface $input, OutputInterface $output) {
$output->setDecorated(true);
$output = new SymfonyStyleWithForcedLineLength($input, $output);
$output->success(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum',
'TEST'
);
};

View File

@@ -1,6 +1,4 @@
// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
// aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
// Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
// sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
§ [CUSTOM] Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophatto
§ peristeralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon

View File

@@ -0,0 +1,6 @@
// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
// aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
// Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
// sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum

View File

@@ -0,0 +1,7 @@
 // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 
 // dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea 
 // commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 
 // pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim
 // id est laborum

View File

@@ -0,0 +1,6 @@
$ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
$ aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
$ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
$ occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum

View File

@@ -0,0 +1,7 @@
[TEST] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum

View File

@@ -0,0 +1,8 @@
 
 [OK] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore 
 magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 
 consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
 Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum 
 

View File

@@ -29,7 +29,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0 [>---------------------------]').
' 0 [>---------------------------]'.
$this->generateOutput(' 1 [->--------------------------]').
$this->generateOutput(' 0 [>---------------------------]'),
stream_get_contents($output->getStream())
@@ -44,7 +44,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0 [>---------------------------]').
' 0 [>---------------------------]'.
$this->generateOutput(' 1 [->--------------------------]'),
stream_get_contents($output->getStream())
);
@@ -58,7 +58,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0 [>---------------------------]').
' 0 [>---------------------------]'.
$this->generateOutput(' 5 [----->----------------------]'),
stream_get_contents($output->getStream())
);
@@ -73,7 +73,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0 [>---------------------------]').
' 0 [>---------------------------]'.
$this->generateOutput(' 3 [--->------------------------]').
$this->generateOutput(' 5 [----->----------------------]'),
stream_get_contents($output->getStream())
@@ -89,7 +89,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 9/10 [=========================>--] 90%').
' 9/10 [=========================>--] 90%'.
$this->generateOutput(' 10/10 [============================] 100%').
$this->generateOutput(' 11/11 [============================] 100%'),
stream_get_contents($output->getStream())
@@ -99,7 +99,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
public function testFormat()
{
$expected =
$this->generateOutput(' 0/10 [>---------------------------] 0%').
' 0/10 [>---------------------------] 0%'.
$this->generateOutput(' 10/10 [============================] 100%').
$this->generateOutput(' 10/10 [============================] 100%')
;
@@ -156,7 +156,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/10 [/ ] 0%').
' 0/10 [/ ] 0%'.
$this->generateOutput(' 1/10 [_/ ] 10%'),
stream_get_contents($output->getStream())
);
@@ -169,7 +169,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/50 [>---------------------------] 0%'),
' 0/50 [>---------------------------] 0%',
stream_get_contents($output->getStream())
);
}
@@ -193,7 +193,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 50/50 [============================] 100%'),
' 50/50 [============================] 100%',
stream_get_contents($output->getStream())
);
}
@@ -208,7 +208,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/50 [>---------------------------] 0%').
' 0/50 [>---------------------------] 0%'.
$this->generateOutput(' 0/50 [>---------------------------] 0%').
$this->generateOutput(' 1/50 [>---------------------------] 2%').
$this->generateOutput(' 2/50 [=>--------------------------] 4%'),
@@ -230,7 +230,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/50 [>---------------------------] 0%').
' 0/50 [>---------------------------] 0%'.
$this->generateOutput(' 0/50 [>---------------------------] 0%').
$this->generateOutput(' 1/50 [>---------------------------] 2%').
$this->generateOutput(' 2/50 [=>--------------------------]'),
@@ -247,7 +247,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/50 [>---------------------------]').
' 0/50 [>---------------------------]'.
$this->generateOutput(' 1/50 [>---------------------------]'),
stream_get_contents($output->getStream())
);
@@ -264,7 +264,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/50 [>---------------------------] 0%').
' 0/50 [>---------------------------] 0%'.
$this->generateOutput(' 0/50 [>---------------------------] 0%').
$this->generateOutput(' 1/50 [>---------------------------] 2%').
$this->generateOutput(' 15/50 [========>-------------------] 30%').
@@ -336,7 +336,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0 [>---------------------------]').
' 0 [>---------------------------]'.
$this->generateOutput(' 3 [■■■>------------------------]'),
stream_get_contents($output->getStream())
);
@@ -351,7 +351,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/50 [>---------------------------] 0%').
' 0/50 [>---------------------------] 0%'.
$this->generateOutput(' 25/50 [==============>-------------] 50%').
$this->generateOutput(''),
stream_get_contents($output->getStream())
@@ -368,7 +368,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/200 [>---------------------------] 0%').
' 0/200 [>---------------------------] 0%'.
$this->generateOutput(' 0/200 [>---------------------------] 0%').
$this->generateOutput(' 199/200 [===========================>] 99%').
$this->generateOutput(' 200/200 [============================] 100%'),
@@ -468,9 +468,9 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/2 [>---------------------------] 0%')."\n".
$this->generateOutput(' 0/3 [#---------------------------] 0%')."\n".
rtrim($this->generateOutput(' 0 [>---------------------------]')).
' 0/2 [>---------------------------] 0%'."\n".
' 0/3 [#---------------------------] 0%'."\n".
rtrim(' 0 [>---------------------------]').
"\033[2A".
$this->generateOutput(' 1/2 [==============>-------------] 50%')."\n".
@@ -508,7 +508,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
rtrim($this->generateOutput(' 0 [>---------------------------]')).
rtrim(' 0 [>---------------------------]').
rtrim($this->generateOutput(' 1 [->--------------------------]')).
rtrim($this->generateOutput(' 2 [-->-------------------------]')).
rtrim($this->generateOutput(' 3 [--->------------------------]')).
@@ -531,7 +531,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 3 [>---------------------------]').
' 3 [>---------------------------]'.
$this->generateOutput(' 2 [=========>------------------]').
$this->generateOutput(' 0 [============================]'),
stream_get_contents($output->getStream())
@@ -550,7 +550,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(">---------------------------\nfoobar").
">---------------------------\nfoobar".
$this->generateOutput("=========>------------------\nfoobar").
"\x0D\x1B[2K\x1B[1A\x1B[2K".
$this->generateOutput("============================\nfoobar"),
@@ -582,11 +582,11 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(
" \033[44;37m Starting the demo... fingers crossed \033[0m\n".
' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n".
" \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m"
).
.
$this->generateOutput(
" \033[44;37m Looks good to me... \033[0m\n".
' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
@@ -608,7 +608,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
$bar->start();
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0 [>---------------------------]'),
' 0 [>---------------------------]',
stream_get_contents($output->getStream())
);
@@ -617,7 +617,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
$bar->start();
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' 0/10 [>---------------------------] 0%'),
' 0/10 [>---------------------------] 0%',
stream_get_contents($output->getStream())
);
}

View File

@@ -0,0 +1,109 @@
<?php
namespace Symfony\Component\Console\Tests\Helper;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Question\ChoiceQuestion;
/**
* @group tty
*/
class SymfonyQuestionHelperTest extends \PHPUnit_Framework_TestCase
{
public function testAskChoice()
{
$questionHelper = new SymfonyQuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$questionHelper->setHelperSet($helperSet);
$heroes = array('Superman', 'Batman', 'Spiderman');
$questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
$question->setMaxAttempts(1);
// first answer is an empty answer, we're supposed to receive the default value
$this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Spiderman]', $output);
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setMaxAttempts(1);
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setErrorMessage('Input "%s" is not a superhero!');
$question->setMaxAttempts(2);
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('Input "Fabien" is not a superhero!', $output);
try {
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
$question->setMaxAttempts(1);
$questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
}
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output);
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output);
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
fwrite($stream, $input);
rewind($stream);
return $stream;
}
protected function createOutputInterface()
{
$output = new StreamOutput(fopen('php://memory', 'r+', false));
$output->setDecorated(false);
return $output;
}
protected function createInputInterfaceMock($interactive = true)
{
$mock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$mock->expects($this->any())
->method('isInteractive')
->will($this->returnValue($interactive));
return $mock;
}
private function assertOutputContains($expected, StreamOutput $output)
{
rewind($output->getStream());
$stream = stream_get_contents($output->getStream());
$this->assertContains($expected, $stream);
}
}

View File

@@ -631,6 +631,25 @@ TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
/**
* @expectedException Symfony\Component\Console\Exception\InvalidArgumentException
* @expectedExceptionMessage Style "absent" is not defined.
*/
public function testIsNotDefinedStyleException()
{
$table = new Table($this->getOutputStream());
$table->setStyle('absent');
}
/**
* @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
* @expectedExceptionMessage Style "absent" is not defined.
*/
public function testGetStyleDefinition()
{
Table::getStyleDefinition('absent');
}
protected function getOutputStream()
{
return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false);

View File

@@ -183,7 +183,17 @@ class ArgvInputTest extends \PHPUnit_Framework_TestCase
array(
array('cli.php', 'foo', 'bar'),
new InputDefinition(),
'Too many arguments.',
'No arguments expected, got "foo".',
),
array(
array('cli.php', 'foo', 'bar'),
new InputDefinition(array(new InputArgument('number'))),
'Too many arguments, expected arguments "number".',
),
array(
array('cli.php', 'foo', 'bar', 'zzz'),
new InputDefinition(array(new InputArgument('number'), new InputArgument('county'))),
'Too many arguments, expected arguments "number" "county".',
),
array(
array('cli.php', '--foo'),

View File

@@ -19,7 +19,10 @@ class InputDefinitionTest extends \PHPUnit_Framework_TestCase
{
protected static $fixtures;
protected $foo, $bar, $foo1, $foo2;
protected $foo;
protected $bar;
protected $foo1;
protected $foo2;
public static function setUpBeforeClass()
{

View File

@@ -54,22 +54,6 @@ class SymfonyStyleTest extends PHPUnit_Framework_TestCase
return array_map(null, glob($baseDir.'/command/command_*.php'), glob($baseDir.'/output/output_*.txt'));
}
public function testLongWordsBlockWrapping()
{
$word = 'Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygovgollhjvhvljfezefeqifzeiqgiqzhrsdgihqzridghqridghqirshdghdghieridgheirhsdgehrsdvhqrsidhqshdgihrsidvqhneriqsdvjzergetsrfhgrstsfhsetsfhesrhdgtesfhbzrtfbrztvetbsdfbrsdfbrn';
$wordLength = strlen($word);
$maxLineLength = SymfonyStyle::MAX_LINE_LENGTH - 3;
$this->command->setCode(function (InputInterface $input, OutputInterface $output) use ($word) {
$sfStyle = new SymfonyStyleWithForcedLineLength($input, $output);
$sfStyle->block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false);
});
$this->tester->execute(array(), array('interactive' => false, 'decorated' => false));
$expectedCount = (int) ceil($wordLength / ($maxLineLength)) + (int) ($wordLength > $maxLineLength - 5);
$this->assertSame($expectedCount, substr_count($this->tester->getDisplay(true), ' § '));
}
}
/**

View File

@@ -29,7 +29,7 @@ class CssSelectorConverter
private $translator;
/**
* @param bool $html Whether HTML support should be enabled. Disable it for XML documents.
* @param bool $html Whether HTML support should be enabled. Disable it for XML documents
*/
public function __construct($html = true)
{
@@ -53,8 +53,8 @@ class CssSelectorConverter
* Optionally, a prefix can be added to the resulting XPath
* expression with the $prefix parameter.
*
* @param string $cssExpr The CSS expression.
* @param string $prefix An optional prefix for the XPath expression.
* @param string $cssExpr The CSS expression
* @param string $prefix An optional prefix for the XPath expression
*
* @return string
*/

View File

@@ -353,7 +353,7 @@ class ErrorHandler
* @param int $line
* @param array $context
*
* @return bool Returns false when no handling happens so that the PHP engine can handle the error itself.
* @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
*
* @throws \ErrorException When $this->thrownErrors requests so
*

View File

@@ -84,7 +84,7 @@ class ExceptionHandler
*
* @param string $format The format for links to source files
*
* @return string The previous file link format.
* @return string The previous file link format
*/
public function setFileLinkFormat($format)
{

View File

@@ -13,12 +13,11 @@ namespace Symfony\Component\Debug\Tests;
use Symfony\Component\Debug\DebugClassLoader;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\ContextErrorException;
class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
{
/**
* @var int Error reporting level before running tests.
* @var int Error reporting level before running tests
*/
private $errorReporting;

View File

@@ -13,7 +13,7 @@ namespace Symfony\Component\Debug\Tests;
use Symfony\Component\Debug\ExceptionHandler;
class MockExceptionHandler extends Exceptionhandler
class MockExceptionHandler extends ExceptionHandler
{
public $e;

View File

@@ -337,7 +337,7 @@ class Crawler implements \Countable, \IteratorAggregate
*
* @param int $position The position
*
* @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist.
* @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist
*/
public function eq($position)
{
@@ -394,7 +394,7 @@ class Crawler implements \Countable, \IteratorAggregate
*
* @param \Closure $closure An anonymous function
*
* @return Crawler A Crawler instance with the selected nodes.
* @return Crawler A Crawler instance with the selected nodes
*/
public function reduce(\Closure $closure)
{
@@ -1105,6 +1105,7 @@ class Crawler implements \Countable, \IteratorAggregate
$crawler = new static($nodes, $this->uri, $this->baseHref);
$crawler->isHtml = $this->isHtml;
$crawler->document = $this->document;
$crawler->namespaces = $this->namespaces;
return $crawler;
}

View File

@@ -85,7 +85,7 @@ class Form extends Link implements \ArrayAccess
*
* The returned array does not include file fields (@see getFiles).
*
* @return array An array of field values.
* @return array An array of field values
*/
public function getValues()
{
@@ -106,7 +106,7 @@ class Form extends Link implements \ArrayAccess
/**
* Gets the file field values.
*
* @return array An array of file field values.
* @return array An array of file field values
*/
public function getFiles()
{
@@ -135,7 +135,7 @@ class Form extends Link implements \ArrayAccess
* This method converts fields with the array notation
* (like foo[bar] to arrays) like PHP does.
*
* @return array An array of field values.
* @return array An array of field values
*/
public function getPhpValues()
{
@@ -162,7 +162,7 @@ class Form extends Link implements \ArrayAccess
* For a compound file field foo[bar] it will create foo[bar][name],
* instead of foo[name][bar] which would be found in $_FILES.
*
* @return array An array of file field values.
* @return array An array of file field values
*/
public function getPhpFiles()
{
@@ -246,8 +246,6 @@ class Form extends Link implements \ArrayAccess
* Removes a field from the form.
*
* @param string $name The field name
*
* @throws \InvalidArgumentException when the name is malformed
*/
public function remove($name)
{

View File

@@ -26,8 +26,6 @@ class FormFieldRegistry
* Adds a field to the registry.
*
* @param FormField $field The field
*
* @throws \InvalidArgumentException when the name is malformed
*/
public function add(FormField $field)
{
@@ -52,8 +50,6 @@ class FormFieldRegistry
* Removes a field and its children from the registry.
*
* @param string $name The fully qualified name of the base field
*
* @throws \InvalidArgumentException when the name is malformed
*/
public function remove($name)
{
@@ -76,7 +72,6 @@ class FormFieldRegistry
*
* @return mixed The value of the field
*
* @throws \InvalidArgumentException when the name is malformed
* @throws \InvalidArgumentException if the field does not exist
*/
public function &get($name)
@@ -118,7 +113,6 @@ class FormFieldRegistry
* @param string $name The fully qualified name of the field
* @param mixed $value The value
*
* @throws \InvalidArgumentException when the name is malformed
* @throws \InvalidArgumentException if the field does not exist
*/
public function set($name, $value)
@@ -199,24 +193,23 @@ class FormFieldRegistry
* @param string $name The name of the field
*
* @return string[] The list of segments
*
* @throws \InvalidArgumentException when the name is malformed
*/
private function getSegments($name)
{
if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) {
$segments = array($m['base']);
while (!empty($m['extra'])) {
if (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $m['extra'], $m)) {
$extra = $m['extra'];
if (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $extra, $m)) {
$segments[] = $m['segment'];
} else {
throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name));
$segments[] = $extra;
}
}
return $segments;
}
throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name));
return array($name);
}
}

View File

@@ -13,7 +13,6 @@ namespace Symfony\Component\DomCrawler\Tests;
use Symfony\Component\DomCrawler\Form;
use Symfony\Component\DomCrawler\FormFieldRegistry;
use Symfony\Component\DomCrawler\Field;
class FormTest extends \PHPUnit_Framework_TestCase
{
@@ -345,18 +344,6 @@ class FormTest extends \PHPUnit_Framework_TestCase
}
}
public function testSetValueOnMultiValuedFieldsWithMalformedName()
{
$form = $this->createForm('<form><input type="text" name="foo[bar]" value="bar" /><input type="text" name="foo[baz]" value="baz" /><input type="submit" /></form>');
try {
$form['foo[bar'] = 'bar';
$this->fail('->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.');
} catch (\InvalidArgumentException $e) {
$this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.');
}
}
public function testDisableValidation()
{
$form = $this->createForm('<form>
@@ -681,31 +668,19 @@ class FormTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($form->has('example.y'), '->has() returns true if the image input was correctly turned into an x and a y fields');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testFormFieldRegistryAddThrowAnExceptionWhenTheNameIsMalformed()
public function testFormFieldRegistryAcceptAnyNames()
{
$registry = new FormFieldRegistry();
$registry->add($this->getFormFieldMock('[foo]'));
}
$field = $this->getFormFieldMock('[t:dbt%3adate;]data_daterange_enddate_value');
/**
* @expectedException \InvalidArgumentException
*/
public function testFormFieldRegistryRemoveThrowAnExceptionWhenTheNameIsMalformed()
{
$registry = new FormFieldRegistry();
$registry->remove('[foo]');
}
$registry->add($field);
$this->assertEquals($field, $registry->get('[t:dbt%3adate;]data_daterange_enddate_value'));
$registry->set('[t:dbt%3adate;]data_daterange_enddate_value', null);
/**
* @expectedException \InvalidArgumentException
*/
public function testFormFieldRegistryGetThrowAnExceptionWhenTheNameIsMalformed()
{
$registry = new FormFieldRegistry();
$registry->get('[foo]');
$form = $this->createForm('<form><input type="text" name="[t:dbt%3adate;]data_daterange_enddate_value" value="bar" /><input type="submit" /></form>');
$form['[t:dbt%3adate;]data_daterange_enddate_value'] = 'bar';
$registry->remove('[t:dbt%3adate;]data_daterange_enddate_value');
}
/**
@@ -717,15 +692,6 @@ class FormTest extends \PHPUnit_Framework_TestCase
$registry->get('foo');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testFormFieldRegistrySetThrowAnExceptionWhenTheNameIsMalformed()
{
$registry = new FormFieldRegistry();
$registry->set('[foo]', null);
}
/**
* @expectedException \InvalidArgumentException
*/

View File

@@ -97,9 +97,13 @@ class RegisterListenersPass implements CompilerPassInterface
// We must assume that the class value has been correctly filled, even if the service is created by a factory
$class = $container->getParameterBag()->resolveValue($def->getClass());
$interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
if (!is_subclass_of($class, $interface)) {
if (!class_exists($class, false)) {
throw new \InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
}

View File

@@ -37,7 +37,7 @@ class Event
*
* @see Event::stopPropagation()
*
* @return bool Whether propagation was already stopped for this event.
* @return bool Whether propagation was already stopped for this event
*/
public function isPropagationStopped()
{

View File

@@ -161,9 +161,9 @@ class EventDispatcher implements EventDispatcherInterface
* This method can be overridden to add functionality that is executed
* for each listener.
*
* @param callable[] $listeners The event listeners.
* @param string $eventName The name of the event to dispatch.
* @param Event $event The event object to pass to the event handlers/listeners.
* @param callable[] $listeners The event listeners
* @param string $eventName The name of the event to dispatch
* @param Event $event The event object to pass to the event handlers/listeners
*/
protected function doDispatch($listeners, $eventName, Event $event)
{
@@ -178,7 +178,7 @@ class EventDispatcher implements EventDispatcherInterface
/**
* Sorts the internal list of listeners for the given event by priority.
*
* @param string $eventName The name of the event.
* @param string $eventName The name of the event
*/
private function sortListeners($eventName)
{

View File

@@ -26,7 +26,7 @@ interface EventDispatcherInterface
* @param string $eventName The name of the event to dispatch. The name of
* the event is the name of the method that is
* invoked on listeners.
* @param Event $event The event to pass to the event handlers/listeners.
* @param Event $event The event to pass to the event handlers/listeners
* If not supplied, an empty Event instance is created.
*
* @return Event
@@ -49,7 +49,7 @@ interface EventDispatcherInterface
* The subscriber is asked for all the events he is
* interested in and added as a listener for these events.
*
* @param EventSubscriberInterface $subscriber The subscriber.
* @param EventSubscriberInterface $subscriber The subscriber
*/
public function addSubscriber(EventSubscriberInterface $subscriber);

View File

@@ -37,8 +37,8 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* Encapsulate an event with $subject and $args.
*
* @param mixed $subject The subject of the event, usually an object.
* @param array $arguments Arguments to store in the event.
* @param mixed $subject The subject of the event, usually an object
* @param array $arguments Arguments to store in the event
*/
public function __construct($subject = null, array $arguments = array())
{
@@ -49,7 +49,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* Getter for subject property.
*
* @return mixed $subject The observer subject.
* @return mixed $subject The observer subject
*/
public function getSubject()
{
@@ -59,9 +59,9 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* Get argument by key.
*
* @param string $key Key.
* @param string $key Key
*
* @return mixed Contents of array key.
* @return mixed Contents of array key
*
* @throws \InvalidArgumentException If key is not found.
*/
@@ -77,8 +77,8 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* Add argument to event.
*
* @param string $key Argument name.
* @param mixed $value Value.
* @param string $key Argument name
* @param mixed $value Value
*
* @return GenericEvent
*/
@@ -102,7 +102,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* Set args property.
*
* @param array $args Arguments.
* @param array $args Arguments
*
* @return GenericEvent
*/
@@ -116,7 +116,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* Has argument.
*
* @param string $key Key of arguments array.
* @param string $key Key of arguments array
*
* @return bool
*/
@@ -128,7 +128,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* ArrayAccess for argument getter.
*
* @param string $key Array key.
* @param string $key Array key
*
* @return mixed
*
@@ -142,8 +142,8 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* ArrayAccess for argument setter.
*
* @param string $key Array key to set.
* @param mixed $value Value.
* @param string $key Array key to set
* @param mixed $value Value
*/
public function offsetSet($key, $value)
{
@@ -153,7 +153,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* ArrayAccess for unset argument.
*
* @param string $key Array key.
* @param string $key Array key
*/
public function offsetUnset($key)
{
@@ -165,7 +165,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* ArrayAccess has argument.
*
* @param string $key Array key.
* @param string $key Array key
*
* @return bool
*/

View File

@@ -28,7 +28,7 @@ class ImmutableEventDispatcher implements EventDispatcherInterface
/**
* Creates an unmodifiable proxy for an event dispatcher.
*
* @param EventDispatcherInterface $dispatcher The proxied event dispatcher.
* @param EventDispatcherInterface $dispatcher The proxied event dispatcher
*/
public function __construct(EventDispatcherInterface $dispatcher)
{

View File

@@ -557,7 +557,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* This method implements the IteratorAggregate interface.
*
* @return \Iterator An iterator
* @return \Iterator|SplFileInfo[] An iterator
*
* @throws \LogicException if the in() method has not been called
*/

View File

@@ -45,7 +45,7 @@ class PathFilterIterator extends MultiplePcreFilterIterator
*
* Use only / as directory separator (on Windows also).
*
* @param string $str Pattern: regexp or dirname.
* @param string $str Pattern: regexp or dirname
*
* @return string regexp corresponding to a given string or regexp
*/

View File

@@ -29,13 +29,13 @@ class Cookie
/**
* Constructor.
*
* @param string $name The name of the cookie
* @param string $value The value of the cookie
* @param int|string|\DateTime|\DateTimeInterface $expire The time the cookie expires
* @param string $path The path on the server in which the cookie will be available on
* @param string $domain The domain that the cookie is available to
* @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
* @param string $name The name of the cookie
* @param string $value The value of the cookie
* @param int|string|\DateTimeInterface $expire The time the cookie expires
* @param string $path The path on the server in which the cookie will be available on
* @param string $domain The domain that the cookie is available to
* @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
*
* @throws \InvalidArgumentException
*/

View File

@@ -0,0 +1,23 @@
<?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\HttpFoundation\Exception;
/**
* The HTTP request contains headers with conflicting information.
*
* This exception should trigger an HTTP 400 response in your application code.
*
* @author Magnus Nordlander <magnus@fervo.se>
*/
class ConflictingHeadersException extends \RuntimeException
{
}

View File

@@ -68,7 +68,7 @@ class File extends \SplFileInfo
* mime_content_type() and the system binary "file" (in this order), depending on
* which of those are available.
*
* @return string|null The guessed mime type (i.e. "application/pdf")
* @return string|null The guessed mime type (e.g. "application/pdf")
*
* @see MimeTypeGuesser
*/

View File

@@ -661,6 +661,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'image/gif' => 'gif',
'image/ief' => 'ief',
'image/jpeg' => 'jpeg',
'image/pjpeg' => 'jpeg',
'image/ktx' => 'ktx',
'image/png' => 'png',
'image/prs.btif' => 'btif',

View File

@@ -194,7 +194,7 @@ class UploadedFile extends File
/**
* Returns whether the file was uploaded successfully.
*
* @return bool True if the file has been uploaded with HTTP and no error occurred.
* @return bool True if the file has been uploaded with HTTP and no error occurred
*/
public function isValid()
{

View File

@@ -57,7 +57,7 @@ class IpUtils
* @param string $requestIp IPv4 address to check
* @param string $ip IPv4 address or subnet in CIDR notation
*
* @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet.
* @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
*/
public static function checkIp4($requestIp, $ip)
{

View File

@@ -190,10 +190,10 @@ class ParameterBag implements \IteratorAggregate, \Countable
/**
* Filter key.
*
* @param string $key Key.
* @param mixed $default Default = null.
* @param int $filter FILTER_* constant.
* @param mixed $options Filter options.
* @param string $key Key
* @param mixed $default Default = null
* @param int $filter FILTER_* constant
* @param mixed $options Filter options
*
* @see http://php.net/manual/en/function.filter-var.php
*

View File

@@ -66,7 +66,7 @@ class RedirectResponse extends Response
*
* @param string $url The URL to redirect to
*
* @return RedirectResponse The current response.
* @return RedirectResponse The current response
*
* @throws \InvalidArgumentException
*/

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
/**
@@ -553,7 +554,7 @@ class Request
/**
* Gets the list of trusted proxies.
*
* @return array An array of trusted proxies.
* @return array An array of trusted proxies
*/
public static function getTrustedProxies()
{
@@ -579,7 +580,7 @@ class Request
/**
* Gets the list of trusted host patterns.
*
* @return array An array of trusted host patterns.
* @return array An array of trusted host patterns
*/
public static function getTrustedHosts()
{
@@ -798,41 +799,34 @@ class Request
return array($ip);
}
if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
$hasTrustedForwardedHeader = self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED]);
$hasTrustedClientIpHeader = self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP]);
if ($hasTrustedForwardedHeader) {
$forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
$clientIps = $matches[3];
} elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
$clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
$forwardedClientIps = $matches[3];
$forwardedClientIps = $this->normalizeAndFilterClientIps($forwardedClientIps, $ip);
$clientIps = $forwardedClientIps;
}
$clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
$firstTrustedIp = null;
if ($hasTrustedClientIpHeader) {
$xForwardedForClientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
foreach ($clientIps as $key => $clientIp) {
// Remove port (unfortunately, it does happen)
if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
$clientIps[$key] = $clientIp = $match[1];
}
if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
unset($clientIps[$key]);
continue;
}
if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
unset($clientIps[$key]);
// Fallback to this when the client IP falls into the range of trusted proxies
if (null === $firstTrustedIp) {
$firstTrustedIp = $clientIp;
}
}
$xForwardedForClientIps = $this->normalizeAndFilterClientIps($xForwardedForClientIps, $ip);
$clientIps = $xForwardedForClientIps;
}
// Now the IP chain contains only untrusted proxies and the client IP
return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
if ($hasTrustedForwardedHeader && $hasTrustedClientIpHeader && $forwardedClientIps !== $xForwardedForClientIps) {
throw new ConflictingHeadersException('The request has both a trusted Forwarded header and a trusted Client IP header, conflicting with each other with regards to the originating IP addresses of the request. This is the result of a misconfiguration. You should either configure your proxy only to send one of these headers, or configure Symfony to distrust one of them.');
}
if (!$hasTrustedForwardedHeader && !$hasTrustedClientIpHeader) {
return $this->normalizeAndFilterClientIps(array(), $ip);
}
return $clientIps;
}
/**
@@ -1389,7 +1383,7 @@ class Request
/**
* Sets the request format.
*
* @param string $format The request format.
* @param string $format The request format
*/
public function setRequestFormat($format)
{
@@ -1453,7 +1447,7 @@ class Request
/**
* Checks if the request method is of specified type.
*
* @param string $method Uppercase request method (GET, POST etc).
* @param string $method Uppercase request method (GET, POST etc)
*
* @return bool
*/
@@ -1469,7 +1463,7 @@ class Request
*/
public function isMethodSafe()
{
return in_array($this->getMethod(), array('GET', 'HEAD'));
return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE'));
}
/**
@@ -1477,7 +1471,7 @@ class Request
*
* @param bool $asResource If true, a resource will be returned
*
* @return string|resource The request body content or a resource to read the body stream.
* @return string|resource The request body content or a resource to read the body stream
*
* @throws \LogicException
*/
@@ -1923,4 +1917,35 @@ class Request
{
return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
}
private function normalizeAndFilterClientIps(array $clientIps, $ip)
{
$clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
$firstTrustedIp = null;
foreach ($clientIps as $key => $clientIp) {
// Remove port (unfortunately, it does happen)
if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
$clientIps[$key] = $clientIp = $match[1];
}
if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
unset($clientIps[$key]);
continue;
}
if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
unset($clientIps[$key]);
// Fallback to this when the client IP falls into the range of trusted proxies
if (null === $firstTrustedIp) {
$firstTrustedIp = $clientIp;
}
}
}
// Now the IP chain contains only untrusted proxies and the client IP
return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
}
}

View File

@@ -258,7 +258,7 @@ class Response
*
* @param Request $request A Request instance
*
* @return Response The current response.
* @return Response The current response
*/
public function prepare(Request $request)
{

View File

@@ -230,7 +230,7 @@ class ResponseHeaderBag extends HeaderBag
* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @return string A string suitable for use as a Content-Disposition field-value.
* @return string A string suitable for use as a Content-Disposition field-value
*
* @throws \InvalidArgumentException
*

View File

@@ -29,8 +29,8 @@ class NamespacedAttributeBag extends AttributeBag
/**
* Constructor.
*
* @param string $storageKey Session storage key.
* @param string $namespaceCharacter Namespace character to use in keys.
* @param string $storageKey Session storage key
* @param string $namespaceCharacter Namespace character to use in keys
*/
public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/')
{

View File

@@ -37,7 +37,7 @@ class AutoExpireFlashBag implements FlashBagInterface
/**
* Constructor.
*
* @param string $storageKey The key used to store flashes in the session.
* @param string $storageKey The key used to store flashes in the session
*/
public function __construct($storageKey = '_sf2_flashes')
{

View File

@@ -37,7 +37,7 @@ class FlashBag implements FlashBagInterface
/**
* Constructor.
*
* @param string $storageKey The key used to store flashes in the session.
* @param string $storageKey The key used to store flashes in the session
*/
public function __construct($storageKey = '_sf2_flashes')
{

View File

@@ -39,8 +39,8 @@ interface FlashBagInterface extends SessionBagInterface
/**
* Gets flash messages for a given type.
*
* @param string $type Message category type.
* @param array $default Default value if $type does not exist.
* @param string $type Message category type
* @param array $default Default value if $type does not exist
*
* @return array
*/
@@ -57,7 +57,7 @@ interface FlashBagInterface extends SessionBagInterface
* Gets and clears flash from the stack.
*
* @param string $type
* @param array $default Default value if $type does not exist.
* @param array $default Default value if $type does not exist
*
* @return array
*/

View File

@@ -46,7 +46,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
/**
* Constructor.
*
* @param SessionStorageInterface $storage A SessionStorageInterface instance.
* @param SessionStorageInterface $storage A SessionStorageInterface instance
* @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
* @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
*/

View File

@@ -42,7 +42,7 @@ interface SessionBagInterface
/**
* Clears out data from bag.
*
* @return mixed Whatever data was contained.
* @return mixed Whatever data was contained
*/
public function clear();
}

View File

@@ -23,7 +23,7 @@ interface SessionInterface
/**
* Starts the session storage.
*
* @return bool True if session started.
* @return bool True if session started
*
* @throws \RuntimeException If session fails to start.
*/
@@ -32,7 +32,7 @@ interface SessionInterface
/**
* Returns the session ID.
*
* @return string The session ID.
* @return string The session ID
*/
public function getId();
@@ -46,7 +46,7 @@ interface SessionInterface
/**
* Returns the session name.
*
* @return mixed The session name.
* @return mixed The session name
*/
public function getName();
@@ -68,7 +68,7 @@ interface SessionInterface
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*
* @return bool True if session invalidated, false if error.
* @return bool True if session invalidated, false if error
*/
public function invalidate($lifetime = null);
@@ -76,13 +76,13 @@ interface SessionInterface
* Migrates the current session to a new session id while maintaining all
* session attributes.
*
* @param bool $destroy Whether to delete the old session or leave it to garbage collection.
* @param bool $destroy Whether to delete the old session or leave it to garbage collection
* @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
* will leave the system settings unchanged, 0 sets the cookie
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*
* @return bool True if session migrated, false if error.
* @return bool True if session migrated, false if error
*/
public function migrate($destroy = false, $lifetime = null);
@@ -108,7 +108,7 @@ interface SessionInterface
* Returns an attribute.
*
* @param string $name The attribute name
* @param mixed $default The default value if not found.
* @param mixed $default The default value if not found
*
* @return mixed
*/

View File

@@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
class MemcacheSessionHandler implements \SessionHandlerInterface
{
/**
* @var \Memcache Memcache driver.
* @var \Memcache Memcache driver
*/
private $memcache;
@@ -29,7 +29,7 @@ class MemcacheSessionHandler implements \SessionHandlerInterface
private $ttl;
/**
* @var string Key prefix for shared environments.
* @var string Key prefix for shared environments
*/
private $prefix;

View File

@@ -24,7 +24,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
class MemcachedSessionHandler implements \SessionHandlerInterface
{
/**
* @var \Memcached Memcached driver.
* @var \Memcached Memcached driver
*/
private $memcached;
@@ -34,7 +34,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
private $ttl;
/**
* @var string Key prefix for shared environments.
* @var string Key prefix for shared environments
*/
private $prefix;

View File

@@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
class MongoDbSessionHandler implements \SessionHandlerInterface
{
/**
* @var \Mongo
* @var \Mongo|\MongoClient|\MongoDB\Client
*/
private $mongo;
@@ -61,15 +61,15 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
* If you use such an index, you can drop `gc_probability` to 0 since
* no garbage-collection is required.
*
* @param \Mongo|\MongoClient $mongo A MongoClient or Mongo instance
* @param array $options An associative array of field options
* @param \Mongo|\MongoClient|\MongoDB\Client $mongo A MongoDB\Client, MongoClient or Mongo instance
* @param array $options An associative array of field options
*
* @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
* @throws \InvalidArgumentException When "database" or "collection" not provided
*/
public function __construct($mongo, array $options)
{
if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
throw new \InvalidArgumentException('MongoClient or Mongo instance required');
}
@@ -108,7 +108,9 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
*/
public function destroy($sessionId)
{
$this->getCollection()->remove(array(
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
$this->getCollection()->$methodName(array(
$this->options['id_field'] => $sessionId,
));
@@ -120,8 +122,10 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
*/
public function gc($maxlifetime)
{
$this->getCollection()->remove(array(
$this->options['expiry_field'] => array('$lt' => new \MongoDate()),
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
$this->getCollection()->$methodName(array(
$this->options['expiry_field'] => array('$lt' => $this->createDateTime()),
));
return true;
@@ -132,18 +136,28 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
*/
public function write($sessionId, $data)
{
$expiry = new \MongoDate(time() + (int) ini_get('session.gc_maxlifetime'));
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
$fields = array(
$this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY),
$this->options['time_field'] => new \MongoDate(),
$this->options['time_field'] => $this->createDateTime(),
$this->options['expiry_field'] => $expiry,
);
$this->getCollection()->update(
$options = array('upsert' => true);
if ($this->mongo instanceof \MongoDB\Client) {
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
} else {
$fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY);
$options['multiple'] = false;
}
$methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update';
$this->getCollection()->$methodName(
array($this->options['id_field'] => $sessionId),
array('$set' => $fields),
array('upsert' => true, 'multiple' => false)
$options
);
return true;
@@ -156,10 +170,18 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
{
$dbData = $this->getCollection()->findOne(array(
$this->options['id_field'] => $sessionId,
$this->options['expiry_field'] => array('$gte' => new \MongoDate()),
$this->options['expiry_field'] => array('$gte' => $this->createDateTime()),
));
return null === $dbData ? '' : $dbData[$this->options['data_field']]->bin;
if (null === $dbData) {
return '';
}
if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) {
return $dbData[$this->options['data_field']]->getData();
}
return $dbData[$this->options['data_field']]->bin;
}
/**
@@ -179,10 +201,30 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
/**
* Return a Mongo instance.
*
* @return \Mongo
* @return \Mongo|\MongoClient|\MongoDB\Client
*/
protected function getMongo()
{
return $this->mongo;
}
/**
* Create a date object using the class appropriate for the current mongo connection.
*
* Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime
*
* @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now.
*/
private function createDateTime($seconds = null)
{
if (null === $seconds) {
$seconds = time();
}
if ($this->mongo instanceof \MongoDB\Client) {
return new \MongoDB\BSON\UTCDateTime($seconds * 1000);
}
return new \MongoDate($seconds);
}
}

View File

@@ -23,7 +23,7 @@ class NativeFileSessionHandler extends NativeSessionHandler
/**
* Constructor.
*
* @param string $savePath Path of directory to save session files.
* @param string $savePath Path of directory to save session files
* Default null will leave setting as defined by PHP.
* '/path', 'N;/path', or 'N;octal-mode;/path
*

View File

@@ -325,14 +325,8 @@ class PdoSessionHandler implements \SessionHandlerInterface
try {
// We use a single MERGE SQL query when supported by the database.
$mergeSql = $this->getMergeSql();
if (null !== $mergeSql) {
$mergeStmt = $this->pdo->prepare($mergeSql);
$mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
$mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$mergeStmt = $this->getMergeStatement($sessionId, $data, $maxlifetime);
if (null !== $mergeStmt) {
$mergeStmt->execute();
return true;
@@ -347,7 +341,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$updateStmt->execute();
// When MERGE is not supported, like in Postgres, we have to use this approach that can result in
// When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
// duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
// We can just catch such an error and re-execute the update. This is similar to a serializable
// transaction with retry logic on serialization failures but without the overhead and without possible
@@ -510,54 +504,51 @@ class PdoSessionHandler implements \SessionHandlerInterface
$selectSql = $this->getSelectSql();
$selectStmt = $this->pdo->prepare($selectSql);
$selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$selectStmt->execute();
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
do {
$selectStmt->execute();
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
if ($sessionRows) {
if ($sessionRows[0][1] + $sessionRows[0][2] < time()) {
$this->sessionExpired = true;
return '';
}
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
}
if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
// until other connections to the session are committed.
try {
$insertStmt = $this->pdo->prepare(
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"
);
$insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$insertStmt->bindValue(':data', '', \PDO::PARAM_LOB);
$insertStmt->bindValue(':lifetime', 0, \PDO::PARAM_INT);
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$insertStmt->execute();
} catch (\PDOException $e) {
// Catch duplicate key error because other connection created the session already.
// It would only not be the case when the other connection destroyed the session.
if (0 === strpos($e->getCode(), '23')) {
// Retrieve finished session data written by concurrent connection. SELECT
// FOR UPDATE is necessary to avoid deadlock of connection that starts reading
// before we write (transform intention to real lock).
$selectStmt->execute();
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
if ($sessionRows) {
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
}
if ($sessionRows) {
if ($sessionRows[0][1] + $sessionRows[0][2] < time()) {
$this->sessionExpired = true;
return '';
}
throw $e;
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
}
}
return '';
if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
// until other connections to the session are committed.
try {
$insertStmt = $this->pdo->prepare(
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"
);
$insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$insertStmt->bindValue(':data', '', \PDO::PARAM_LOB);
$insertStmt->bindValue(':lifetime', 0, \PDO::PARAM_INT);
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$insertStmt->execute();
} catch (\PDOException $e) {
// Catch duplicate key error because other connection created the session already.
// It would only not be the case when the other connection destroyed the session.
if (0 === strpos($e->getCode(), '23')) {
// Retrieve finished session data written by concurrent connection by restarting the loop.
// We have to start a new transaction as a failed query will mark the current transaction as
// aborted in PostgreSQL and disallow further queries within it.
$this->rollback();
$this->beginTransaction();
continue;
}
throw $e;
}
}
return '';
} while (true);
}
/**
@@ -653,29 +644,64 @@ class PdoSessionHandler implements \SessionHandlerInterface
}
/**
* Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database for writing session data.
* Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
*
* @return string|null The SQL string or null when not supported
* @param string $sessionId Session ID
* @param string $data Encoded session data
* @param int $maxlifetime session.gc_maxlifetime
*
* @return \PDOStatement|null The merge statement or null when not supported
*/
private function getMergeSql()
private function getMergeStatement($sessionId, $data, $maxlifetime)
{
switch ($this->driver) {
case 'mysql':
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
$mergeSql = null;
switch (true) {
case 'mysql' === $this->driver:
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
case 'oci':
break;
case 'oci' === $this->driver:
// DUAL is Oracle specific dummy table
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time";
$mergeSql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = ?) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?";
break;
case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='):
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time;";
case 'sqlite':
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
$mergeSql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
break;
case 'sqlite' === $this->driver:
$mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
break;
case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
break;
}
if (null !== $mergeSql) {
$mergeStmt = $this->pdo->prepare($mergeSql);
if ('sqlsrv' === $this->driver || 'oci' === $this->driver) {
$mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
$mergeStmt->bindParam(4, $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(5, time(), \PDO::PARAM_INT);
$mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB);
$mergeStmt->bindParam(7, $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(8, time(), \PDO::PARAM_INT);
} else {
$mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
$mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
}
return $mergeStmt;
}
}

View File

@@ -56,7 +56,7 @@ class MetadataBag implements SessionBagInterface
/**
* Constructor.
*
* @param string $storageKey The key used to store bag in the session.
* @param string $storageKey The key used to store bag in the session
* @param int $updateThreshold The time to wait between two UPDATED updates
*/
public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0)

View File

@@ -66,7 +66,7 @@ class MockArraySessionStorage implements SessionStorageInterface
* Constructor.
*
* @param string $name Session name
* @param MetadataBag $metaBag MetadataBag instance.
* @param MetadataBag $metaBag MetadataBag instance
*/
public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null)
{

View File

@@ -32,9 +32,9 @@ class MockFileSessionStorage extends MockArraySessionStorage
/**
* Constructor.
*
* @param string $savePath Path of directory to save session files.
* @param string $name Session name.
* @param MetadataBag $metaBag MetadataBag instance.
* @param string $savePath Path of directory to save session files
* @param string $name Session name
* @param MetadataBag $metaBag MetadataBag instance
*/
public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null)
{

View File

@@ -91,9 +91,9 @@ class NativeSessionStorage implements SessionStorageInterface
* upload_progress.min-freq, "1"
* url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
*
* @param array $options Session configuration options.
* @param array $options Session configuration options
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
* @param MetadataBag $metaBag MetadataBag.
* @param MetadataBag $metaBag MetadataBag
*/
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
{
@@ -299,7 +299,7 @@ class NativeSessionStorage implements SessionStorageInterface
* For convenience we omit 'session.' from the beginning of the keys.
* Explicitly ignores other ini keys.
*
* @param array $options Session ini directives array(key => value).
* @param array $options Session ini directives array(key => value)
*
* @see http://php.net/session.configuration
*/

View File

@@ -32,7 +32,7 @@ class NativeProxy extends AbstractProxy
/**
* Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
*
* @return bool False.
* @return bool False
*/
public function isWrapper()
{

View File

@@ -24,7 +24,7 @@ interface SessionStorageInterface
/**
* Starts the session.
*
* @return bool True if started.
* @return bool True if started
*
* @throws \RuntimeException If something goes wrong starting the session.
*/
@@ -33,14 +33,14 @@ interface SessionStorageInterface
/**
* Checks if the session is started.
*
* @return bool True if started, false otherwise.
* @return bool True if started, false otherwise
*/
public function isStarted();
/**
* Returns the session ID.
*
* @return string The session ID or empty.
* @return string The session ID or empty
*/
public function getId();
@@ -54,7 +54,7 @@ interface SessionStorageInterface
/**
* Returns the session name.
*
* @return mixed The session name.
* @return mixed The session name
*/
public function getName();

View File

@@ -923,6 +923,74 @@ class RequestTest extends \PHPUnit_Framework_TestCase
);
}
/**
* @expectedException \Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException
* @dataProvider testGetClientIpsWithConflictingHeadersProvider
*/
public function testGetClientIpsWithConflictingHeaders($httpForwarded, $httpXForwardedFor)
{
$request = new Request();
$server = array(
'REMOTE_ADDR' => '88.88.88.88',
'HTTP_FORWARDED' => $httpForwarded,
'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
);
Request::setTrustedProxies(array('88.88.88.88'));
$request->initialize(array(), array(), array(), array(), array(), $server);
$request->getClientIps();
}
public function testGetClientIpsWithConflictingHeadersProvider()
{
// $httpForwarded $httpXForwardedFor
return array(
array('for=87.65.43.21', '192.0.2.60'),
array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60'),
array('for=192.0.2.60', '192.0.2.60,87.65.43.21'),
array('for="::face", for=192.0.2.60', '192.0.2.60,192.0.2.43'),
array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60,87.65.43.21'),
);
}
/**
* @dataProvider testGetClientIpsWithAgreeingHeadersProvider
*/
public function testGetClientIpsWithAgreeingHeaders($httpForwarded, $httpXForwardedFor)
{
$request = new Request();
$server = array(
'REMOTE_ADDR' => '88.88.88.88',
'HTTP_FORWARDED' => $httpForwarded,
'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
);
Request::setTrustedProxies(array('88.88.88.88'));
$request->initialize(array(), array(), array(), array(), array(), $server);
$request->getClientIps();
Request::setTrustedProxies(array());
}
public function testGetClientIpsWithAgreeingHeadersProvider()
{
// $httpForwarded $httpXForwardedFor
return array(
array('for="192.0.2.60"', '192.0.2.60'),
array('for=192.0.2.60, for=87.65.43.21', '192.0.2.60,87.65.43.21'),
array('for="[::face]", for=192.0.2.60', '::face,192.0.2.60'),
array('for="192.0.2.60:80"', '192.0.2.60'),
array('for=192.0.2.60;proto=http;by=203.0.113.43', '192.0.2.60'),
array('for="[2001:db8:cafe::17]:4711"', '2001:db8:cafe::17'),
);
}
public function testGetContentWorksTwiceInDefaultMode()
{
$req = new Request();
@@ -1866,6 +1934,32 @@ class RequestTest extends \PHPUnit_Framework_TestCase
array(str_repeat(':', 101)),
);
}
/**
* @dataProvider methodSafeProvider
*/
public function testMethodSafe($method, $safe)
{
$request = new Request();
$request->setMethod($method);
$this->assertEquals($safe, $request->isMethodSafe());
}
public function methodSafeProvider()
{
return array(
array('HEAD', true),
array('GET', true),
array('POST', false),
array('PUT', false),
array('PATCH', false),
array('DELETE', false),
array('PURGE', false),
array('OPTIONS', true),
array('TRACE', true),
array('CONNECT', false),
);
}
}
class RequestContentProxy extends Request

View File

@@ -6,7 +6,7 @@
* (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.
* file this was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
@@ -15,7 +15,6 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandl
/**
* @author Markus Bachmann <markus.bachmann@bachi.biz>
* @requires extension mongo
* @group time-sensitive
*/
class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
@@ -31,7 +30,15 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
{
parent::setUp();
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
if (!extension_loaded('mongo') && !extension_loaded('mongodb')) {
$this->markTestSkipped('The Mongo or MongoDB extension is required.');
}
if (phpversion('mongodb')) {
$mongoClass = 'MongoDB\Client';
} else {
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
}
$this->mongo = $this->getMockBuilder($mongoClass)
->disableOriginalConstructor()
@@ -96,14 +103,28 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertArrayHasKey($this->options['expiry_field'], $criteria);
$this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
$this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
return array(
if (phpversion('mongodb')) {
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
$this->assertGreaterThanOrEqual(round(intval((string) $criteria[$this->options['expiry_field']]['$gte']) / 1000), $testTimeout);
} else {
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
$this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
}
$fields = array(
$this->options['id_field'] => 'foo',
$this->options['data_field'] => new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY),
$this->options['id_field'] => new \MongoDate(),
);
if (phpversion('mongodb')) {
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
$fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
} else {
$fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
$fields[$this->options['id_field']] = new \MongoDate();
}
return $fields;
}));
$this->assertEquals('bar', $this->storage->read('foo'));
@@ -120,11 +141,18 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
$data = array();
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->once())
->method('update')
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
$this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
if (phpversion('mongodb')) {
$this->assertEquals(array('upsert' => true), $options);
} else {
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
}
$data = $updateData['$set'];
}));
@@ -132,10 +160,17 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
$this->assertTrue($this->storage->write('foo', 'bar'));
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
$this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
if (phpversion('mongodb')) {
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
$this->assertGreaterThanOrEqual($expectedExpiry, round(intval((string) $data[$this->options['expiry_field']]) / 1000));
} else {
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
$this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
}
}
public function testWriteWhenUsingExpiresField()
@@ -160,20 +195,33 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
$data = array();
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->once())
->method('update')
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
$this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
if (phpversion('mongodb')) {
$this->assertEquals(array('upsert' => true), $options);
} else {
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
}
$data = $updateData['$set'];
}));
$this->assertTrue($this->storage->write('foo', 'bar'));
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
if (phpversion('mongodb')) {
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
} else {
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
}
}
public function testReplaceSessionData()
@@ -187,8 +235,10 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
$data = array();
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->exactly(2))
->method('update')
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
$data = $updateData;
}));
@@ -196,7 +246,11 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
$this->storage->write('foo', 'bar');
$this->storage->write('foo', 'foobar');
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
if (phpversion('mongodb')) {
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
} else {
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
}
}
public function testDestroy()
@@ -208,8 +262,10 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
$collection->expects($this->once())
->method('remove')
->method($methodName)
->with(array($this->options['id_field'] => 'foo'));
$this->assertTrue($this->storage->destroy('foo'));
@@ -224,11 +280,18 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
$collection->expects($this->once())
->method('remove')
->method($methodName)
->will($this->returnCallback(function ($criteria) {
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
$this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
if (phpversion('mongodb')) {
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']);
$this->assertGreaterThanOrEqual(time() - 1, round(intval((string) $criteria[$this->options['expiry_field']]['$lt']) / 1000));
} else {
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
$this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
}
}));
$this->assertTrue($this->storage->gc(1));
@@ -239,14 +302,23 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
$method = new \ReflectionMethod($this->storage, 'getMongo');
$method->setAccessible(true);
$mongoClass = (version_compare(phpversion('mongo'), '1.3.0', '<')) ? '\Mongo' : '\MongoClient';
if (phpversion('mongodb')) {
$mongoClass = 'MongoDB\Client';
} else {
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
}
$this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
}
private function createMongoCollectionMock()
{
$collection = $this->getMockBuilder('MongoCollection')
$collectionClass = 'MongoCollection';
if (phpversion('mongodb')) {
$collectionClass = 'MongoDB\Collection';
}
$collection = $this->getMockBuilder($collectionClass)
->disableOriginalConstructor()
->getMock();

View File

@@ -362,4 +362,8 @@ class MockPdo extends \PDO
public function beginTransaction()
{
}
public function rollBack()
{
}
}

View File

@@ -21,7 +21,7 @@ interface CacheClearerInterface
/**
* Clears any caches necessary.
*
* @param string $cacheDir The cache directory.
* @param string $cacheDir The cache directory
*/
public function clear($cacheDir);
}

View File

@@ -26,7 +26,7 @@ class ChainCacheClearer implements CacheClearerInterface
/**
* Constructs a new instance of ChainCacheClearer.
*
* @param array $clearers The initial clearers.
* @param array $clearers The initial clearers
*/
public function __construct(array $clearers = array())
{

View File

@@ -20,7 +20,7 @@ abstract class CacheWarmer implements CacheWarmerInterface
{
protected function writeCacheFile($file, $content)
{
$tmpFile = tempnam(dirname($file), basename($file));
$tmpFile = @tempnam(dirname($file), basename($file));
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
@chmod($file, 0666 & ~umask());

View File

@@ -53,7 +53,12 @@ class FragmentRendererPass implements CompilerPassInterface
$class = $container->getParameterBag()->resolveValue($def->getClass());
$interface = 'Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface';
if (!is_subclass_of($class, $interface)) {
if (!class_exists($class, false)) {
throw new \InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
}

View File

@@ -28,8 +28,8 @@ class DumpListener implements EventSubscriberInterface
private $dumper;
/**
* @param ClonerInterface $cloner Cloner service.
* @param DataDumperInterface $dumper Dumper service.
* @param ClonerInterface $cloner Cloner service
* @param DataDumperInterface $dumper Dumper service
*/
public function __construct(ClonerInterface $cloner, DataDumperInterface $dumper)
{

View File

@@ -96,10 +96,10 @@ class ExceptionListener implements EventSubscriberInterface
/**
* Clones the request for the exception.
*
* @param \Exception $exception The thrown exception.
* @param Request $request The original request.
* @param \Exception $exception The thrown exception
* @param Request $request The original request
*
* @return Request $request The cloned request.
* @return Request $request The cloned request
*/
protected function duplicateRequest(\Exception $exception, Request $request)
{

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Validates that the headers and other information indicating the
* client IP address of a request are consistent.
*
* @author Magnus Nordlander <magnus@fervo.se>
*/
class ValidateRequestListener implements EventSubscriberInterface
{
/**
* Performs the validation.
*
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
$request = $event->getRequest();
if ($request::getTrustedProxies()) {
// This will throw an exception if the headers are inconsistent.
$request->getClientIps();
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array(
array('onKernelRequest', 256),
),
);
}
}

View File

@@ -129,6 +129,8 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
}
$server['REMOTE_ADDR'] = '127.0.0.1';
unset($server['HTTP_IF_MODIFIED_SINCE']);
unset($server['HTTP_IF_NONE_MATCH']);
$subRequest = Request::create($uri, 'get', array(), $cookies, array(), $server);
if ($request->headers->has('Surrogate-Capability')) {

View File

@@ -37,7 +37,7 @@ class Esi implements SurrogateInterface
/**
* Constructor.
*
* @param array $contentTypes An array of content-type that should be parsed for ESI information.
* @param array $contentTypes An array of content-type that should be parsed for ESI information
* (default: text/html, text/xml, application/xhtml+xml, and application/xml)
*/
public function __construct(array $contentTypes = array('text/html', 'text/xml', 'application/xhtml+xml', 'application/xml'))

View File

@@ -580,6 +580,9 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
*/
protected function store(Request $request, Response $response)
{
if (!$response->headers->has('Date')) {
$response->setDate(\DateTime::createFromFormat('U', time()));
}
try {
$this->store->write($request, $response);

View File

@@ -31,7 +31,7 @@ class Ssi implements SurrogateInterface
/**
* Constructor.
*
* @param array $contentTypes An array of content-type that should be parsed for SSI information.
* @param array $contentTypes An array of content-type that should be parsed for SSI information
* (default: text/html, text/xml, application/xhtml+xml, and application/xml)
*/
public function __construct(array $contentTypes = array('text/html', 'text/xml', 'application/xhtml+xml', 'application/xml'))

View File

@@ -38,7 +38,7 @@ class Store implements StoreInterface
public function __construct($root)
{
$this->root = $root;
if (!is_dir($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root));
}
$this->keyCache = new \SplObjectStorage();
@@ -52,22 +52,15 @@ class Store implements StoreInterface
{
// unlock everything
foreach ($this->locks as $lock) {
if (file_exists($lock)) {
@unlink($lock);
}
flock($lock, LOCK_UN);
fclose($lock);
}
$error = error_get_last();
if (1 === $error['type'] && false === headers_sent()) {
// send a 503
header('HTTP/1.0 503 Service Unavailable');
header('Retry-After: 10');
echo '503 Service Unavailable';
}
$this->locks = array();
}
/**
* Locks the cache for a given Request.
* Tries to lock the cache for a given Request, without blocking.
*
* @param Request $request A Request instance
*
@@ -75,21 +68,24 @@ class Store implements StoreInterface
*/
public function lock(Request $request)
{
$path = $this->getPath($this->getCacheKey($request).'.lck');
if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
return false;
$key = $this->getCacheKey($request);
if (!isset($this->locks[$key])) {
$path = $this->getPath($key);
if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
return $path;
}
$h = fopen($path, 'cb');
if (!flock($h, LOCK_EX | LOCK_NB)) {
fclose($h);
return $path;
}
$this->locks[$key] = $h;
}
$lock = @fopen($path, 'x');
if (false !== $lock) {
fclose($lock);
$this->locks[] = $path;
return true;
}
return !file_exists($path) ?: $path;
return true;
}
/**
@@ -101,17 +97,37 @@ class Store implements StoreInterface
*/
public function unlock(Request $request)
{
$file = $this->getPath($this->getCacheKey($request).'.lck');
$key = $this->getCacheKey($request);
return is_file($file) ? @unlink($file) : false;
if (isset($this->locks[$key])) {
flock($this->locks[$key], LOCK_UN);
fclose($this->locks[$key]);
unset($this->locks[$key]);
return true;
}
return false;
}
public function isLocked(Request $request)
{
$path = $this->getPath($this->getCacheKey($request).'.lck');
clearstatcache(true, $path);
$key = $this->getCacheKey($request);
return is_file($path);
if (isset($this->locks[$key])) {
return true; // shortcut if lock held by this process
}
if (!file_exists($path = $this->getPath($key))) {
return false;
}
$h = fopen($path, 'rb');
flock($h, LOCK_EX | LOCK_NB, $wouldBlock);
flock($h, LOCK_UN); // release the lock we just acquired
fclose($h);
return (bool) $wouldBlock;
}
/**
@@ -144,7 +160,7 @@ class Store implements StoreInterface
}
list($req, $headers) = $match;
if (is_file($body = $this->getPath($headers['x-content-digest'][0]))) {
if (file_exists($body = $this->getPath($headers['x-content-digest'][0]))) {
return $this->restoreResponse($headers, $body);
}
@@ -291,7 +307,7 @@ class Store implements StoreInterface
*/
private function getMetadata($key)
{
if (false === $entries = $this->load($key)) {
if (!$entries = $this->load($key)) {
return array();
}
@@ -307,7 +323,15 @@ class Store implements StoreInterface
*/
public function purge($url)
{
if (is_file($path = $this->getPath($this->getCacheKey(Request::create($url))))) {
$key = $this->getCacheKey(Request::create($url));
if (isset($this->locks[$key])) {
flock($this->locks[$key], LOCK_UN);
fclose($this->locks[$key]);
unset($this->locks[$key]);
}
if (file_exists($path = $this->getPath($key))) {
unlink($path);
return true;
@@ -327,7 +351,7 @@ class Store implements StoreInterface
{
$path = $this->getPath($key);
return is_file($path) ? file_get_contents($path) : false;
return file_exists($path) ? file_get_contents($path) : false;
}
/**
@@ -341,23 +365,36 @@ class Store implements StoreInterface
private function save($key, $data)
{
$path = $this->getPath($key);
if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
return false;
}
$tmpFile = tempnam(dirname($path), basename($path));
if (false === $fp = @fopen($tmpFile, 'wb')) {
return false;
}
@fwrite($fp, $data);
@fclose($fp);
if (isset($this->locks[$key])) {
$fp = $this->locks[$key];
@ftruncate($fp, 0);
@fseek($fp, 0);
$len = @fwrite($fp, $data);
if (strlen($data) !== $len) {
@ftruncate($fp, 0);
if ($data != file_get_contents($tmpFile)) {
return false;
}
return false;
}
} else {
if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
return false;
}
if (false === @rename($tmpFile, $path)) {
return false;
$tmpFile = tempnam(dirname($path), basename($path));
if (false === $fp = @fopen($tmpFile, 'wb')) {
return false;
}
@fwrite($fp, $data);
@fclose($fp);
if ($data != file_get_contents($tmpFile)) {
return false;
}
if (false === @rename($tmpFile, $path)) {
return false;
}
}
@chmod($path, 0666 & ~umask());

View File

@@ -12,6 +12,7 @@
namespace Symfony\Component\HttpKernel;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
@@ -21,6 +22,7 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
@@ -61,6 +63,9 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
try {
return $this->handleRaw($request, $type);
} catch (\Exception $e) {
if ($e instanceof ConflictingHeadersException) {
$e = new BadRequestHttpException('The request headers contain conflicting information regarding the origin of this request.', $e);
}
if (false === $catch) {
$this->finishRequest($request, $type);

View File

@@ -59,11 +59,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $startTime;
protected $loadClassCache;
const VERSION = '3.0.7';
const VERSION_ID = 30007;
const VERSION = '3.0.9';
const VERSION_ID = 30009;
const MAJOR_VERSION = 3;
const MINOR_VERSION = 0;
const RELEASE_VERSION = 7;
const RELEASE_VERSION = 9;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '07/2016';

Some files were not shown because too many files have changed in this diff Show More