Skip to content

Commit 9dff164

Browse files
committed
Merge branch '3.3' into 3.4
* 3.3: [DI] Fix deep-inlining of non-shared refs parse newlines in quoted multiline strings Fix collision between view properties and form fields
2 parents 8353d69 + 561767c commit 9dff164

File tree

9 files changed

+150
-14
lines changed

9 files changed

+150
-14
lines changed

src/Symfony/Bridge/Twig/Extension/FormExtension.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Bridge\Twig\Form\TwigRendererInterface;
1616
use Symfony\Component\DependencyInjection\ContainerInterface;
1717
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
18+
use Symfony\Component\Form\FormView;
1819
use Twig\Environment;
1920
use Twig\Extension\AbstractExtension;
2021
use Twig\Extension\InitRuntimeInterface;
@@ -105,6 +106,7 @@ public function getTests()
105106
{
106107
return array(
107108
new TwigTest('selectedchoice', 'Symfony\Bridge\Twig\Extension\twig_is_selected_choice'),
109+
new TwigTest('rootform', array($this, 'isRootForm')),
108110
);
109111
}
110112

@@ -164,6 +166,11 @@ public function __unset($name)
164166
unset($this->$name);
165167
}
166168

169+
public function isRootForm(FormView $formView)
170+
{
171+
return null === $formView->parent;
172+
}
173+
167174
/**
168175
* {@inheritdoc}
169176
*/

src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,12 @@
140140

141141
{% block form_errors -%}
142142
{% if errors|length > 0 -%}
143-
{% if form.parent %}<span class="help-block">{% else %}<div class="alert alert-danger">{% endif %}
143+
{% if form is not rootform %}<span class="help-block">{% else %}<div class="alert alert-danger">{% endif %}
144144
<ul class="list-unstyled">
145145
{%- for error in errors -%}
146146
<li><span class="glyphicon glyphicon-exclamation-sign"></span> {{ error.message }}</li>
147147
{%- endfor -%}
148148
</ul>
149-
{% if form.parent %}</span>{% else %}</div>{% endif %}
149+
{% if form is not rootform %}</span>{% else %}</div>{% endif %}
150150
{%- endif %}
151151
{%- endblock form_errors %}

src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
{%- block form_widget_compound -%}
1717
<div {{ block('widget_container_attributes') }}>
18-
{%- if form.parent is empty -%}
18+
{%- if form is rootform -%}
1919
{{ form_errors(form) }}
2020
{%- endif -%}
2121
{{- block('form_rows') -}}
@@ -349,7 +349,7 @@
349349
{% endif %}
350350
{%- endfor %}
351351

352-
{% if not form.methodRendered and form.parent is null %}
352+
{% if not form.methodRendered and form is rootform %}
353353
{%- do form.setMethodRendered() -%}
354354
{% set method = method|upper %}
355355
{%- if method in ["GET", "POST"] -%}

src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
{%- block form_widget_compound -%}
3333
<table {{ block('widget_container_attributes') }}>
34-
{%- if form.parent is empty and errors|length > 0 -%}
34+
{%- if form is rootform and errors|length > 0 -%}
3535
<tr>
3636
<td colspan="2">
3737
{{- form_errors(form) -}}

src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ public function testStartTagHasActionAttributeWhenActionIsZero()
149149
$this->assertSame('<form name="form" method="get" action="0">', $html);
150150
}
151151

