diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md
index cc725bab2c..c471e64dd9 100644
--- a/UPGRADE-3.0.md
+++ b/UPGRADE-3.0.md
@@ -1341,8 +1341,21 @@ UPGRADE FROM 2.x to 3.0
* Using a colon in an unquoted mapping value leads to a `ParseException`.
* Starting an unquoted string with `@`, `` ` ``, `|`, or `>` leads to a `ParseException`.
- * Deprecated non-escaped \ in double-quoted strings when parsing Yaml
- ("Foo\Var" is not valid whereas "Foo\\Var" is)
+ * When surrounding strings with double-quotes, you must now escape `\` characters. Not
+ escaping those characters (when surrounded by double-quotes) leads to a `ParseException`.
+
+ Before:
+
+ ```yml
+ class: "Foo\Var"
+ ```
+
+ After:
+
+ ```yml
+ class: "Foo\\Var"
+ ```
+
* The ability to pass file names to `Yaml::parse()` has been removed.
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
index 767e2798f3..e997615d11 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
@@ -25,15 +25,13 @@ col-sm-2
{# Rows #}
{% block form_row -%}
-{% spaceless %}
-{% endspaceless %}
+{##}
{%- endblock form_row %}
{% block checkbox_row -%}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php
new file mode 100644
index 0000000000..bf26dda05b
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php
@@ -0,0 +1,118 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\FormExtension;
+use Symfony\Bridge\Twig\Form\TwigRenderer;
+use Symfony\Bridge\Twig\Form\TwigRendererEngine;
+use Symfony\Bridge\Twig\Extension\TranslationExtension;
+use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator;
+use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader;
+use Symfony\Component\Form\FormView;
+use Symfony\Component\Form\Tests\AbstractBootstrap3HorizontalLayoutTest;
+
+class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3HorizontalLayoutTest
+{
+ /**
+ * @var FormExtension
+ */
+ protected $extension;
+
+ protected $testableFeatures = array(
+ 'choice_attr',
+ );
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $rendererEngine = new TwigRendererEngine(array(
+ 'bootstrap_3_horizontal_layout.html.twig',
+ 'custom_widgets.html.twig',
+ ));
+ $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface'));
+
+ $this->extension = new FormExtension($renderer);
+
+ $loader = new StubFilesystemLoader(array(
+ __DIR__.'/../../Resources/views/Form',
+ __DIR__.'/Fixtures/templates/form',
+ ));
+
+ $environment = new \Twig_Environment($loader, array('strict_variables' => true));
+ $environment->addExtension(new TranslationExtension(new StubTranslator()));
+ $environment->addExtension($this->extension);
+
+ $this->extension->initRuntime($environment);
+ }
+
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ $this->extension = null;
+ }
+
+ protected function renderForm(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->renderBlock($view, 'form', $vars);
+ }
+
+ protected function renderEnctype(FormView $view)
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'enctype');
+ }
+
+ protected function renderLabel(FormView $view, $label = null, array $vars = array())
+ {
+ if ($label !== null) {
+ $vars += array('label' => $label);
+ }
+
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars);
+ }
+
+ protected function renderErrors(FormView $view)
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors');
+ }
+
+ protected function renderWidget(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars);
+ }
+
+ protected function renderRow(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars);
+ }
+
+ protected function renderRest(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars);
+ }
+
+ protected function renderStart(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars);
+ }
+
+ protected function renderEnd(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars);
+ }
+
+ protected function setTheme(FormView $view, array $themes)
+ {
+ $this->extension->renderer->setTheme($view, $themes);
+ }
+}
diff --git a/src/Symfony/Component/Asset/Package.php b/src/Symfony/Component/Asset/Package.php
index 43bdcf21db..77b1c934eb 100644
--- a/src/Symfony/Component/Asset/Package.php
+++ b/src/Symfony/Component/Asset/Package.php
@@ -60,6 +60,9 @@ class Package implements PackageInterface
return $this->context;
}
+ /**
+ * @return VersionStrategyInterface
+ */
protected function getVersionStrategy()
{
return $this->versionStrategy;
diff --git a/src/Symfony/Component/Asset/UrlPackage.php b/src/Symfony/Component/Asset/UrlPackage.php
index 6381a9c1cd..3626ec843c 100644
--- a/src/Symfony/Component/Asset/UrlPackage.php
+++ b/src/Symfony/Component/Asset/UrlPackage.php
@@ -39,10 +39,11 @@ class UrlPackage extends Package
private $sslPackage;
/**
- * @param string|array $baseUrls Base asset URLs
+ * @param string|string[] $baseUrls Base asset URLs
* @param VersionStrategyInterface $versionStrategy The version strategy
+ * @param ContextInterface|null $context Context
*/
- public function __construct($baseUrls = array(), VersionStrategyInterface $versionStrategy, ContextInterface $context = null)
+ public function __construct($baseUrls, VersionStrategyInterface $versionStrategy, ContextInterface $context = null)
{
parent::__construct($versionStrategy, $context);
diff --git a/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php
index 6028eb57fe..857cf9432b 100644
--- a/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php
+++ b/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php
@@ -21,6 +21,10 @@ class StaticVersionStrategy implements VersionStrategyInterface
private $version;
private $format;
+ /**
+ * @param string $version Version number
+ * @param string $format Url format
+ */
public function __construct($version, $format = null)
{
$this->version = $version;
diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
index 4fb08772d7..d1375df889 100644
--- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
+++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
@@ -217,7 +217,7 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
return $this->data;
}
- private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output = null, array &$outputByHash)
+ private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output, array &$outputByHash)
{
$hash = spl_object_hash($form);
@@ -236,7 +236,7 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
}
}
- private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output = null, array &$outputByHash)
+ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output, array &$outputByHash)
{
$viewHash = spl_object_hash($view);
$formHash = null;
diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php
new file mode 100644
index 0000000000..1273fa505b
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php
@@ -0,0 +1,157 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests;
+
+abstract class AbstractBootstrap3HorizontalLayoutTest extends AbstractBootstrap3LayoutTest
+{
+ public function testLabelOnForm()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType');
+ $view = $form->createView();
+ $this->renderWidget($view, array('label' => 'foo'));
+ $html = $this->renderLabel($view);
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@class="col-sm-2 control-label required"]
+ [.="[trans]Name[/trans]"]
+'
+ );
+ }
+
+ public function testLabelDoesNotRenderFieldAttributes()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
+ $html = $this->renderLabel($form->createView(), null, array(
+ 'attr' => array(
+ 'class' => 'my&class',
+ ),
+ ));
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@for="name"]
+ [@class="col-sm-2 control-label required"]
+'
+ );
+ }
+
+ public function testLabelWithCustomAttributesPassedDirectly()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
+ $html = $this->renderLabel($form->createView(), null, array(
+ 'label_attr' => array(
+ 'class' => 'my&class',
+ ),
+ ));
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@for="name"]
+ [@class="my&class col-sm-2 control-label required"]
+'
+ );
+ }
+
+ public function testLabelWithCustomTextAndCustomAttributesPassedDirectly()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
+ $html = $this->renderLabel($form->createView(), 'Custom label', array(
+ 'label_attr' => array(
+ 'class' => 'my&class',
+ ),
+ ));
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@for="name"]
+ [@class="my&class col-sm-2 control-label required"]
+ [.="[trans]Custom label[/trans]"]
+'
+ );
+ }
+
+ public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
+ 'label' => 'Custom label',
+ ));
+ $html = $this->renderLabel($form->createView(), null, array(
+ 'label_attr' => array(
+ 'class' => 'my&class',
+ ),
+ ));
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@for="name"]
+ [@class="my&class col-sm-2 control-label required"]
+ [.="[trans]Custom label[/trans]"]
+'
+ );
+ }
+
+ public function testStartTag()
+ {
+ $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
+ 'method' => 'get',
+ 'action' => 'http://example.com/directory',
+ ));
+
+ $html = $this->renderStart($form->createView());
+
+ $this->assertSame('