From 834f55026345ffc220995091b2095c6651ec38dd Mon Sep 17 00:00:00 2001 From: Mathieu Lemoine Date: Fri, 13 May 2016 15:34:52 -0400 Subject: [PATCH 01/13] [DX][DI] Make Autowiring exceptions more future friendly --- .../Compiler/AutowirePass.php | 15 +++++--- .../Tests/Compiler/AutowirePassTest.php | 37 ++++++++++--------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index e4457d4608..a7812fe03d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -225,13 +225,16 @@ class AutowirePass implements CompilerPassInterface private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) { if (isset($this->notGuessableTypes[$typeHint->name])) { - throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Several services implementing this type have been declared: "%s".', $typeHint->name, $id, implode('", "', $this->types[$typeHint->name]))); + $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; + $matchingServices = implode(', ', $this->types[$typeHint->name]); + + throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices)); + } - $noAvailableDefinitionMessage = sprintf('Unable to autowire argument of type "%s" for the service "%s". This type cannot be instantiated automatically and no service implementing this type is declared.', $typeHint->name, $id); - if (!$typeHint->isInstantiable()) { - throw new RuntimeException($noAvailableDefinitionMessage); + $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; + 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); @@ -244,7 +247,9 @@ class AutowirePass implements CompilerPassInterface try { $this->completeDefinition($argumentId, $argumentDefinition); } catch (RuntimeException $e) { - throw new RuntimeException($noAvailableDefinitionMessage, 0, $e); + $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; + $message = 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); + throw new RuntimeException($message, 0, $e); } return new Reference($argumentId); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 6098d9a913..b5badc31fd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -103,7 +103,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Several services implementing this type have been declared: "c1", "c2". + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Multiple services exist for this interface (c1, c2, c3). */ public function testTypeCollision() { @@ -111,6 +111,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase $container->register('c1', __NAMESPACE__.'\CollisionA'); $container->register('c2', __NAMESPACE__.'\CollisionB'); + $container->register('c3', __NAMESPACE__.'\CollisionB'); $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); $aDefinition->setAutowired(true); @@ -120,7 +121,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". Several services implementing this type have been declared: "a1", "a2". + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". Multiple services exist for this class (a1, a2). */ public function testTypeNotGuessable() { @@ -137,7 +138,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". Several services implementing this type have been declared: "a1", "a2". + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". Multiple services exist for this class (a1, a2). */ public function testTypeNotGuessableWithSubclass() { @@ -152,6 +153,21 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase $pass->process($container); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". No services were found matching this interface and it cannot be auto-registered. + */ + public function testTypeNotGuessableNoServicesFound() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + public function testTypeNotGuessableWithTypeSet() { $container = new ContainerBuilder(); @@ -207,21 +223,6 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass()); } - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". This type cannot be instantiated automatically and no service implementing this type is declared. - */ - public function testCreateNonInstanciable() - { - $container = new ContainerBuilder(); - - $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - public function testResolveParameter() { $container = new ContainerBuilder(); From f8dd87d7cc2e9c2551322662fb3d9bed7f332d3c Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Sat, 2 Apr 2016 12:30:41 +0200 Subject: [PATCH 02/13] [WebProfilerBundle] Fix CORS ajax security issues --- .../Resources/views/Profiler/base_js.html.twig | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig index 8c10c95655..d9f92ca26a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -80,6 +80,20 @@ requestStack = [], + extractHeaders = function(xhr, stackElement) { + // Here we avoid to call xhr.getResponseHeader in order to + // prevent polluting the console with CORS security errors + var allHeaders = xhr.getAllResponseHeaders(); + var ret; + + if (ret = allHeaders.match(/^x-debug-token:\s+(.*)$/im)) { + stackElement.profile = ret[1]; + } + if (ret = allHeaders.match(/^x-debug-token-link:\s+(.*)$/im)) { + stackElement.profilerUrl = ret[1]; + } + }, + renderAjaxRequests = function() { var requestCounter = document.querySelectorAll('.sf-toolbar-ajax-requests'); if (!requestCounter.length) { @@ -239,8 +253,8 @@ stackElement.duration = new Date() - stackElement.start; stackElement.loading = false; stackElement.error = self.status < 200 || self.status >= 400; - stackElement.profile = self.getResponseHeader("X-Debug-Token"); - stackElement.profilerUrl = self.getResponseHeader("X-Debug-Token-Link"); + + extractHeaders(self, stackElement); Sfjs.renderAjaxRequests(); } From ac7f74eccac01ce7b279c41b72f1422e575e67f6 Mon Sep 17 00:00:00 2001 From: Jeremy Benoist Date: Tue, 17 May 2016 14:52:22 +0200 Subject: [PATCH 03/13] Use levenshtein level for better Bundle matching --- .../Controller/ControllerNameParser.php | 1 + .../Tests/Controller/ControllerNameParserTest.php | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php index 873c736841..374d0eba10 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php @@ -142,6 +142,7 @@ class ControllerNameParser $lev = levenshtein($nonExistentBundleName, $bundleName); if ($lev <= strlen($nonExistentBundleName) / 3 && ($alternative === null || $lev < $shortest)) { $alternative = $bundleName; + $shortest = $lev; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php index 4718c1eb0b..0fe47b0908 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php @@ -59,8 +59,8 @@ class ControllerNameParserTest extends TestCase { $parser = $this->createParser(); - $this->assertEquals('FooBundle:Default:index', $parser->build('TestBundle\FooBundle\Controller\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); - $this->assertEquals('FooBundle:Sub\Default:index', $parser->build('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); + $this->assertEquals('FoooooBundle:Default:index', $parser->build('TestBundle\FooBundle\Controller\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); + $this->assertEquals('FoooooBundle:Sub\Default:index', $parser->build('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); try { $parser->build('TestBundle\FooBundle\Controller\DefaultController::index'); @@ -132,8 +132,9 @@ class ControllerNameParserTest extends TestCase public function getInvalidBundleNameTests() { return array( - array('FoodBundle:Default:index', 'FooBundle:Default:index'), - array('CrazyBundle:Default:index', false), + 'Alternative will be found using levenshtein' => array('FoodBundle:Default:index', 'FooBundle:Default:index'), + 'Alternative will be found using partial match' => array('FabpotFooBund:Default:index', 'FabpotFooBundle:Default:index'), + 'Bundle does not exist at all' => array('CrazyBundle:Default:index', false), ); } @@ -162,6 +163,7 @@ class ControllerNameParserTest extends TestCase $bundles = array( 'SensioFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), 'SensioCmsFooBundle' => $this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle'), + 'FoooooBundle' => $this->getBundle('TestBundle\FooBundle', 'FoooooBundle'), 'FooBundle' => $this->getBundle('TestBundle\FooBundle', 'FooBundle'), 'FabpotFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), ); From 103526b40f6cfc46ad2f627e4e19c8f02cc08276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 12 May 2016 17:11:08 +0200 Subject: [PATCH 04/13] Catch \Throwable --- src/Symfony/Component/Debug/DebugClassLoader.php | 4 ++++ src/Symfony/Component/Debug/ErrorHandler.php | 6 ++++++ src/Symfony/Component/OptionsResolver/OptionsResolver.php | 6 ++++++ src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php | 2 ++ 4 files changed, 18 insertions(+) diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 36afa36930..603c814b56 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -175,6 +175,10 @@ class DebugClassLoader } catch (\Exception $e) { ErrorHandler::unstackErrors(); + throw $e; + } catch (\Throwable $e) { + ErrorHandler::unstackErrors(); + throw $e; } diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 33779af325..9c30ffea44 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -447,6 +447,10 @@ class ErrorHandler } catch (\Exception $e) { $this->isRecursive = false; + throw $e; + } catch (\Throwable $e) { + $this->isRecursive = false; + throw $e; } } @@ -555,6 +559,8 @@ class ErrorHandler } } catch (\Exception $exception) { // Handled below + } catch (\Throwable $exception) { + // Handled below } if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index e0578af717..bc763202f2 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -861,6 +861,9 @@ class OptionsResolver implements Options, OptionsResolverInterface } catch (\Exception $e) { unset($this->calling[$option]); throw $e; + } catch (\Throwable $e) { + unset($this->calling[$option]); + throw $e; } unset($this->calling[$option]); // END @@ -963,6 +966,9 @@ class OptionsResolver implements Options, OptionsResolverInterface } catch (\Exception $e) { unset($this->calling[$option]); throw $e; + } catch (\Throwable $e) { + unset($this->calling[$option]); + throw $e; } unset($this->calling[$option]); // END diff --git a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php index cc4b2ef498..1035bd70c2 100644 --- a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php @@ -141,6 +141,8 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface $this->dumpLine(-1); } catch (\Exception $exception) { // Re-thrown below + } catch (\Throwable $exception) { + // Re-thrown below } if ($output) { $this->setOutput($prevOutput); From de671f4cb2299f46fdbbd43e3bc747e813b0a90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 12 May 2016 17:11:08 +0200 Subject: [PATCH 05/13] Catch \Throwable --- .../Component/DependencyInjection/Compiler/AutowirePass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index e4457d4608..7bb0516dc0 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -44,8 +44,8 @@ class AutowirePass implements CompilerPassInterface $this->completeDefinition($id, $definition); } } - } catch (\Error $e) { } catch (\Exception $e) { + } catch (\Throwable $e) { } spl_autoload_unregister($throwingAutoloader); From 3234ca491c20f3b173448115838056ac284b8c80 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 19 May 2016 21:50:23 +0200 Subject: [PATCH 06/13] fix removed commands wording in upgrade file --- UPGRADE-3.0.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 35823d8aaf..aef162311c 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -1604,8 +1604,7 @@ UPGRADE FROM 2.x to 3.0 ### WebProfiler - * The `profiler:import` and `profiler:export` commands have been deprecated and - will be removed in 3.0. + * The `profiler:import` and `profiler:export` commands have been removed. * All the profiler storages different than `FileProfilerStorage` have been removed. The removed classes are: From cd66a452c6576fd308716f1c20905c08d20903f2 Mon Sep 17 00:00:00 2001 From: bradbyu Date: Fri, 13 May 2016 10:45:14 -0400 Subject: [PATCH 07/13] Update UPGRADE FROM 2.x to 3.0 --- UPGRADE-3.0.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 475fcbbc80..467c13e644 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -132,7 +132,10 @@ UPGRADE FROM 2.x to 3.0 ### Form - * The `ChoiceToBooleanArrayTransformer`, `ChoicesToBooleanArrayTransformer`, + * The `max_length` option was removed. Use the `attr` option instead by setting it to + an `array` with a `maxlength` key. + + * The `ChoiceToBooleanArrayTransformer`, `ChoicesToBooleanArrayTransformer`, `FixRadioInputListener`, and `FixCheckboxInputListener` classes were removed. * The `choice_list` option of `ChoiceType` was removed. From 94e4706609b5bfe5f0ac2c26f53141042a65b55c Mon Sep 17 00:00:00 2001 From: Peter Rehm Date: Thu, 19 May 2016 16:20:34 +0200 Subject: [PATCH 08/13] Fixed server status command when port has been omitted --- .../Bundle/FrameworkBundle/Command/ServerStatusCommand.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php index 2c6d2c49ff..fa5c537a0c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php @@ -13,6 +13,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** @@ -31,6 +32,7 @@ class ServerStatusCommand extends ServerCommand $this ->setDefinition(array( new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), + new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), )) ->setName('server:status') ->setDescription('Outputs the status of the built-in web server for the given address') @@ -44,6 +46,10 @@ class ServerStatusCommand extends ServerCommand { $address = $input->getArgument('address'); + if (false === strpos($address, ':')) { + $address = $address.':'.$input->getOption('port'); + } + // remove an orphaned lock file if (file_exists($this->getLockFile($address)) && !$this->isServerRunning($address)) { unlink($this->getLockFile($address)); From a4b1fa669432ff433df65a7e89e122c3d7d8b892 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 14 May 2016 17:57:37 +0200 Subject: [PATCH 09/13] chomp newlines only at the end of YAML documents --- src/Symfony/Component/Yaml/Parser.php | 26 ++++++++++++++----- .../Component/Yaml/Tests/ParserTest.php | 3 ++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index a37e3e6948..67f780aaae 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -25,6 +25,7 @@ class Parser const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN; private $offset = 0; + private $totalNumberOfLines; private $lines = array(); private $currentLineNb = -1; private $currentLine = ''; @@ -33,11 +34,13 @@ class Parser /** * Constructor. * - * @param int $offset The offset of YAML document (used for line numbers in error messages) + * @param int $offset The offset of YAML document (used for line numbers in error messages) + * @param int|null $totalNumberOfLines The overall number of lines being parsed */ - public function __construct($offset = 0) + public function __construct($offset = 0, $totalNumberOfLines = null) { $this->offset = $offset; + $this->totalNumberOfLines = $totalNumberOfLines; } /** @@ -61,6 +64,10 @@ class Parser $value = $this->cleanup($value); $this->lines = explode("\n", $value); + if (null === $this->totalNumberOfLines) { + $this->totalNumberOfLines = count($this->lines); + } + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('UTF-8'); @@ -93,7 +100,7 @@ class Parser // array if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { $c = $this->getRealCurrentLineNb() + 1; - $parser = new self($c); + $parser = new self($c, $this->totalNumberOfLines); $parser->refs = &$this->refs; $data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport); } else { @@ -102,7 +109,7 @@ class Parser ) { // this is a compact notation element, add to next block and parse $c = $this->getRealCurrentLineNb(); - $parser = new self($c); + $parser = new self($c, $this->totalNumberOfLines); $parser->refs = &$this->refs; $block = $values['value']; @@ -153,7 +160,7 @@ class Parser $value = $this->getNextEmbedBlock(); } $c = $this->getRealCurrentLineNb() + 1; - $parser = new self($c); + $parser = new self($c, $this->totalNumberOfLines); $parser->refs = &$this->refs; $parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport); @@ -190,7 +197,7 @@ class Parser $data[$key] = null; } else { $c = $this->getRealCurrentLineNb() + 1; - $parser = new self($c); + $parser = new self($c, $this->totalNumberOfLines); $parser->refs = &$this->refs; $data[$key] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport); } @@ -528,6 +535,8 @@ class Parser if ($notEOF) { $blockLines[] = ''; $this->moveToPreviousLine(); + } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { + $blockLines[] = ''; } // folded style @@ -634,6 +643,11 @@ class Parser return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#'; } + private function isCurrentLineLastLineInDocument() + { + return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); + } + /** * Cleanups a YAML string to be parsed. * diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 34cc81b2d3..6645c36108 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -826,6 +826,7 @@ EOT foo # bar baz + EOT , ), @@ -854,7 +855,7 @@ EOT; $expected = array( 'foo' => array( 'bar' => array( - 'scalar-block' => 'line1 line2>', + 'scalar-block' => "line1 line2>\n", ), 'baz' => array( 'foobar' => null, From c2f7fedfd6494ac8e97347e554b0d3d16f7086eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 22 May 2016 13:47:44 +0200 Subject: [PATCH 10/13] [Serializer] Add test for ignored attributes during denormalization --- .../Tests/Normalizer/ObjectNormalizerTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index c0602d7fa6..398a579b9f 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -324,6 +324,19 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase ); } + public function testIgnoredAttributesDenormalize() + { + $this->normalizer->setIgnoredAttributes(array('fooBar', 'bar', 'baz')); + + $obj = new ObjectDummy(); + $obj->setFoo('foo'); + + $this->assertEquals( + $obj, + $this->normalizer->denormalize(array('fooBar' => 'fooBar', 'foo' => 'foo', 'baz' => 'baz'), __NAMESPACE__.'\ObjectDummy') + ); + } + public function provideCallbacks() { return array( From 717e1a9e47d0899efcb65676cf10285a43a03a23 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 22 May 2016 18:02:36 +0200 Subject: [PATCH 11/13] [Yaml] properly handle unindented collections --- src/Symfony/Component/Yaml/Parser.php | 2 +- .../Component/Yaml/Tests/ParserTest.php | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index a37e3e6948..d27968bb40 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -711,7 +711,7 @@ class Parser */ private function isStringUnIndentedCollectionItem() { - return 0 === strpos($this->currentLine, '- '); + return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); } /** diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 34cc81b2d3..ba4064272a 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -557,6 +557,34 @@ EOF ); } + public function testSequenceInMappingStartedBySingleDashLine() + { + $yaml = << array( + array( + 'b' => array( + array( + 'bar' => 'baz', + ), + ), + ), + 'foo', + ), + 'd' => 'e', + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + /** * @expectedException \Symfony\Component\Yaml\Exception\ParseException */ From 02070f9fd353df6395db1cff0a46eb0ea3cbb605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mor=C3=A1vek=20=28moravek=2Emartin=29?= Date: Sun, 22 May 2016 21:49:29 +0900 Subject: [PATCH 12/13] People - person singularization --- src/Symfony/Component/PropertyAccess/StringUtil.php | 3 +++ src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/Symfony/Component/PropertyAccess/StringUtil.php b/src/Symfony/Component/PropertyAccess/StringUtil.php index 248a6483d8..caa8c3d7e1 100644 --- a/src/Symfony/Component/PropertyAccess/StringUtil.php +++ b/src/Symfony/Component/PropertyAccess/StringUtil.php @@ -124,6 +124,9 @@ class StringUtil // chateaux (chateau) array('xuae', 4, false, true, 'eau'), + + // people (person) + array('elpoep', 6, true, true, 'person'), ); /** diff --git a/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php b/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php index 0fd6bb69b2..983e355cb9 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/StringUtilTest.php @@ -107,6 +107,8 @@ class StringUtilTest extends \PHPUnit_Framework_TestCase array('objectives', 'objective'), array('oxen', 'ox'), array('parties', 'party'), + array('people', 'person'), + array('persons', 'person'), array('phenomena', array('phenomenon', 'phenomenum')), array('photos', 'photo'), array('pianos', 'piano'), From 9bdaba48018b459bb8fdc3673e6155e0e418adb7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 23 May 2016 11:57:38 +0200 Subject: [PATCH 13/13] [Yaml] fix exception contexts --- src/Symfony/Component/Yaml/Parser.php | 8 ++++---- src/Symfony/Component/Yaml/Tests/ParserTest.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 46e7549749..2739723c7e 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -88,7 +88,7 @@ class Parser $isRef = $isInPlace = $isProcessed = false; if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { if ($context && 'mapping' == $context) { - throw new ParseException('You cannot define a sequence item when in a mapping'); + throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); } $context = 'sequence'; @@ -127,7 +127,7 @@ class Parser } } elseif (preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $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'); + throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); } $context = 'mapping'; @@ -214,7 +214,7 @@ class Parser } else { // multiple documents are not supported if ('---' === $this->currentLine) { - throw new ParseException('Multiple documents are not supported.'); + throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine); } // 1-liner optionally followed by newline(s) @@ -449,7 +449,7 @@ class Parser } if (!array_key_exists($value, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLine); + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine); } return $this->refs[$value]; diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index c3fc6b2ada..87f0cdbb1b 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -525,7 +525,7 @@ EOF; /** * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage Multiple documents are not supported. + * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/ */ public function testMultipleDocumentsNotSupportedException() {