152+
public function isRootFormProvider()
153+
{
154+
return array(
155+
array(true, new FormView()),
156+
array(false, new FormView(new FormView())),
157+
);
158+
}
159+
160+
/**
161+
* @dataProvider isRootFormProvider
162+
*/
163+
public function testIsRootForm($expected, FormView $formView)
164+
{
165+
$this->assertSame($expected, $this->extension->isRootForm($formView));
166+
}
167+
152168
protected function renderForm(FormView $view, array $vars = array())
153169
{
154170
return (string) $this->renderer->renderBlock($view, 'form', $vars);

src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1515
use Symfony\Component\DependencyInjection\Definition;
16+
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
1617
use Symfony\Component\DependencyInjection\Reference;
1718

1819
/**
@@ -23,6 +24,7 @@
2324
class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface
2425
{
2526
private $repeatedPass;
27+
private $cloningIds = array();
2628
private $inlinedServiceIds = array();
2729

2830
/**
@@ -58,18 +60,44 @@ protected function processValue($value, $isRoot = false)
5860
// Reference found in ArgumentInterface::getValues() are not inlineable
5961
return $value;
6062
}
61-
if ($value instanceof Reference && $this->container->hasDefinition($id = (string) $value)) {
62-
$definition = $this->container->getDefinition($id);
6363

64-
if ($this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) {
65-
$this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId));
66-
$this->inlinedServiceIds[$id][] = $this->currentId;
67-
68-
return $definition->isShared() ? $definition : clone $definition;
64+
if ($value instanceof Definition && $this->cloningIds) {
65+
if ($value->isShared()) {
66+
return $value;
6967
}
68+
$value = clone $value;
69+
}
70+
71+
if (!$value instanceof Reference || !$this->container->hasDefinition($id = (string) $value)) {
72+
return parent::processValue($value, $isRoot);
73+
}
74+
75+
$definition = $this->container->getDefinition($id);
76+
77+
if (!$this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) {
78+
return $value;
7079
}
7180

72-
return parent::processValue($value, $isRoot);
81+
$this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId));
82+
$this->inlinedServiceIds[$id][] = $this->currentId;
83+
84+
if ($definition->isShared()) {
85+
return $definition;
86+
}
87+
88+
if (isset($this->cloningIds[$id])) {
89+
$ids = array_keys($this->cloningIds);
90+
$ids[] = $id;
91+
92+
throw new ServiceCircularReferenceException($id, array_slice($ids, array_search($id, $ids)));
93+
}
94+
95+
$this->cloningIds[$id] = true;
96+
try {
97+
return $this->processValue($definition);
98+
} finally {
99+
unset($this->cloningIds[$id]);
100+
}
73101
}
74102

75103
/**

src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,60 @@ public function testProcessInlinesMixedServicesLoop()
111111
$this->assertEquals($container->getDefinition('foo')->getArgument(0), $container->getDefinition('bar'));
112112
}
113113

114+
/**
115+
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
116+
* @expectedExceptionMessage Circular reference detected for service "bar", path: "bar -> foo -> bar".
117+
*/
118+
public function testProcessThrowsOnNonSharedLoops()
119+
{
120+
$container = new ContainerBuilder();
121+
$container
122+
->register('foo')
123+
->addArgument(new Reference('bar'))
124+
->setShared(false)
125+
;
126+
$container
127+
->register('bar')
128+
->setShared(false)
129+
->addMethodCall('setFoo', array(new Reference('foo')))
130+
;
131+
132+
$this->process($container);
133+
}
134+
135+
public function testProcessNestedNonSharedServices()
136+
{
137+
$container = new ContainerBuilder();
138+
$container
139+
->register('foo')
140+
->addArgument(new Reference('bar1'))
141+
->addArgument(new Reference('bar2'))
142+
;
143+
$container
144+
->register('bar1')
145+
->setShared(false)
146+
->addArgument(new Reference('baz'))
147+
;
148+
$container
149+
->register('bar2')
150+
->setShared(false)
151+
->addArgument(new Reference('baz'))
152+
;
153+
$container
154+
->register('baz')
155+
->setShared(false)
156+
;
157+
158+
$this->process($container);
159+
160+
$baz1 = $container->getDefinition('foo')->getArgument(0)->getArgument(0);
161+
$baz2 = $container->getDefinition('foo')->getArgument(1)->getArgument(0);
162+
163+
$this->assertEquals($container->getDefinition('baz'), $baz1);
164+
$this->assertEquals($container->getDefinition('baz'), $baz2);
165+
$this->assertNotSame($baz1, $baz2);
166+
}
167+
114168
public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition()
115169
{
116170
$container = new ContainerBuilder();

src/Symfony/Component/Yaml/Parser.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,8 @@ private function parseValue($value, $flags, $context)
738738
return Inline::parse($value, $flags, $this->refs);
739739
}
740740

741+
$lines = array();
742+
741743
while ($this->moveToNextLine()) {
742744
// unquoted strings end before the first unindented line
743745
if (null === $quotation && 0 === $this->getCurrentLineIndentation()) {
@@ -746,14 +748,29 @@ private function parseValue($value, $flags, $context)
746748
break;
747749
}
748750

749-
$value .= ' '.trim($this->currentLine);
751+
$lines[] = trim($this->currentLine);
750752

751753
// quoted string values end with a line that is terminated with the quotation character
752754
if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) {
753755
break;
754756
}
755757
}
756758

759+
for ($i = 0, $linesCount = count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
760+
if ('' === $lines[$i]) {
761+
$value .= "\n";
762+
$previousLineBlank = true;
763+
} elseif ($previousLineBlank) {
764+
$value .= $lines[$i];
765+
$previousLineBlank = false;
766+
} else {
767+
$value .= ' '.$lines[$i];
768+
$previousLineBlank = false;
769+
}
770+
}
771+
772+
Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
773+
757774
$parsedValue = Inline::parse($value, $flags, $this->refs);
758775

759776
if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {

src/Symfony/Component/Yaml/Tests/ParserTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,20 @@ public function testCommentCharactersInMultiLineQuotedStrings()
15791579
$this->assertSame($expected, $this->parser->parse($yaml));
15801580
}
15811581

1582+
public function testBlankLinesInQuotedMultiLineString()
1583+
{
1584+
$yaml = <<<YAML
1585+
foobar: 'foo
1586+
1587+
bar'
1588+
YAML;
1589+
$expected = array(
1590+
'foobar' => "foo\nbar",
1591+
);
1592+
1593+
$this->assertSame($expected, $this->parser->parse($yaml));
1594+
}
1595+
15821596
public function testParseMultiLineUnquotedString()
15831597
{
15841598
$yaml = <<<EOT

0 commit comments

Comments
 (0)