Update core 8.3.0

This commit is contained in:
Rob Davies 2017-04-13 15:53:35 +01:00
parent da7a7918f8
commit cd7a898e66
6144 changed files with 132297 additions and 87747 deletions

View file

@ -21,7 +21,7 @@
"symfony/polyfill-apcu": "~1.1"
},
"require-dev": {
"symfony/finder": "~2.0,>=2.0.5|~3.0.0"
"symfony/finder": "^2.0.5|~3.0.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\ClassLoader\\": "" },

View file

@ -14,6 +14,7 @@ namespace Symfony\Component\Console;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\DebugFormatterHelper;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Helper\QuestionHelper;
@ -652,12 +653,11 @@ class Application
if (defined('HHVM_VERSION') && $width > 1 << 31) {
$width = 1 << 31;
}
$formatter = $output->getFormatter();
$lines = array();
foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
// pre-format lines to get the right string length
$lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
$lineLength = $this->stringWidth($line) + 4;
$lines[] = array($line, $lineLength);
$len = max($lineLength, $len);
@ -665,15 +665,15 @@ class Application
}
$messages = array();
$messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
$messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
$messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))));
foreach ($lines as $line) {
$messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
$messages[] = sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
}
$messages[] = $emptyLine;
$messages[] = '';
$output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET);
$output->writeln($messages, OutputInterface::VERBOSITY_QUIET);
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);

View file

@ -105,6 +105,11 @@ abstract class Helper implements HelperInterface
}
public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string)
{
return self::strlen(self::removeDecoration($formatter, $string));
}
public static function removeDecoration(OutputFormatterInterface $formatter, $string)
{
$isDecorated = $formatter->isDecorated();
$formatter->setDecorated(false);
@ -114,6 +119,6 @@ abstract class Helper implements HelperInterface
$string = preg_replace("/\033\[[^m]*m/", '', $string);
$formatter->setDecorated($isDecorated);
return self::strlen($string);
return $string;
}
}

View file

@ -38,7 +38,7 @@ class QuestionHelper extends Helper
* @param OutputInterface $output An OutputInterface instance
* @param Question $question The question to ask
*
* @return string The user answer
* @return mixed The user answer
*
* @throws RuntimeException If there is no data to read in the input stream
*/
@ -384,7 +384,7 @@ class QuestionHelper extends Helper
* @param OutputInterface $output An Output instance
* @param Question $question A Question instance
*
* @return string The validated response
* @return mixed The validated response
*
* @throws \Exception In case the max number of attempts has been reached and no valid response has been given
*/

View file

@ -387,7 +387,7 @@ class Table
if (!strstr($cell, "\n")) {
continue;
}
$lines = explode("\n", $cell);
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
foreach ($lines as $lineKey => $line) {
if ($cell instanceof TableCell) {
$line = new TableCell($line, array('colspan' => $cell->getColspan()));
@ -428,7 +428,7 @@ class Table
$nbLines = $cell->getRowspan() - 1;
$lines = array($cell);
if (strstr($cell, "\n")) {
$lines = explode("\n", $cell);
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
$nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
$rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan()));
@ -440,6 +440,9 @@ class Table
foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
$value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : '';
$unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan()));
if ($nbLines === $unmergedRowKey - $line) {
break;
}
}
}
}
@ -560,9 +563,10 @@ class Table
foreach ($row as $i => $cell) {
if ($cell instanceof TableCell) {
$textLength = strlen($cell);
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
$textLength = Helper::strlen($textContent);
if ($textLength > 0) {
$contentColumns = str_split($cell, ceil($textLength / $cell->getColspan()));
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
foreach ($contentColumns as $position => $content) {
$row[$i + $position] = $content;
}

View file

@ -37,6 +37,10 @@ class TableCell
*/
public function __construct($value = '', array $options = array())
{
if (is_numeric($value) && !is_string($value)) {
$value = (string) $value;
}
$this->value = $value;
// check option names

View file

@ -42,7 +42,7 @@ class BufferedOutput extends Output
$this->buffer .= $message;
if ($newline) {
$this->buffer .= "\n";
$this->buffer .= PHP_EOL;
}
}
}

View file

@ -70,9 +70,7 @@ class CommandTester
}
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
$this->output->setDecorated($options['decorated']);
}
$this->output->setDecorated(isset($options['decorated']) ? $options['decorated'] : false);
if (isset($options['verbosity'])) {
$this->output->setVerbosity($options['verbosity']);
}

View file

