Merge branch '2.7' into 2.8
* 2.7: [DependencyInjection] fix dumped YAML snytax Remove InputOption::VALUE_REQUIRED mode from $default parameter description as InputOption::setDefault() throws an exception only when called in InputOption::VALUE_NONE mode. In practice the $default value could still be accessed in InputOption::VALUE_REQUIRED mode in case InputOption was never set but accessed from InputDefinition::getOption() method [Form] Fixed violation mapping if multiple forms are using the same (or part of the same) property path [TwigBridge] Symfony 3.1 forward compatibility
This commit is contained in:
commit
fc2fa79bb5
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Bridge\Twig\Extension;
|
namespace Symfony\Bridge\Twig\Extension;
|
||||||
|
|
||||||
use Symfony\Component\Yaml\Dumper as YamlDumper;
|
use Symfony\Component\Yaml\Dumper as YamlDumper;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides integration of the Yaml component with Twig.
|
* Provides integration of the Yaml component with Twig.
|
||||||
@ -40,7 +41,7 @@ class YamlExtension extends \Twig_Extension
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (defined('Symfony\Component\Yaml\Yaml::DUMP_OBJECT')) {
|
if (defined('Symfony\Component\Yaml\Yaml::DUMP_OBJECT')) {
|
||||||
$dumpObjects = (int) $dumpObjects;
|
return $dumper->dump($input, $inline, 0, is_bool($dumpObjects) ? Yaml::DUMP_OBJECT : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $dumper->dump($input, $inline, 0, false, $dumpObjects);
|
return $dumper->dump($input, $inline, 0, false, $dumpObjects);
|
||||||
|
@ -390,7 +390,7 @@ class Command
|
|||||||
* @param string $shortcut The shortcut (can be null)
|
* @param string $shortcut The shortcut (can be null)
|
||||||
* @param int $mode The option mode: One of the InputOption::VALUE_* constants
|
* @param int $mode The option mode: One of the InputOption::VALUE_* constants
|
||||||
* @param string $description A description text
|
* @param string $description A description text
|
||||||
* @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE)
|
* @param mixed $default The default value (must be null for InputOption::VALUE_NONE)
|
||||||
*
|
*
|
||||||
* @return Command The current instance
|
* @return Command The current instance
|
||||||
*/
|
*/
|
||||||
|
@ -39,7 +39,7 @@ class InputOption
|
|||||||
* @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
|
* @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
|
||||||
* @param int $mode The option mode: One of the VALUE_* constants
|
* @param int $mode The option mode: One of the VALUE_* constants
|
||||||
* @param string $description A description text
|
* @param string $description A description text
|
||||||
* @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE)
|
* @param mixed $default The default value (must be null for self::VALUE_NONE)
|
||||||
*
|
*
|
||||||
* @throws InvalidArgumentException If option mode is invalid or incompatible
|
* @throws InvalidArgumentException If option mode is invalid or incompatible
|
||||||
*/
|
*/
|
||||||
|
@ -65,7 +65,7 @@ class YamlDumper extends Dumper
|
|||||||
$class = substr($class, 1);
|
$class = substr($class, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$code .= sprintf(" class: %s\n", $class);
|
$code .= sprintf(" class: %s\n", $this->dumper->dump($class));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$definition->isPublic()) {
|
if (!$definition->isPublic()) {
|
||||||
@ -117,7 +117,7 @@ class YamlDumper extends Dumper
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($definition->getFactoryClass(false)) {
|
if ($definition->getFactoryClass(false)) {
|
||||||
$code .= sprintf(" factory_class: %s\n", $definition->getFactoryClass(false));
|
$code .= sprintf(" factory_class: %s\n", $this->dumper->dump($definition->getFactoryClass(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($definition->isLazy()) {
|
if ($definition->isLazy()) {
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper;
|
|||||||
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Dumper\YamlDumper;
|
use Symfony\Component\DependencyInjection\Dumper\YamlDumper;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class YamlDumperTest extends \PHPUnit_Framework_TestCase
|
class YamlDumperTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
@ -27,17 +28,14 @@ class YamlDumperTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
$dumper = new YamlDumper($container = new ContainerBuilder());
|
$dumper = new YamlDumper($container = new ContainerBuilder());
|
||||||
|
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services1.yml', $dumper->dump(), '->dump() dumps an empty container as an empty YAML file');
|
$this->assertEqualYamlStructure(self::$fixturesPath.'/yaml/services1.yml', $dumper->dump(), '->dump() dumps an empty container as an empty YAML file');
|
||||||
|
|
||||||
$container = new ContainerBuilder();
|
|
||||||
$dumper = new YamlDumper($container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddParameters()
|
public function testAddParameters()
|
||||||
{
|
{
|
||||||
$container = include self::$fixturesPath.'/containers/container8.php';
|
$container = include self::$fixturesPath.'/containers/container8.php';
|
||||||
$dumper = new YamlDumper($container);
|
$dumper = new YamlDumper($container);
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services8.yml', $dumper->dump(), '->dump() dumps parameters');
|
$this->assertEqualYamlStructure(self::$fixturesPath.'/yaml/services8.yml', $dumper->dump(), '->dump() dumps parameters');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,7 +63,7 @@ class YamlDumperTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
$container = include self::$fixturesPath.'/containers/container9.php';
|
$container = include self::$fixturesPath.'/containers/container9.php';
|
||||||
$dumper = new YamlDumper($container);
|
$dumper = new YamlDumper($container);
|
||||||
$this->assertEquals(str_replace('%path%', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR, file_get_contents(self::$fixturesPath.'/yaml/services9.yml')), $dumper->dump(), '->dump() dumps services');
|
$this->assertEqualYamlStructure(str_replace('%path%', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR, file_get_contents(self::$fixturesPath.'/yaml/services9.yml')), $dumper->dump(), '->dump() dumps services');
|
||||||
|
|
||||||
$dumper = new YamlDumper($container = new ContainerBuilder());
|
$dumper = new YamlDumper($container = new ContainerBuilder());
|
||||||
$container->register('foo', 'FooClass')->addArgument(new \stdClass());
|
$container->register('foo', 'FooClass')->addArgument(new \stdClass());
|
||||||
@ -84,4 +82,9 @@ class YamlDumperTest extends \PHPUnit_Framework_TestCase
|
|||||||
$dumper = new YamlDumper($container);
|
$dumper = new YamlDumper($container);
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services24.yml', $dumper->dump());
|
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services24.yml', $dumper->dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertEqualYamlStructure($yaml, $expected, $message = '')
|
||||||
|
{
|
||||||
|
$this->assertEquals(Yaml::parse($expected), Yaml::parse($yaml), $message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ services:
|
|||||||
|
|
||||||
configurator: sc_configure
|
configurator: sc_configure
|
||||||
foo.baz:
|
foo.baz:
|
||||||
class: %baz_class%
|
class: '%baz_class%'
|
||||||
factory_class: %baz_class%
|
factory_class: '%baz_class%'
|
||||||
factory_method: getInstance
|
factory_method: getInstance
|
||||||
configurator: ['%baz_class%', configureStatic1]
|
configurator: ['%baz_class%', configureStatic1]
|
||||||
factory_service:
|
factory_service:
|
||||||
@ -28,6 +28,6 @@ services:
|
|||||||
factory_method: getInstance
|
factory_method: getInstance
|
||||||
factory_service: foo.baz
|
factory_service: foo.baz
|
||||||
foo_bar:
|
foo_bar:
|
||||||
class: %foo_class%
|
class: '%foo_class%'
|
||||||
shared: false
|
shared: false
|
||||||
scope: prototype
|
scope: prototype
|
||||||
|
@ -6,4 +6,4 @@ services:
|
|||||||
class: BAR
|
class: BAR
|
||||||
|
|
||||||
project:
|
project:
|
||||||
test: %project.parameter.foo%
|
test: '%project.parameter.foo%'
|
||||||
|
@ -2,7 +2,7 @@ services:
|
|||||||
foo: { class: FooClass }
|
foo: { class: FooClass }
|
||||||
baz: { class: BazClass }
|
baz: { class: BazClass }
|
||||||
not_shared: { class: FooClass, shared: false }
|
not_shared: { class: FooClass, shared: false }
|
||||||
file: { class: FooClass, file: %path%/foo.php }
|
file: { class: FooClass, file: '%path%/foo.php' }
|
||||||
arguments: { class: FooClass, arguments: [foo, '@foo', [true, false]] }
|
arguments: { class: FooClass, arguments: [foo, '@foo', [true, false]] }
|
||||||
configurator1: { class: FooClass, configurator: sc_configure }
|
configurator1: { class: FooClass, configurator: sc_configure }
|
||||||
configurator2: { class: FooClass, configurator: ['@baz', configure] }
|
configurator2: { class: FooClass, configurator: ['@baz', configure] }
|
||||||
|
@ -18,7 +18,7 @@ services:
|
|||||||
factory: [Bar\FooClass, getInstance]
|
factory: [Bar\FooClass, getInstance]
|
||||||
configurator: sc_configure
|
configurator: sc_configure
|
||||||
foo.baz:
|
foo.baz:
|
||||||
class: %baz_class%
|
class: '%baz_class%'
|
||||||
factory: ['%baz_class%', getInstance]
|
factory: ['%baz_class%', getInstance]
|
||||||
configurator: ['%baz_class%', configureStatic1]
|
configurator: ['%baz_class%', configureStatic1]
|
||||||
bar:
|
bar:
|
||||||
@ -26,11 +26,11 @@ services:
|
|||||||
arguments: [foo, '@foo.baz', '%foo_bar%']
|
arguments: [foo, '@foo.baz', '%foo_bar%']
|
||||||
configurator: ['@foo.baz', configure]
|
configurator: ['@foo.baz', configure]
|
||||||
foo_bar:
|
foo_bar:
|
||||||
class: %foo_class%
|
class: '%foo_class%'
|
||||||
shared: false
|
shared: false
|
||||||
method_call1:
|
method_call1:
|
||||||
class: Bar\FooClass
|
class: Bar\FooClass
|
||||||
file: %path%foo.php
|
file: '%path%foo.php'
|
||||||
calls:
|
calls:
|
||||||
- [setBar, ['@foo']]
|
- [setBar, ['@foo']]
|
||||||
- [setBar, ['@?foo2']]
|
- [setBar, ['@?foo2']]
|
||||||
|
@ -148,12 +148,9 @@ class ViolationMapper implements ViolationMapperInterface
|
|||||||
*/
|
*/
|
||||||
private function matchChild(FormInterface $form, PropertyPathIteratorInterface $it)
|
private function matchChild(FormInterface $form, PropertyPathIteratorInterface $it)
|
||||||
{
|
{
|
||||||
// Remember at what property path underneath "data"
|
$target = null;
|
||||||
// we are looking. Check if there is a child with that
|
|
||||||
// path, otherwise increase path by one more piece
|
|
||||||
$chunk = '';
|
$chunk = '';
|
||||||
$foundChild = null;
|
$foundAtIndex = null;
|
||||||
$foundAtIndex = 0;
|
|
||||||
|
|
||||||
// Construct mapping rules for the given form
|
// Construct mapping rules for the given form
|
||||||
$rules = array();
|
$rules = array();
|
||||||
@ -165,17 +162,11 @@ class ViolationMapper implements ViolationMapperInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip forms inheriting their parent data when iterating the children
|
$children = iterator_to_array(new \RecursiveIteratorIterator(
|
||||||
$childIterator = new \RecursiveIteratorIterator(
|
|
||||||
new InheritDataAwareIterator($form)
|
new InheritDataAwareIterator($form)
|
||||||
);
|
));
|
||||||
|
|
||||||
// Make the path longer until we find a matching child
|
|
||||||
while (true) {
|
|
||||||
if (!$it->valid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while ($it->valid()) {
|
||||||
if ($it->isIndex()) {
|
if ($it->isIndex()) {
|
||||||
$chunk .= '['.$it->current().']';
|
$chunk .= '['.$it->current().']';
|
||||||
} else {
|
} else {
|
||||||
@ -197,33 +188,27 @@ class ViolationMapper implements ViolationMapperInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test children unless we already found one
|
/** @var FormInterface $child */
|
||||||
if (null === $foundChild) {
|
foreach ($children as $key => $child) {
|
||||||
foreach ($childIterator as $child) {
|
$childPath = (string) $child->getPropertyPath();
|
||||||
/* @var FormInterface $child */
|
if ($childPath === $chunk) {
|
||||||
$childPath = (string) $child->getPropertyPath();
|
$target = $child;
|
||||||
|
$foundAtIndex = $it->key();
|
||||||
// Child found, mark as return value
|
} elseif (0 === strpos($childPath, $chunk)) {
|
||||||
if ($chunk === $childPath) {
|
continue;
|
||||||
$foundChild = $child;
|
|
||||||
$foundAtIndex = $it->key();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unset($children[$key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add element to the chunk
|
|
||||||
$it->next();
|
$it->next();
|
||||||
|
|
||||||
// If we reached the end of the path or if there are no
|
|
||||||
// more matching mapping rules, return the found child
|
|
||||||
if (null !== $foundChild && (!$it->valid() || count($rules) === 0)) {
|
|
||||||
// Reset index in case we tried to find mapping
|
|
||||||
// rules further down the path
|
|
||||||
$it->seek($foundAtIndex);
|
|
||||||
|
|
||||||
return $foundChild;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (null !== $foundAtIndex) {
|
||||||
|
$it->seek($foundAtIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $target;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1539,4 +1539,25 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals(array($this->getFormError($violation, $grandChild)), iterator_to_array($grandChild->getErrors()), $grandChildName.' should have an error, but has none');
|
$this->assertEquals(array($this->getFormError($violation, $grandChild)), iterator_to_array($grandChild->getErrors()), $grandChildName.' should have an error, but has none');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testBacktrackIfSeveralSubFormsWithSamePropertyPath()
|
||||||
|
{
|
||||||
|
$violation = $this->getConstraintViolation('data.address[street]');
|
||||||
|
$parent = $this->getForm('parent');
|
||||||
|
$child1 = $this->getForm('subform1', 'address');
|
||||||
|
$child2 = $this->getForm('subform2', 'address');
|
||||||
|
$grandChild = $this->getForm('street');
|
||||||
|
|
||||||
|
$parent->add($child1);
|
||||||
|
$parent->add($child2);
|
||||||
|
$child2->add($grandChild);
|
||||||
|
|
||||||
|
$this->mapper->mapViolation($violation, $parent);
|
||||||
|
|
||||||
|
// The error occurred on the child of the second form with the same path
|
||||||
|
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
|
||||||
|
$this->assertCount(0, $child1->getErrors(), $child1->getName().' should not have an error, but has one');
|
||||||
|
$this->assertCount(0, $child2->getErrors(), $child2->getName().' should not have an error, but has one');
|
||||||
|
$this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChild->getName().' should have an error, but has none');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user