@ -18,7 +18,7 @@
"require": {
"php": ">=5.3.9",
"symfony/polyfill-mbstring": "~1.0",
"symfony/debug": "~2.7,>=2.7.2|~3.0.0"
"symfony/debug": "^2.7.2|~3.0.0"
},
"require-dev": {
"symfony/event-dispatcher": "~2.1|~3.0.0",

View file

@ -375,12 +375,10 @@ class ErrorHandler
/**
* Handles errors by filtering then logging them according to the configured bit fields.
*
* @param int $type One of the E_* constants
* @param int $type One of the E_* constants
* @param string $message
* @param string $file
* @param int $line
* @param array $context
* @param array $backtrace
*
* @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
*
@ -388,7 +386,7 @@ class ErrorHandler
*
* @internal
*/
public function handleError($type, $message, $file, $line, array $context, array $backtrace = null)
public function handleError($type, $message, $file, $line)
{
$level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
$log = $this->loggedErrors & $type;
@ -398,8 +396,17 @@ class ErrorHandler
if (!$type || (!$log && !$throw)) {
return $type && $log;
}
$scope = $this->scopedErrors & $type;
if (isset($context['GLOBALS']) && ($this->scopedErrors & $type)) {
if (4 < $numArgs = func_num_args()) {
$context = $scope ? (func_get_arg(4) ?: array()) : array();
$backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM
} else {
$context = array();
$backtrace = null;
}
if (isset($context['GLOBALS']) && $scope) {
$e = $context; // Whatever the signature of the method,
unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
$context = $e;
@ -418,7 +425,7 @@ class ErrorHandler
if (null !== self::$toStringException) {
$throw = self::$toStringException;
self::$toStringException = null;
} elseif (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
} elseif ($scope && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
// Checking for class existence is a work around for https://bugs.php.net/42098
$throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
} else {
@ -490,7 +497,7 @@ class ErrorHandler
$e = compact('type', 'file', 'line', 'level');
if ($type & $level) {
if ($this->scopedErrors & $type) {
if ($scope) {
$e['scope_vars'] = $context;
if ($trace) {
$e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);

View file

@ -179,7 +179,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
);
if ($prefix) {
$candidates = array_filter($candidates, function ($candidate) use ($prefix) {return 0 === strpos($candidate, $prefix);});
$candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); });
}
// We cannot use the autoloader here as most of them use require; but if the class

View file

@ -24,7 +24,7 @@
},
"require-dev": {
"symfony/class-loader": "~2.2|~3.0.0",
"symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2|~3.0.0"
"symfony/http-kernel": "~2.3.24|~2.5.9|^2.6.2|~3.0.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Debug\\": "" },

View file

@ -28,6 +28,7 @@ class AutowirePass implements CompilerPassInterface
private $definedTypes = array();
private $types;
private $notGuessableTypes = array();
private $autowired = array();
/**
* {@inheritdoc}
@ -56,6 +57,7 @@ class AutowirePass implements CompilerPassInterface
$this->definedTypes = array();
$this->types = null;
$this->notGuessableTypes = array();
$this->autowired = array();
if (isset($e)) {
throw $e;
@ -72,6 +74,10 @@ class AutowirePass implements CompilerPassInterface
*/
private function completeDefinition($id, Definition $definition)
{
if ($definition->getFactory() || $definition->getFactoryClass(false) || $definition->getFactoryService(false)) {
throw new RuntimeException(sprintf('Service "%s" can use either autowiring or a factory, not both.', $id));
}
if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
return;
}
@ -81,15 +87,23 @@ class AutowirePass implements CompilerPassInterface
if (!$constructor = $reflectionClass->getConstructor()) {
return;
}
$parameters = $constructor->getParameters();
if (method_exists('ReflectionMethod', 'isVariadic') && $constructor->isVariadic()) {
array_pop($parameters);
}
$arguments = $definition->getArguments();
foreach ($constructor->getParameters() as $index => $parameter) {
foreach ($parameters as $index => $parameter) {
if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
continue;
}
try {
if (!$typeHint = $parameter->getClass()) {
if (isset($arguments[$index])) {
continue;
}
// no default value? Then fail
if (!$parameter->isOptional()) {
throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id));
@ -101,6 +115,10 @@ class AutowirePass implements CompilerPassInterface
continue;
}
if (isset($this->autowired[$typeHint->name])) {
return $this->autowired[$typeHint->name] ? new Reference($this->autowired[$typeHint->name]) : null;
}
if (null === $this->types) {
$this->populateAvailableTypes();
}
@ -111,13 +129,14 @@ class AutowirePass implements CompilerPassInterface
try {
$value = $this->createAutowiredDefinition($typeHint, $id);
} catch (RuntimeException $e) {
if ($parameter->allowsNull()) {
$value = null;
} elseif ($parameter->isDefaultValueAvailable()) {
if ($parameter->isDefaultValueAvailable()) {
$value = $parameter->getDefaultValue();
} elseif ($parameter->allowsNull()) {
$value = null;
} else {
throw $e;
}
$this->autowired[$typeHint->name] = false;
}
}
} catch (\ReflectionException $e) {
@ -133,6 +152,16 @@ class AutowirePass implements CompilerPassInterface
$arguments[$index] = $value;
}
if ($parameters && !isset($arguments[++$index])) {
while (0 <= --$index) {
$parameter = $parameters[$index];
if (!$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== $arguments[$index]) {
break;
}
unset($arguments[$index]);
}
}
// it's possible index 1 was set, then index 0, then 2, etc
// make sure that we re-order so they're injected as expected
ksort($arguments);
@ -167,6 +196,7 @@ class AutowirePass implements CompilerPassInterface
foreach ($definition->getAutowiringTypes() as $type) {
$this->definedTypes[$type] = true;
$this->types[$type] = $id;
unset($this->notGuessableTypes[$type]);
}
if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
@ -236,13 +266,11 @@ class AutowirePass implements CompilerPassInterface
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface));
}
$argumentId = sprintf('autowired.%s', $typeHint->name);
$this->autowired[$typeHint->name] = $argumentId = sprintf('autowired.%s', $typeHint->name);
$argumentDefinition = $this->container->register($argumentId, $typeHint->name);
$argumentDefinition->setPublic(false);
$this->populateAvailableType($argumentId, $argumentDefinition);
try {
$this->completeDefinition($argumentId, $argumentDefinition);
} catch (RuntimeException $e) {

View file

@ -60,7 +60,6 @@ class CheckCircularReferencesPass implements CompilerPassInterface
$id = $node->getId();
if (empty($this->checkedNodes[$id])) {
// don't check circular dependencies for lazy services
if (!$node->getValue() || !$node->getValue()->isLazy()) {
$searchKey = array_search($id, $this->currentPath);

View file

@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
/**
@ -47,6 +48,9 @@ class MergeExtensionConfigurationPass implements CompilerPassInterface
$tmpContainer = new ContainerBuilder($container->getParameterBag());
$tmpContainer->setResourceTracking($container->isTrackingResources());
$tmpContainer->addObjectResource($extension);
if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) {
$tmpContainer->addObjectResource($configuration);
}
foreach ($exprLangProviders as $provider) {
$tmpContainer->addExpressionLanguageProvider($provider);

View file

@ -51,7 +51,7 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface
if (isset($replacements[$targetId])) {
$container->setAlias($definitionId, $replacements[$targetId]);
}
// No neeed to process the same target twice
// No need to process the same target twice
if (isset($seenAliasTargets[$targetId])) {
continue;
}

View file

@ -26,6 +26,7 @@ use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
@ -73,7 +74,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private $compiler;
private $trackResources = true;
private $trackResources;
/**
* @var InstantiatorInterface|null
@ -90,6 +91,13 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private $expressionLanguageProviders = array();
public function __construct(ParameterBagInterface $parameterBag = null)
{
parent::__construct($parameterBag);
$this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface');
}
/**
* @var string[] with tag names used by findTaggedServiceIds
*/

View file

@ -164,7 +164,7 @@ class Definition
}
/**
* Gets the service that decorates this service.
* Gets the service that this service is decorating.
*
* @return null|array An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated
*/
@ -308,6 +308,10 @@ class Definition
*/
public function replaceArgument($index, $argument)
{
if (0 === count($this->arguments)) {
throw new OutOfBoundsException('Cannot replace arguments if none have been configured yet.');
}
if ($index < 0 || $index > count($this->arguments) - 1) {
throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1));
}

View file

@ -628,7 +628,7 @@ EOF;
}
if ($definition->isAutowired()) {
$doc = <<<EOF
$doc .= <<<EOF
*
* This service is autowired.
@ -1029,11 +1029,7 @@ EOF;
private function addAliases()
{
if (!$aliases = $this->container->getAliases()) {
if ($this->container->isFrozen()) {
return "\n \$this->aliases = array();\n";
} else {
return '';
}
return $this->container->isFrozen() ? "\n \$this->aliases = array();\n" : '';
}
$code = " \$this->aliases = array(\n";
@ -1402,9 +1398,9 @@ EOF;
$service = $this->dumpValue($value->getFactoryService(false));
return sprintf('%s->%s(%s)', 0 === strpos($service, '$') ? sprintf('$this->get(%s)', $service) : $this->getServiceCall($value->getFactoryService(false)), $value->getFactoryMethod(false), implode(', ', $arguments));
} else {
throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
}
throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
}
$class = $value->getClass();
@ -1442,9 +1438,9 @@ EOF;
}
} elseif (is_object($value) || is_resource($value)) {
throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
} else {
return $this->export($value);
}
return $this->export($value);
}
/**
@ -1513,13 +1509,13 @@ EOF;
if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
} else {
if ($this->container->hasAlias($id)) {
$id = (string) $this->container->getAlias($id);
}
return sprintf('$this->get(\'%s\')', $id);
}
if ($this->container->hasAlias($id)) {
$id = (string) $this->container->getAlias($id);
}
return sprintf('$this->get(\'%s\')', $id);
}
/**

View file

@ -85,7 +85,7 @@ class YamlFileLoader extends FileLoader
* @param array $content
* @param string $file
*/
private function parseImports($content, $file)
private function parseImports(array $content, $file)
{
if (!isset($content['imports'])) {
return;
@ -112,7 +112,7 @@ class YamlFileLoader extends FileLoader
* @param array $content
* @param string $file
*/
private function parseDefinitions($content, $file)
private function parseDefinitions(array $content, $file)
{
if (!isset($content['services'])) {
return;
@ -130,9 +130,9 @@ class YamlFileLoader extends FileLoader
/**
* Parses a definition.
*
* @param string $id
* @param array $service
* @param string $file
* @param string $id
* @param array|string $service
* @param string $file
*
* @throws InvalidArgumentException When tags are invalid
*/
@ -353,7 +353,7 @@ class YamlFileLoader extends FileLoader
}
if (!file_exists($file)) {
throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file));
throw new InvalidArgumentException(sprintf('The file "%s" does not exist.', $file));
}
if (null === $this->yamlParser) {
@ -454,7 +454,7 @@ class YamlFileLoader extends FileLoader
*
* @param array $content
*/
private function loadFromExtensions($content)
private function loadFromExtensions(array $content)
{
foreach ($content as $namespace => $values) {
if (in_array($namespace, array('imports', 'parameters', 'services'))) {

View file

@ -18,7 +18,7 @@ use Symfony\Component\CssSelector\CssSelectorConverter;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Crawler extends \SplObjectStorage
class Crawler implements \Countable, \IteratorAggregate
{
/**
* @var string The current URI
@ -45,6 +45,11 @@ class Crawler extends \SplObjectStorage
*/
private $document;
/**
* @var \DOMElement[]
*/
private $nodes = array();
/**
* Whether the Crawler contains HTML or XML content (used when converting CSS to XPath).
*
@ -72,7 +77,7 @@ class Crawler extends \SplObjectStorage
*/
public function clear()
{
parent::removeAll($this);
$this->nodes = array();
$this->document = null;
}
@ -294,25 +299,19 @@ class Crawler extends \SplObjectStorage
}
if (null !== $this->document && $this->document !== $node->ownerDocument) {
@trigger_error('Attaching DOM nodes from multiple documents in a Crawler is deprecated as of 2.8 and will be forbidden in 3.0.', E_USER_DEPRECATED);
throw new \InvalidArgumentException('Attaching DOM nodes from multiple documents in the same crawler is forbidden.');
}
if (null === $this->document) {
$this->document = $node->ownerDocument;
}
parent::attach($node);
}
// Don't add duplicate nodes in the Crawler
if (in_array($node, $this->nodes, true)) {
return;
}
// Serializing and unserializing a crawler creates DOM objects in a corrupted state. DOM elements are not properly serializable.
public function unserialize($serialized)
{
throw new \BadMethodCallException('A Crawler cannot be serialized.');
}
public function serialize()
{
throw new \BadMethodCallException('A Crawler cannot be serialized.');
$this->nodes[] = $node;
}
/**
@ -320,14 +319,12 @@ class Crawler extends \SplObjectStorage
*
* @param int $position The position
*
* @return self
* @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)
{
foreach ($this as $i => $node) {
if ($i == $position) {
return $this->createSubCrawler($node);
}
if (isset($this->nodes[$position])) {
return $this->createSubCrawler($this->nodes[$position]);
}
return $this->createSubCrawler(null);
@ -352,7 +349,7 @@ class Crawler extends \SplObjectStorage
public function each(\Closure $closure)
{
$data = array();
foreach ($this as $i => $node) {
foreach ($this->nodes as $i => $node) {
$data[] = $closure($this->createSubCrawler($node), $i);
}
@ -365,11 +362,11 @@ class Crawler extends \SplObjectStorage
* @param int $offset
* @param int $length
*
* @return self
* @return Crawler A Crawler instance with the sliced nodes
*/
public function slice($offset = 0, $length = -1)
public function slice($offset = 0, $length = null)
{
return $this->createSubCrawler(iterator_to_array(new \LimitIterator($this, $offset, $length)));
return $this->createSubCrawler(array_slice($this->nodes, $offset, $length));
}
/**
@ -379,12 +376,12 @@ class Crawler extends \SplObjectStorage
*
* @param \Closure $closure An anonymous function
*
* @return self
* @return Crawler A Crawler instance with the selected nodes
*/
public function reduce(\Closure $closure)
{
$nodes = array();
foreach ($this as $i => $node) {
foreach ($this->nodes as $i => $node) {
if (false !== $closure($this->createSubCrawler($node), $i)) {
$nodes[] = $node;
}
@ -396,7 +393,7 @@ class Crawler extends \SplObjectStorage
/**
* Returns the first node of the current selection.
*
* @return self
* @return Crawler A Crawler instance with the first selected node
*/
public function first()
{
@ -406,23 +403,23 @@ class Crawler extends \SplObjectStorage
/**
* Returns the last node of the current selection.
*
* @return self
* @return Crawler A Crawler instance with the last selected node
*/
public function last()
{
return $this->eq(count($this) - 1);
return $this->eq(count($this->nodes) - 1);
}
/**
* Returns the siblings nodes of the current selection.
*
* @return self
* @return Crawler A Crawler instance with the sibling nodes
*
* @throws \InvalidArgumentException When current node is empty
*/
public function siblings()
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -432,13 +429,13 @@ class Crawler extends \SplObjectStorage
/**
* Returns the next siblings nodes of the current selection.
*
* @return self
* @return Crawler A Crawler instance with the next sibling nodes
*
* @throws \InvalidArgumentException When current node is empty
*/
public function nextAll()
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -448,13 +445,13 @@ class Crawler extends \SplObjectStorage
/**
* Returns the previous sibling nodes of the current selection.
*
* @return self
* @return Crawler A Crawler instance with the previous sibling nodes
*
* @throws \InvalidArgumentException
*/
public function previousAll()
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -464,13 +461,13 @@ class Crawler extends \SplObjectStorage
/**
* Returns the parents nodes of the current selection.
*
* @return self
* @return Crawler A Crawler instance with the parents nodes of the current selection
*
* @throws \InvalidArgumentException When current node is empty
*/
public function parents()
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -489,13 +486,13 @@ class Crawler extends \SplObjectStorage
/**
* Returns the children nodes of the current selection.
*
* @return self
* @return Crawler A Crawler instance with the children nodes
*
* @throws \InvalidArgumentException When current node is empty
*/
public function children()
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -515,7 +512,7 @@ class Crawler extends \SplObjectStorage
*/
public function attr($attribute)
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -533,7 +530,7 @@ class Crawler extends \SplObjectStorage
*/
public function nodeName()
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -549,7 +546,7 @@ class Crawler extends \SplObjectStorage
*/
public function text()
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -565,7 +562,7 @@ class Crawler extends \SplObjectStorage
*/
public function html()
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -596,7 +593,7 @@ class Crawler extends \SplObjectStorage
$count = count($attributes);
$data = array();
foreach ($this as $node) {
foreach ($this->nodes as $node) {
$elements = array();
foreach ($attributes as $attribute) {
if ('_text' === $attribute) {
@ -622,7 +619,7 @@ class Crawler extends \SplObjectStorage
*
* @param string $xpath An XPath expression
*
* @return self
* @return Crawler A new instance of Crawler with the filtered list of nodes
*/
public function filterXPath($xpath)
{
@ -643,7 +640,7 @@ class Crawler extends \SplObjectStorage
*
* @param string $selector A CSS selector
*
* @return self
* @return Crawler A new instance of Crawler with the filtered list of nodes
*
* @throws \RuntimeException if the CssSelector Component is not available
*/
@ -664,7 +661,7 @@ class Crawler extends \SplObjectStorage
*
* @param string $value The link text
*
* @return self
* @return Crawler A new instance of Crawler with the filtered list of nodes
*/
public function selectLink($value)
{
@ -679,7 +676,7 @@ class Crawler extends \SplObjectStorage
*
* @param string $value The button text
*
* @return self
* @return Crawler A new instance of Crawler with the filtered list of nodes
*/
public function selectButton($value)
{
@ -702,7 +699,7 @@ class Crawler extends \SplObjectStorage
*/
public function link($method = 'get')
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -725,7 +722,7 @@ class Crawler extends \SplObjectStorage
public function links()
{
$links = array();
foreach ($this as $node) {
foreach ($this->nodes as $node) {
if (!$node instanceof \DOMElement) {
throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_class($node)));
}
@ -748,7 +745,7 @@ class Crawler extends \SplObjectStorage
*/
public function form(array $values = null, $method = null)
{
if (!count($this)) {
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@ -830,127 +827,7 @@ class Crawler extends \SplObjectStorage
}
}
return sprintf('concat(%s)', implode(', ', $parts));
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function attach($object, $data = null)
{
$this->triggerDeprecation(__METHOD__);
parent::attach($object, $data);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function detach($object)
{
$this->triggerDeprecation(__METHOD__);
parent::detach($object);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function contains($object)
{
$this->triggerDeprecation(__METHOD__);
return parent::contains($object);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function addAll($storage)
{
$this->triggerDeprecation(__METHOD__);
parent::addAll($storage);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function removeAll($storage)
{
$this->triggerDeprecation(__METHOD__);
parent::removeAll($storage);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function removeAllExcept($storage)
{
$this->triggerDeprecation(__METHOD__);
parent::removeAllExcept($storage);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function getInfo()
{
$this->triggerDeprecation(__METHOD__);
return parent::getInfo();
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function setInfo($data)
{
$this->triggerDeprecation(__METHOD__);
parent::setInfo($data);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function offsetExists($object)
{
$this->triggerDeprecation(__METHOD__);
return parent::offsetExists($object);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function offsetSet($object, $data = null)
{
$this->triggerDeprecation(__METHOD__);
parent::offsetSet($object, $data);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function offsetUnset($object)
{
$this->triggerDeprecation(__METHOD__);
parent::offsetUnset($object);
}
/**
* @deprecated Using the SplObjectStorage API on the Crawler is deprecated as of 2.8 and will be removed in 3.0.
*/
public function offsetGet($object)
{
$this->triggerDeprecation(__METHOD__);
return parent::offsetGet($object);
return sprintf('concat(%s)', implode($parts, ', '));
}
/**
@ -960,7 +837,7 @@ class Crawler extends \SplObjectStorage
*
* @param string $xpath
*
* @return self
* @return Crawler
*/
private function filterRelativeXPath($xpath)
{
@ -968,7 +845,7 @@ class Crawler extends \SplObjectStorage
$crawler = $this->createSubCrawler(null);
foreach ($this as $node) {
foreach ($this->nodes as $node) {
$domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes);
$crawler->add($domxpath->query($xpath, $node));
}
@ -990,59 +867,29 @@ class Crawler extends \SplObjectStorage
{
$expressions = array();
$unionPattern = '/\|(?![^\[]*\])/';
// An expression which will never match to replace expressions which cannot match in the crawler
// We cannot simply drop
$nonMatchingExpression = 'a[name() = "b"]';
$xpathLen = strlen($xpath);
$openedBrackets = 0;
$startPosition = strspn($xpath, " \t\n\r\0\x0B");
// Split any unions into individual expressions.
foreach (preg_split($unionPattern, $xpath) as $expression) {
$expression = trim($expression);
$parenthesis = '';
for ($i = $startPosition; $i <= $xpathLen; ++$i) {
$i += strcspn($xpath, '"\'[]|', $i);
if ($i < $xpathLen) {
switch ($xpath[$i]) {
case '"':
case "'":
if (false === $i = strpos($xpath, $xpath[$i], $i + 1)) {
return $xpath; // The XPath expression is invalid
}
continue 2;
case '[':
++$openedBrackets;
continue 2;
case ']':
--$openedBrackets;
continue 2;
}
}
if ($openedBrackets) {
continue;
// If the union is inside some braces, we need to preserve the opening braces and apply
// the change only inside it.
if (preg_match('/^[\(\s*]+/', $expression, $matches)) {
$parenthesis = $matches[0];
$expression = substr($expression, strlen($parenthesis));
}
if ($startPosition < $xpathLen && '(' === $xpath[$startPosition]) {
// If the union is inside some braces, we need to preserve the opening braces and apply
// the change only inside it.
$j = 1 + strspn($xpath, "( \t\n\r\0\x0B", $startPosition + 1);
$parenthesis = substr($xpath, $startPosition, $j);
$startPosition += $j;
} else {
$parenthesis = '';
}
$expression = rtrim(substr($xpath, $startPosition, $i - $startPosition));
// BC for Symfony 2.4 and lower were elements were adding in a fake _root parent
if (0 === strpos($expression, '/_root/')) {
@trigger_error('XPath expressions referencing the fake root node are deprecated since version 2.8 and will be unsupported in 3.0. Please use "./" instead of "/_root/".', E_USER_DEPRECATED);
$expression = './'.substr($expression, 7);
} elseif (0 === strpos($expression, 'self::*/')) {
if (0 === strpos($expression, 'self::*/')) {
$expression = './'.substr($expression, 8);
}
// add prefix before absolute element selector
if ('' === $expression) {
if (empty($expression)) {
$expression = $nonMatchingExpression;
} elseif (0 === strpos($expression, '//')) {
$expression = 'descendant-or-self::'.substr($expression, 2);
@ -1052,15 +899,10 @@ class Crawler extends \SplObjectStorage
$expression = 'self::'.substr($expression, 2);
} elseif (0 === strpos($expression, 'child::')) {
$expression = 'self::'.substr($expression, 7);
} elseif ('/' === $expression[0] || 0 === strpos($expression, 'self::')) {
// the only direct child in Symfony 2.4 and lower is _root, which is already handled previously
// so let's drop the expression entirely
$expression = $nonMatchingExpression;
} elseif ('.' === $expression[0]) {
// '.' is the fake root element in Symfony 2.4 and lower, which is excluded from results
} elseif ('/' === $expression[0] || '.' === $expression[0] || 0 === strpos($expression, 'self::')) {
$expression = $nonMatchingExpression;
} elseif (0 === strpos($expression, 'descendant::')) {
$expression = 'descendant-or-self::'.substr($expression, 12);
$expression = 'descendant-or-self::'.substr($expression, strlen('descendant::'));
} elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) {
// the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes)
$expression = $nonMatchingExpression;
@ -1068,16 +910,9 @@ class Crawler extends \SplObjectStorage
$expression = 'self::'.$expression;
}
$expressions[] = $parenthesis.$expression;
if ($i === $xpathLen) {
return implode(' | ', $expressions);
}
$i += strspn($xpath, " \t\n\r\0\x0B", $i + 1);
$startPosition = $i + 1;
}
return $xpath; // The XPath expression is invalid
return implode(' | ', $expressions);
}
/**
@ -1087,13 +922,27 @@ class Crawler extends \SplObjectStorage
*/
public function getNode($position)
{
foreach ($this as $i => $node) {
if ($i == $position) {
return $node;
}
if (isset($this->nodes[$position])) {
return $this->nodes[$position];
}
}
/**
* @return int
*/
public function count()
{
return count($this->nodes);
}
/**
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->nodes);
}
/**
* @param \DOMElement $node
* @param string $siblingDir
@ -1187,23 +1036,4 @@ class Crawler extends \SplObjectStorage
return $crawler;
}
private function triggerDeprecation($methodName, $useTrace = false)
{
if ($useTrace || defined('HHVM_VERSION')) {
if (PHP_VERSION_ID >= 50400) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
} else {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
// The SplObjectStorage class performs calls to its own methods. These
// method calls must not lead to triggered deprecation notices.
if (isset($trace[2]['class']) && 'SplObjectStorage' === $trace[2]['class']) {
return;
}
}
@trigger_error('The '.$methodName.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
}

View file

@ -155,11 +155,11 @@ class ChoiceFormField extends FormField
/**
* Adds a choice to the current ones.
*
* This method should only be used internally.
*
* @param \DOMElement $node
*
* @throws \LogicException When choice provided is not multiple nor radio
*
* @internal
*/
public function addChoice(\DOMElement $node)
{

View file

@ -69,7 +69,7 @@ class Form extends Link implements \ArrayAccess
*
* @param array $values An array of field values
*
* @return $this
* @return Form
*/
public function setValues(array $values)
{
@ -279,7 +279,7 @@ class Form extends Link implements \ArrayAccess
/**
* Gets all fields.
*
* @return FormField[]
* @return FormField[] An array of fields
*/
public function all()
{

View file

@ -15,8 +15,6 @@ use Symfony\Component\DomCrawler\Field\FormField;
/**
* This is an internal class that must not be used directly.
*
* @internal
*/
class FormFieldRegistry
{
@ -151,7 +149,7 @@ class FormFieldRegistry
* @param string $base The fully qualified name of the base field
* @param array $values The values of the fields
*
* @return static
* @return FormFieldRegistry
*/
private static function create($base, array $values)
{

View file

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

View file

@ -16,11 +16,11 @@
}
],
"require": {
"php": ">=5.3.9",
"php": ">=5.5.9",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
"symfony/css-selector": "~2.8|~3.0.0"
"symfony/css-selector": "~2.8|~3.0"
},
"suggest": {
"symfony/css-selector": ""
@ -34,7 +34,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
"dev-master": "3.0-dev"
}
}
}

View file

@ -103,7 +103,7 @@ class EventDispatcher implements EventDispatcherInterface
*/
public function hasListeners($eventName = null)
{
return (bool) count($this->getListeners($eventName));
return (bool) $this->getListeners($eventName);
}
/**

View file

@ -11,11 +11,12 @@
namespace Symfony\Component\EventDispatcher\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase
abstract class AbstractEventDispatcherTest extends TestCase
{
/* Some pseudo events */
const preFoo = 'pre.foo';

View file

@ -11,6 +11,7 @@
namespace Symfony\Component\EventDispatcher\Tests\Debug;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@ -18,7 +19,7 @@ use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\Stopwatch\Stopwatch;
class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
class TraceableEventDispatcherTest extends TestCase
{
public function testAddRemoveListener()
{
@ -177,14 +178,20 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
{
$dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
$loop = 1;
$dispatchedEvents = 0;
$dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) {
++$loop;
if (2 == $loop) {
$dispatcher->dispatch('foo');
}
});
$dispatcher->addListener('foo', function () use (&$dispatchedEvents) {
++$dispatchedEvents;
});
$dispatcher->dispatch('foo');
$this->assertSame(2, $dispatchedEvents);
}
public function testDispatchReusedEventNested()

View file

@ -11,10 +11,11 @@
namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase
class RegisterListenersPassTest extends TestCase
{
/**
* Tests that event subscribers not implementing EventSubscriberInterface

View file

@ -11,13 +11,14 @@
namespace Symfony\Component\EventDispatcher\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
/**
* Test class for Event.
*/
class EventTest extends \PHPUnit_Framework_TestCase
class EventTest extends TestCase
{
/**
* @var \Symfony\Component\EventDispatcher\Event

View file

@ -11,12 +11,13 @@
namespace Symfony\Component\EventDispatcher\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Test class for Event.
*/
class GenericEventTest extends \PHPUnit_Framework_TestCase
class GenericEventTest extends TestCase
{
/**
* @var GenericEvent
@ -95,7 +96,7 @@ class GenericEventTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('Event', $this->event['name']);
// test getting invalid arg
$this->setExpectedException('InvalidArgumentException');
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
$this->assertFalse($this->event['nameNotExist']);
}

View file

@ -11,13 +11,14 @@
namespace Symfony\Component\EventDispatcher\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase
class ImmutableEventDispatcherTest extends TestCase
{
/**
* @var \PHPUnit_Framework_MockObject_MockObject

View file

@ -21,7 +21,7 @@
"require-dev": {
"symfony/dependency-injection": "~2.6|~3.0.0",
"symfony/expression-language": "~2.6|~3.0.0",
"symfony/config": "~2.0,>=2.0.5|~3.0.0",
"symfony/config": "^2.0.5|~3.0.0",
"symfony/stopwatch": "~2.3|~3.0.0",
"psr/log": "~1.0"
},

View file

@ -164,7 +164,7 @@ class BinaryFileResponse extends Response
if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) {
$encoding = mb_detect_encoding($filename, null, true);
for ($i = 0; $i < mb_strlen($filename, $encoding); ++$i) {
for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) {
$char = mb_substr($filename, $i, 1, $encoding);
if ('%' === $char || ord($char) < 32 || ord($char) > 126) {

View file

@ -206,6 +206,15 @@ class Request
protected static $requestFactory;
private $isForwardedValid = true;
private static $forwardedParams = array(
self::HEADER_CLIENT_IP => 'for',
self::HEADER_CLIENT_HOST => 'host',
self::HEADER_CLIENT_PROTO => 'proto',
self::HEADER_CLIENT_PORT => 'host',
);
/**
* Constructor.
*
@ -596,6 +605,7 @@ class Request
* * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getHost())
* * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getPort())
* * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure())
* * Request::HEADER_FORWARDED: defaults to Forwarded (see RFC 7239)
*
* Setting an empty value allows to disable the trusted header for the given key.
*
@ -805,41 +815,13 @@ class Request
*/
public function getClientIps()
{
$clientIps = array();
$ip = $this->server->get('REMOTE_ADDR');
if (!$this->isFromTrustedProxy()) {
return array($ip);
}
$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);
$forwardedClientIps = $matches[3];
$forwardedClientIps = $this->normalizeAndFilterClientIps($forwardedClientIps, $ip);
$clientIps = $forwardedClientIps;
}
if ($hasTrustedClientIpHeader) {
$xForwardedForClientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
$xForwardedForClientIps = $this->normalizeAndFilterClientIps($xForwardedForClientIps, $ip);
$clientIps = $xForwardedForClientIps;
}
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;
return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip);
}
/**
@ -965,31 +947,25 @@ class Request
*/
public function getPort()
{
if ($this->isFromTrustedProxy()) {
if (self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) {
return $port;
}
if (self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && 'https' === $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO], 'http')) {
return 443;
}
if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_PORT)) {
$host = $host[0];
} elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
$host = $host[0];
} elseif (!$host = $this->headers->get('HOST')) {
return $this->server->get('SERVER_PORT');
}
if ($host = $this->headers->get('HOST')) {
if ($host[0] === '[') {
$pos = strpos($host, ':', strrpos($host, ']'));
} else {
$pos = strrpos($host, ':');
}
if (false !== $pos) {
return (int) substr($host, $pos + 1);
}
return 'https' === $this->getScheme() ? 443 : 80;
if ($host[0] === '[') {
$pos = strpos($host, ':', strrpos($host, ']'));
} else {
$pos = strrpos($host, ':');
}
return $this->server->get('SERVER_PORT');
if (false !== $pos) {
return (int) substr($host, $pos + 1);
}
return 'https' === $this->getScheme() ? 443 : 80;
}
/**
@ -1189,8 +1165,8 @@ class Request
*/
public function isSecure()
{
if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) {
return in_array(strtolower(current(explode(',', $proto))), array('https', 'on', 'ssl', '1'));
if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) {
return in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true);
}
$https = $this->server->get('HTTPS');
@ -1215,10 +1191,8 @@ class Request
*/
public function getHost()
{
if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) {
$elements = explode(',', $host);
$host = $elements[count($elements) - 1];
if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
$host = $host[0];
} elseif (!$host = $this->headers->get('HOST')) {
if (!$host = $this->server->get('SERVER_NAME')) {
$host = $this->server->get('SERVER_ADDR', '');
@ -1387,10 +1361,10 @@ class Request
public function getRequestFormat($default = 'html')
{
if (null === $this->format) {
$this->format = $this->get('_format', $default);
$this->format = $this->get('_format');
}
return $this->format;
return null === $this->format ? $default : $this->format;
}
/**
@ -1779,6 +1753,9 @@ class Request
// Does the baseUrl have anything in common with the request_uri?
$requestUri = $this->getRequestUri();
if ($requestUri !== '' && $requestUri[0] !== '/') {
$requestUri = '/'.$requestUri;
}
if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
// full $baseUrl matches
@ -1851,9 +1828,12 @@ class Request
}
// Remove the query string from REQUEST_URI
if ($pos = strpos($requestUri, '?')) {
if (false !== $pos = strpos($requestUri, '?')) {
$requestUri = substr($requestUri, 0, $pos);
}
if ($requestUri !== '' && $requestUri[0] !== '/') {
$requestUri = '/'.$requestUri;
}
$pathInfo = substr($requestUri, strlen($baseUrl));
if (null !== $baseUrl && (false === $pathInfo || '' === $pathInfo)) {
@ -1947,8 +1927,48 @@ class Request
return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
}
private function getTrustedValues($type, $ip = null)
{
$clientValues = array();
$forwardedValues = array();
if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) {
foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) {
$clientValues[] = (self::HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '').trim($v);
}
}
if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
$forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
$forwardedValues = preg_match_all(sprintf('{(?:%s)=(?:"?\[?)([a-zA-Z0-9\.:_\-/]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array();
}
if (null !== $ip) {
$clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip);
$forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip);
}
if ($forwardedValues === $clientValues || !$clientValues) {
return $forwardedValues;
}
if (!$forwardedValues) {
return $clientValues;
}
if (!$this->isForwardedValid) {
return null !== $ip ? array('0.0.0.0', $ip) : array();
}
$this->isForwardedValid = false;
throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type]));
}
private function normalizeAndFilterClientIps(array $clientIps, $ip)
{
if (!$clientIps) {
return array();
}
$clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
$firstTrustedIp = null;

View file

@ -201,6 +201,11 @@ class Response
$this->setContent($content);
$this->setStatusCode($status);
$this->setProtocolVersion('1.0');
/* RFC2616 - 14.18 says all Responses need to have a Date */
if (!$this->headers->has('Date')) {
$this->setDate(new \DateTime(null, new \DateTimeZone('UTC')));
}
}
/**
@ -329,6 +334,7 @@ class Response
return $this;
}
/* RFC2616 - 14.18 says all Responses need to have a Date */
if (!$this->headers->has('Date')) {
$this->setDate(\DateTime::createFromFormat('U', time()));
}
@ -612,6 +618,11 @@ class Response
*/
public function getDate()
{
/*
RFC2616 - 14.18 says all Responses need to have a Date.
Make sure we provide one even if it the header
has been removed in the meantime.
*/
if (!$this->headers->has('Date')) {
$this->setDate(\DateTime::createFromFormat('U', time()));
}

View file

@ -58,9 +58,9 @@ class MockArraySessionStorage implements SessionStorageInterface
protected $metadataBag;
/**
* @var array
* @var array|SessionBagInterface[]
*/
protected $bags;
protected $bags = array();
/**
* Constructor.

View file

@ -25,6 +25,9 @@ use Symfony\Component\HttpFoundation\Response;
* Client simulates a browser and makes requests to a Kernel object.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @method Request|null getRequest() A Request instance
* @method Response|null getResponse() A Response instance
*/
class Client extends BaseClient
{
@ -47,26 +50,6 @@ class Client extends BaseClient
parent::__construct($server, $history, $cookieJar);
}
/**
* {@inheritdoc}
*
* @return Request|null A Request instance
*/
public function getRequest()
{
return parent::getRequest();
}
/**
* {@inheritdoc}
*
* @return Response|null A Response instance
*/
public function getResponse()
{
return parent::getResponse();
}
/**
* Makes a request.
*

View file

@ -298,7 +298,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
{
$html = '';
$dumper = new HtmlDumper(function ($line) use (&$html) {$html .= $line;}, $this->charset);
$dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset);
$dumper->setDumpHeader('');
$dumper->setDumpBoundaries('', '');

View file

@ -33,7 +33,7 @@ class ValueExporter
if (is_object($value)) {
if ($value instanceof \DateTime || $value instanceof \DateTimeInterface) {
return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ISO8601));
return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ATOM));
}
return sprintf('Object(%s)', get_class($value));

View file

@ -52,6 +52,6 @@ class AddRequestFormatsListener implements EventSubscriberInterface
*/
public static function getSubscribedEvents()
{
return array(KernelEvents::REQUEST => 'onKernelRequest');
return array(KernelEvents::REQUEST => array('onKernelRequest', 1));
}
}

View file

@ -11,6 +11,7 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

View file

@ -12,6 +12,7 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

View file

@ -314,6 +314,26 @@ class Store implements StoreInterface
return unserialize($entries);
}
/**
* Purges data for the given URL.
*
* This method purges both the HTTP and the HTTPS version of the cache entry.
*
* @param string $url A URL
*
* @return bool true if the URL exists with either HTTP or HTTPS scheme and has been purged, false otherwise
*/
public function purge($url)
{
$http = preg_replace('#^https:#', 'http:', $url);
$https = preg_replace('#^http:#', 'https:', $url);
$purgedHttp = $this->doPurge($http);
$purgedHttps = $this->doPurge($https);
return $purgedHttp || $purgedHttps;
}
/**
* Purges data for the given URL.
*
@ -321,10 +341,9 @@ class Store implements StoreInterface
*
* @return bool true if the URL exists and has been purged, false otherwise
*/
public function purge($url)
private function doPurge($url)
{
$key = $this->getCacheKey(Request::create($url));
if (isset($this->locks[$key])) {
flock($this->locks[$key], LOCK_UN);
fclose($this->locks[$key]);

View file

@ -59,11 +59,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $startTime;
protected $loadClassCache;
const VERSION = '2.8.16';
const VERSION_ID = 20816;
const VERSION = '2.8.19';
const VERSION_ID = 20819;
const MAJOR_VERSION = 2;
const MINOR_VERSION = 8;
const RELEASE_VERSION = 16;
const RELEASE_VERSION = 19;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '11/2018';
@ -303,6 +303,9 @@ abstract class Kernel implements KernelInterface, TerminableInterface
{
if (null === $this->name) {
$this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir));
if (ctype_digit($this->name[0])) {
$this->name = '_'.$this->name;
}
}
return $this->name;

View file

@ -46,7 +46,7 @@ class MemcacheProfilerStorage extends BaseMemcacheProfilerStorage
$port = $matches[3];
$memcache = new \Memcache();
$memcache->addserver($host, $port);
$memcache->addServer($host, $port);
$this->memcache = $memcache;
}

View file

@ -17,9 +17,9 @@
],
"require": {
"php": ">=5.3.9",
"symfony/event-dispatcher": "~2.6,>=2.6.7|~3.0.0",
"symfony/event-dispatcher": "^2.6.7|~3.0.0",
"symfony/http-foundation": "~2.7.20|~2.8.13|~3.1.6",
"symfony/debug": "~2.6,>=2.6.2",
"symfony/debug": "^2.6.2",
"psr/log": "~1.0"
},
"require-dev": {
@ -27,16 +27,16 @@
"symfony/class-loader": "~2.1|~3.0.0",
"symfony/config": "~2.8",
"symfony/console": "~2.3|~3.0.0",
"symfony/css-selector": "~2.0,>=2.0.5|~3.0.0",
"symfony/css-selector": "^2.0.5|~3.0.0",
"symfony/dependency-injection": "~2.8|~3.0.0",
"symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0",
"symfony/dom-crawler": "^2.0.5|~3.0.0",
"symfony/expression-language": "~2.4|~3.0.0",
"symfony/finder": "~2.0,>=2.0.5|~3.0.0",
"symfony/process": "~2.0,>=2.0.5|~3.0.0",
"symfony/finder": "^2.0.5|~3.0.0",
"symfony/process": "^2.0.5|~3.0.0",
"symfony/routing": "~2.8|~3.0.0",
"symfony/stopwatch": "~2.3|~3.0.0",
"symfony/templating": "~2.2|~3.0.0",
"symfony/translation": "~2.0,>=2.0.5|~3.0.0",
"symfony/translation": "^2.0.5|~3.0.0",
"symfony/var-dumper": "~2.6|~3.0.0"
},
"conflict": {

View file

@ -75,7 +75,7 @@ class ExecutableFinder
$suffixes = array('');
if ('\\' === DIRECTORY_SEPARATOR) {
$pathExt = getenv('PATHEXT');
$suffixes = $pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes;
$suffixes = array_merge($suffixes, $pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes);
}
foreach ($suffixes as $suffix) {
foreach ($dirs as $dir) {

View file

@ -162,7 +162,6 @@ class Process
$this->setTimeout($timeout);
$this->useFileHandles = '\\' === DIRECTORY_SEPARATOR;
$this->pty = false;
$this->enhanceWindowsCompatibility = true;
$this->enhanceSigchildCompatibility = '\\' !== DIRECTORY_SEPARATOR && $this->isSigchildEnabled();
$this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options);
}

View file

@ -71,7 +71,7 @@ class ProcessUtils
return $escapedArgument;
}
return escapeshellarg($argument);
return "'".str_replace("'", "'\\''", $argument)."'";
}
/**

View file

@ -2,6 +2,10 @@ language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache/files
matrix:
include:
- php: 5.3
@ -12,10 +16,9 @@ matrix:
env: deps=low
- php: 5.6
env: deps=high
- php: nightly
- php: 7.0
- php: hhvm
allow_failures:
- php: nightly
- php: hhvm
fast_finish: true
@ -25,15 +28,15 @@ env:
- SYMFONY_DEPRECATIONS_HELPER=weak
before_install:
- composer self-update
- if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini; fi;
- composer self-update
- if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
- if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then php -i; fi;
# Set the COMPOSER_ROOT_VERSION to the right version according to the branch being built
- if [ "$TRAVIS_BRANCH" = "master" ]; then export COMPOSER_ROOT_VERSION=dev-master; else export COMPOSER_ROOT_VERSION="$TRAVIS_BRANCH".x-dev; fi;
install:
- if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]] && [[ "$TRAVIS_PHP_VERSION" != "5.4" ]]; then composer require --no-update zendframework/zend-diactoros; fi;
- if [ "$TRAVIS_PHP_VERSION" != "5.3" ]; then composer require --no-update zendframework/zend-diactoros; fi;
- if [ "$deps" = "no" ]; then export SYMFONY_DEPRECATIONS_HELPER=strict; fi;
- if [ "$deps" = "no" ]; then composer --prefer-source install; fi;
- if [ "$deps" = "high" ]; then composer --prefer-source update; fi;

View file

@ -0,0 +1,3 @@
* 1.0.0 (2016-09-14)
* Initial release

View file

@ -1,7 +0,0 @@
CHANGELOG
=========
2.8.0
-----
* added the component

View file

@ -45,11 +45,11 @@ class DiactorosFactory implements HttpMessageFactoryInterface
$server = DiactorosRequestFactory::normalizeServer($symfonyRequest->server->all());
$headers = $symfonyRequest->headers->all();
try {
$body = new DiactorosStream($symfonyRequest->getContent(true));
} catch (\LogicException $e) {
if (PHP_VERSION_ID < 50600) {
$body = new DiactorosStream('php://temp', 'wb+');
$body->write($symfonyRequest->getContent());
} else {
$body = new DiactorosStream($symfonyRequest->getContent(true));
}
$request = new ServerRequest(
@ -86,6 +86,10 @@ class DiactorosFactory implements HttpMessageFactoryInterface
$files = array();
foreach ($uploadedFiles as $key => $value) {
if (null === $value) {
$files[$key] = new DiactorosUploadedFile(null, 0, UPLOAD_ERR_NO_FILE, null, null);
continue;
}
if ($value instanceof UploadedFile) {
$files[$key] = $this->createUploadedFile($value);
} else {
@ -107,7 +111,7 @@ class DiactorosFactory implements HttpMessageFactoryInterface
{
return new DiactorosUploadedFile(
$symfonyUploadedFile->getRealPath(),
$symfonyUploadedFile->getSize(),
$symfonyUploadedFile->getClientSize(),
$symfonyUploadedFile->getError(),
$symfonyUploadedFile->getClientOriginalName(),
$symfonyUploadedFile->getClientMimeType()

View file

@ -14,6 +14,7 @@ namespace Symfony\Bridge\PsrHttpMessage\Factory;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UploadedFileInterface;
use Psr\Http\Message\UriInterface;
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\File\UploadedFile;
@ -32,6 +33,20 @@ class HttpFoundationFactory implements HttpFoundationFactoryInterface
*/
public function createRequest(ServerRequestInterface $psrRequest)
{
$server = array();
$uri = $psrRequest->getUri();
if ($uri instanceof UriInterface) {
$server['SERVER_NAME'] = $uri->getHost();
$server['SERVER_PORT'] = $uri->getPort();
$server['REQUEST_URI'] = $uri->getPath();
$server['QUERY_STRING'] = $uri->getQuery();
}
$server['REQUEST_METHOD'] = $psrRequest->getMethod();
$server = array_replace($server, $psrRequest->getServerParams());
$parsedBody = $psrRequest->getParsedBody();
$parsedBody = is_array($parsedBody) ? $parsedBody : array();
@ -41,7 +56,7 @@ class HttpFoundationFactory implements HttpFoundationFactoryInterface
$psrRequest->getAttributes(),
$psrRequest->getCookieParams(),
$this->getFiles($psrRequest->getUploadedFiles()),
$psrRequest->getServerParams(),
$server,
$psrRequest->getBody()->__toString()
);
$request->headers->replace($psrRequest->getHeaders());
@ -80,10 +95,14 @@ class HttpFoundationFactory implements HttpFoundationFactoryInterface
*/
private function createUploadedFile(UploadedFileInterface $psrUploadedFile)
{
$temporaryPath = $this->getTemporaryPath();
$psrUploadedFile->moveTo($temporaryPath);
$temporaryPath = '';
$clientFileName = '';
if (UPLOAD_ERR_NO_FILE !== $psrUploadedFile->getError()) {
$temporaryPath = $this->getTemporaryPath();
$psrUploadedFile->moveTo($temporaryPath);
$clientFileName = $psrUploadedFile->getClientFilename();
$clientFileName = $psrUploadedFile->getClientFilename();
}
return new UploadedFile(
$temporaryPath,

View file

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

View file

@ -24,10 +24,16 @@
"symfony/phpunit-bridge": "~2.7|~3.0"
},
"suggest": {
"psr/http-message-implementation": "To use the HttpFoundation factory",
"zendframework/zend-diactoros": "To use the Zend Diactoros factory"
},
"autoload": {
"psr-4": { "Symfony\\Bridge\\PsrHttpMessage\\": "" }
},
"minimum-stability": "dev"
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
}
}

View file

@ -139,7 +139,7 @@ abstract class AnnotationClassLoader implements LoaderInterface
$defaults = array_replace($globals['defaults'], $annot->getDefaults());
foreach ($method->getParameters() as $param) {
if (!isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) {
if (false !== strpos($globals['path'].$annot->getPath(), sprintf('{%s}', $param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) {
$defaults[$param->getName()] = $param->getDefaultValue();
}
}

View file

@ -38,7 +38,15 @@ class AnnotationDirectoryLoader extends AnnotationFileLoader
$collection = new RouteCollection();
$collection->addResource(new DirectoryResource($dir, '/\.php$/'));
$files = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::LEAVES_ONLY));
$files = iterator_to_array(new \RecursiveIteratorIterator(
new RecursiveCallbackFilterIterator(
new \RecursiveDirectoryIterator($dir),
function (\SplFileInfo $current) {
return '.' !== substr($current->getBasename(), 0, 1);
}
),
\RecursiveIteratorIterator::LEAVES_ONLY
));
usort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
return (string) $a > (string) $b ? 1 : -1;
});
@ -79,3 +87,34 @@ class AnnotationDirectoryLoader extends AnnotationFileLoader
return is_dir($path) && (!$type || 'annotation' === $type);
}
}
/**
* @internal To be removed as RecursiveCallbackFilterIterator is available since PHP 5.4
*/
class RecursiveCallbackFilterIterator extends \FilterIterator implements \RecursiveIterator
{
private $iterator;
private $callback;
public function __construct(\RecursiveIterator $iterator, $callback)
{
$this->iterator = $iterator;
$this->callback = $callback;
parent::__construct($iterator);
}
public function accept()
{
return call_user_func($this->callback, $this->current(), $this->key(), $this->iterator);
}
public function hasChildren()
{
return $this->iterator->hasChildren();
}
public function getChildren()
{
return new static($this->iterator->getChildren(), $this->callback);
}
}

View file

@ -21,7 +21,7 @@
"require-dev": {
"symfony/config": "~2.7|~3.0.0",
"symfony/http-foundation": "~2.3|~3.0.0",
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0",
"symfony/yaml": "^2.0.5|~3.0.0",
"symfony/expression-language": "~2.4|~3.0.0",
"doctrine/annotations": "~1.0",
"doctrine/common": "~2.2",

View file

@ -92,14 +92,16 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
throw new UnexpectedValueException($error->message);
}
$rootNode = null;
foreach ($dom->childNodes as $child) {
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
throw new UnexpectedValueException('Document types are not allowed.');
}
if (!$rootNode && $child->nodeType !== XML_PI_NODE) {
$rootNode = $child;
}
}
$rootNode = $dom->firstChild;
// todo: throw an exception if the root node name is not correctly configured (bc)
if ($rootNode->hasChildNodes()) {
@ -329,6 +331,10 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
$value = array();
foreach ($node->childNodes as $subnode) {
if ($subnode->nodeType === XML_PI_NODE) {
continue;
}
$val = $this->parseXml($subnode);
if ('item' === $subnode->nodeName && isset($val['@key'])) {
@ -369,7 +375,10 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
if (is_array($data) || ($data instanceof \Traversable && !$this->serializer->supportsNormalization($data, $this->format))) {
foreach ($data as $key => $data) {
//Ah this is the magic @ attribute types.
if (0 === strpos($key, '@') && is_scalar($data) && $this->isElementNameValid($attributeName = substr($key, 1))) {
if (0 === strpos($key, '@') && $this->isElementNameValid($attributeName = substr($key, 1))) {
if (!is_scalar($data)) {
$data = $this->serializer->normalize($data, $this->format, $this->context);
}
$parentNode->setAttribute($attributeName, $data);
} elseif ($key === '#') {
$append = $this->selectNodeType($parentNode, $data);
@ -474,7 +483,7 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
} elseif ($val instanceof \Traversable) {
$this->buildXml($node, $val);
} elseif (is_object($val)) {
return $this->buildXml($node, $this->serializer->normalize($val, $this->format, $this->context));
return $this->selectNodeType($node, $this->serializer->normalize($val, $this->format, $this->context));
} elseif (is_numeric($val)) {
return $this->appendText($node, (string) $val);
} elseif (is_string($val) && $this->needsCdataWrapping($val)) {

View file

@ -60,39 +60,39 @@ class YamlFileLoader extends FileLoader
$this->classes = $classes;
}
if (isset($this->classes[$classMetadata->getName()])) {
$yaml = $this->classes[$classMetadata->getName()];
if (!isset($this->classes[$classMetadata->getName()])) {
return false;
}
if (isset($yaml['attributes']) && is_array($yaml['attributes'])) {
$attributesMetadata = $classMetadata->getAttributesMetadata();
$yaml = $this->classes[$classMetadata->getName()];
foreach ($yaml['attributes'] as $attribute => $data) {
if (isset($attributesMetadata[$attribute])) {
$attributeMetadata = $attributesMetadata[$attribute];
} else {
$attributeMetadata = new AttributeMetadata($attribute);
$classMetadata->addAttributeMetadata($attributeMetadata);
if (isset($yaml['attributes']) && is_array($yaml['attributes'])) {
$attributesMetadata = $classMetadata->getAttributesMetadata();
foreach ($yaml['attributes'] as $attribute => $data) {
if (isset($attributesMetadata[$attribute])) {
$attributeMetadata = $attributesMetadata[$attribute];
} else {
$attributeMetadata = new AttributeMetadata($attribute);
$classMetadata->addAttributeMetadata($attributeMetadata);
}
if (isset($data['groups'])) {
if (!is_array($data['groups'])) {
throw new MappingException(sprintf('The "groups" key must be an array of strings in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()));
}
if (isset($data['groups'])) {
if (!is_array($data['groups'])) {
throw new MappingException('The "groups" key must be an array of strings in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName());
foreach ($data['groups'] as $group) {
if (!is_string($group)) {
throw new MappingException(sprintf('Group names must be strings in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()));
}
foreach ($data['groups'] as $group) {
if (!is_string($group)) {
throw new MappingException('Group names must be strings in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName());
}
$attributeMetadata->addGroup($group);
}
$attributeMetadata->addGroup($group);
}
}
}
return true;
}
return false;
return true;
}
}

View file

@ -44,14 +44,15 @@ class CamelCaseToSnakeCaseNameConverter implements NameConverterInterface
public function normalize($propertyName)
{
if (null === $this->attributes || in_array($propertyName, $this->attributes)) {
$lcPropertyName = lcfirst($propertyName);
$snakeCasedName = '';
$len = strlen($propertyName);
$len = strlen($lcPropertyName);
for ($i = 0; $i < $len; ++$i) {
if (ctype_upper($propertyName[$i])) {
$snakeCasedName .= '_'.strtolower($propertyName[$i]);
if (ctype_upper($lcPropertyName[$i])) {
$snakeCasedName .= '_'.strtolower($lcPropertyName[$i]);
} else {
$snakeCasedName .= strtolower($propertyName[$i]);
$snakeCasedName .= strtolower($lcPropertyName[$i]);
}
}
@ -75,7 +76,7 @@ class CamelCaseToSnakeCaseNameConverter implements NameConverterInterface
}
if (null === $this->attributes || in_array($camelCasedName, $this->attributes)) {
return $this->lowerCamelCase ? lcfirst($camelCasedName) : $camelCasedName;
return $camelCasedName;
}
return $propertyName;

View file

@ -20,7 +20,7 @@
"symfony/polyfill-php55": "~1.0"
},
"require-dev": {
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0",
"symfony/yaml": "^2.0.5|~3.0.0",
"symfony/config": "~2.2|~3.0.0",
"symfony/property-access": "~2.3|~3.0.0",
"doctrine/annotations": "~1.0",

View file

@ -83,7 +83,6 @@ abstract class AbstractOperation implements OperationInterface
$this->source = $source;
$this->target = $target;
$this->result = new MessageCatalogue($source->getLocale());
$this->domains = null;
$this->messages = array();
}

View file

@ -137,7 +137,7 @@ class MoFileLoader extends FileLoader
}
/**
* Reads an unsigned long from stream respecting endianess.
* Reads an unsigned long from stream respecting endianness.
*
* @param resource $stream
* @param bool $isBigEndian

View file

@ -62,7 +62,7 @@ class ArrayConverter
* $tree['foo'] was string before we found array {bar: test2}.
* Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2';
*/
$elem = &$elem[ implode('.', array_slice($parts, $i)) ];
$elem = &$elem[implode('.', array_slice($parts, $i))];
break;
}
$parentOfElem = &$elem;

View file

@ -21,7 +21,7 @@
},
"require-dev": {
"symfony/config": "~2.8",
"symfony/intl": "~2.4|~3.0.0",
"symfony/intl": "~2.7.25|^2.8.18|~3.2.5",
"symfony/yaml": "~2.2|~3.0.0",
"psr/log": "~1.0"
},

View file

@ -17,7 +17,7 @@ use Symfony\Component\Validator\ConstraintValidator;
/**
* @author Michael Hirschler <michael.vhirsch@gmail.com>
*
* @link https://en.wikipedia.org/wiki/ISO_9362#Structure
* @see https://en.wikipedia.org/wiki/ISO_9362#Structure
*/
class BicValidator extends ConstraintValidator
{

View file

@ -67,6 +67,10 @@ abstract class Composite extends Constraint
foreach ($nestedConstraints as $constraint) {
if (!$constraint instanceof Constraint) {
if (is_object($constraint)) {
$constraint = get_class($constraint);
}
throw new ConstraintDefinitionException(sprintf('The value %s is not an instance of Constraint in constraint %s', $constraint, get_class($this)));
}

View file

@ -34,7 +34,9 @@ class UrlValidator extends ConstraintValidator
\] # an IPv6 address
)
(:[0-9]+)? # a port (optional)
(/?|/\S+|\?\S*|\#\S*) # a /, nothing, a / with something, a query or a fragment
(?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path
(?:\? (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a query (optional)
(?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional)
$~ixu';
/**

View file

@ -319,6 +319,30 @@ class ClassMetadata extends ElementMetadata implements ClassMetadataInterface
return $this;
}
/**
* Adds a constraint to the getter of the given property.
*
* @param string $property The name of the property
* @param string $method The name of the getter method
* @param Constraint $constraint The constraint
*
* @return $this
*/
public function addGetterMethodConstraint($property, $method, Constraint $constraint)
{
if (!isset($this->getters[$property])) {
$this->getters[$property] = new GetterMetadata($this->getClassName(), $property, $method);
$this->addPropertyMetadata($this->getters[$property]);
}
$constraint->addImplicitGroupName($this->getDefaultGroup());
$this->getters[$property]->addConstraint($constraint);
return $this;
}
/**
* @param string $property
* @param Constraint[] $constraints
@ -334,6 +358,22 @@ class ClassMetadata extends ElementMetadata implements ClassMetadataInterface
return $this;
}
/**
* @param string $property
* @param string $method
* @param Constraint[] $constraints
*
* @return $this
*/
public function addGetterMethodConstraints($property, $method, array $constraints)
{
foreach ($constraints as $constraint) {
$this->addGetterMethodConstraint($property, $method, $constraint);
}
return $this;
}
/**
* Merges the constraints of the given metadata into this object.
*
@ -346,10 +386,6 @@ class ClassMetadata extends ElementMetadata implements ClassMetadataInterface
}
foreach ($source->getConstrainedProperties() as $property) {
if ($this->hasPropertyMetadata($property)) {
continue;
}
foreach ($source->getPropertyMetadata($property) as $member) {
$member = clone $member;

View file

@ -35,25 +35,30 @@ class GetterMetadata extends MemberMetadata
/**
* Constructor.
*
* @param string $class The class the getter is defined on
* @param string $property The property which the getter returns
* @param string $class The class the getter is defined on
* @param string $property The property which the getter returns
* @param string|null $method The method that is called to retrieve the value being validated (null for auto-detection)
*
* @throws ValidatorException
*/
public function __construct($class, $property)
public function __construct($class, $property, $method = null)
{
$getMethod = 'get'.ucfirst($property);
$isMethod = 'is'.ucfirst($property);
$hasMethod = 'has'.ucfirst($property);
if (null === $method) {
$getMethod = 'get'.ucfirst($property);
$isMethod = 'is'.ucfirst($property);
$hasMethod = 'has'.ucfirst($property);
if (method_exists($class, $getMethod)) {
$method = $getMethod;
} elseif (method_exists($class, $isMethod)) {
$method = $isMethod;
} elseif (method_exists($class, $hasMethod)) {
$method = $hasMethod;
} else {
throw new ValidatorException(sprintf('Neither of these methods exist in class %s: %s, %s, %s', $class, $getMethod, $isMethod, $hasMethod));
if (method_exists($class, $getMethod)) {
$method = $getMethod;
} elseif (method_exists($class, $isMethod)) {
$method = $isMethod;
} elseif (method_exists($class, $hasMethod)) {
$method = $hasMethod;
} else {
throw new ValidatorException(sprintf('Neither of these methods exist in class %s: %s, %s, %s', $class, $getMethod, $isMethod, $hasMethod));
}
} elseif (!method_exists($class, $method)) {
throw new ValidatorException(sprintf('The %s() method does not exist in class %s.', $method, $class));
}
parent::__construct($class, $method, $property);

View file

@ -79,7 +79,7 @@ class AnnotationLoader implements LoaderInterface
$metadata->addConstraint($constraint);
} elseif ($constraint instanceof Constraint) {
if (preg_match('/^(get|is|has)(.+)$/i', $method->name, $matches)) {
$metadata->addGetterConstraint(lcfirst($matches[2]), $constraint);
$metadata->addGetterMethodConstraint(lcfirst($matches[2]), $matches[0], $constraint);
} else {
throw new MappingException(sprintf('The constraint on "%s::%s" cannot be added. Constraints can only be added on methods beginning with "get", "is" or "has".', $className, $method->name));
}

View file

@ -22,14 +22,14 @@
},
"require-dev": {
"symfony/http-foundation": "~2.3|~3.0.0",
"symfony/intl": "~2.7.4|~2.8|~3.0.0",
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0",
"symfony/intl": "~2.7.25|^2.8.18|~3.2.5",
"symfony/yaml": "^2.0.5|~3.0.0",
"symfony/config": "~2.2|~3.0.0",
"symfony/property-access": "~2.3|~3.0.0",
"symfony/expression-language": "~2.4|~3.0.0",
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.0",
"egulias/email-validator": "~1.2,>=1.2.1"
"egulias/email-validator": "^1.2.1"
},
"suggest": {
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",

View file

@ -21,7 +21,7 @@ use Symfony\Component\Yaml\Exception\DumpException;
*/
class Inline
{
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
private static $exceptionOnInvalidType = false;
private static $objectSupport = false;
@ -149,8 +149,8 @@ class Inline
case Escaper::requiresDoubleQuoting($value):
return Escaper::escapeWithDoubleQuotes($value);
case Escaper::requiresSingleQuoting($value):
case preg_match(self::getHexRegex(), $value):
case preg_match(self::getTimestampRegex(), $value):
case Parser::preg_match(self::getHexRegex(), $value):
case Parser::preg_match(self::getTimestampRegex(), $value):
return Escaper::escapeWithSingleQuotes($value);
default:
return $value;
@ -244,10 +244,10 @@ class Inline
$i += strlen($output);
// remove comments
if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
$output = substr($output, 0, $match[0][1]);
}
} elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
} elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
$output = $match[1];
$i += strlen($output);
} else {
@ -282,7 +282,7 @@ class Inline
*/
private static function parseQuotedScalar($scalar, &$i)
{
if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
}
@ -530,16 +530,16 @@ class Inline
return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
case is_numeric($scalar):
case preg_match(self::getHexRegex(), $scalar):
case Parser::preg_match(self::getHexRegex(), $scalar):
return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
case '.inf' === $scalarLower:
case '.nan' === $scalarLower:
return -log(0);
case '-.inf' === $scalarLower:
return log(0);
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
case Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
return (float) str_replace(',', '', $scalar);
case preg_match(self::getTimestampRegex(), $scalar):
case Parser::preg_match(self::getTimestampRegex(), $scalar):
$timeZone = date_default_timezone_get();
date_default_timezone_set('UTC');
$time = strtotime($scalar);

View file

@ -61,7 +61,7 @@ class Parser
*/
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false)
{
if (!preg_match('//u', $value)) {
if (false === preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.');
}
$this->currentLineNb = -1;
@ -92,13 +92,13 @@ class Parser
}
$isRef = $mergeNode = false;
if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) {
if ($context && 'mapping' == $context) {
throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
$context = 'sequence';
if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}
@ -108,7 +108,7 @@ class Parser
$data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap);
} else {
if (isset($values['leadspaces'])
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
&& self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($values['value']), $matches)
) {
// this is a compact notation element, add to next block and parse
$block = $values['value'];
@ -124,7 +124,10 @@ class Parser
if ($isRef) {
$this->refs[$isRef] = end($data);
}
} elseif (preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) {
} elseif (
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
&& (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))
) {
if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine);
}
@ -161,11 +164,7 @@ class Parser
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
foreach ($refValue as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
$data += $refValue; // array union
} else {
if (isset($values['value']) && $values['value'] !== '') {
$value = $values['value'];
@ -187,23 +186,15 @@ class Parser
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
}
foreach ($parsedItem as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
$data += $parsedItem; // array union
}
} else {
// If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the
// current mapping, unless the key already exists in it.
foreach ($parsed as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
$data += $parsed; // array union
}
}
} elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
} elseif (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}
@ -262,27 +253,7 @@ class Parser
return $value;
}
switch (preg_last_error()) {
case PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
break;
default:
$error = 'Unable to parse.';
}
throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}
@ -527,7 +498,7 @@ class Parser
return $this->refs[$value];
}
if (preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
if (self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
$modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
return $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers));
@ -582,7 +553,7 @@ class Parser
// determine indentation if not specified
if (0 === $indentation) {
if (preg_match('/^ +/', $this->currentLine, $matches)) {
if (self::preg_match('/^ +/', $this->currentLine, $matches)) {
$indentation = strlen($matches[0]);
}
}
@ -593,7 +564,7 @@ class Parser
while (
$notEOF && (
$isCurrentLineBlank ||
preg_match($pattern, $this->currentLine, $matches)
self::preg_match($pattern, $this->currentLine, $matches)
)
) {
if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) {
@ -626,7 +597,7 @@ class Parser
$previousLineIndented = false;
$previousLineBlank = false;
for ($i = 0; $i < count($blockLines); ++$i) {
for ($i = 0, $blockLinesCount = count($blockLines); $i < $blockLinesCount; ++$i) {
if ('' === $blockLines[$i]) {
$text .= "\n";
$previousLineIndented = false;
@ -681,10 +652,7 @@ class Parser
return false;
}
$ret = false;
if ($this->getCurrentLineIndentation() > $currentIndentation) {
$ret = true;
}
$ret = $this->getCurrentLineIndentation() > $currentIndentation;
$this->moveToPreviousLine();
@ -785,14 +753,7 @@ class Parser
return false;
}
$ret = false;
if (
$this->getCurrentLineIndentation() == $currentIndentation
&&
$this->isStringUnIndentedCollectionItem()
) {
$ret = true;
}
$ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();
$this->moveToPreviousLine();
@ -816,6 +777,48 @@ class Parser
*/
private function isBlockScalarHeader()
{
return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
}
/**
* A local wrapper for `preg_match` which will throw a ParseException if there
* is an internal error in the PCRE engine.
*
* This avoids us needing to check for "false" every time PCRE is used
* in the YAML engine
*
* @throws ParseException on a PCRE internal error
*
* @see preg_last_error()
*
* @internal
*/
public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0)
{
if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) {
switch (preg_last_error()) {
case PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
break;
default:
$error = 'Error.';
}
throw new ParseException($error);
}
return $ret;
}
}