diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerForV7.php index d3acd53960..18bbdbeba0 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerForV7.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerForV7.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Bridge\PhpUnit; +namespace Symfony\Bridge\PhpUnit\Legacy; use PHPUnit\Framework\Test; use PHPUnit\Framework\TestListener; diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index c15f6d2014..fdd5f36c9e 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -13,6 +13,7 @@ namespace Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper; use ProxyManager\Generator\ClassGenerator; use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy; +use ProxyManager\Version; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface; @@ -88,27 +89,43 @@ EOF; */ public function getProxyCode(Definition $definition) { - return preg_replace( + $code = $this->classGenerator->generate($this->generateProxyClass($definition)); + + $code = preg_replace( '/(\$this->initializer[0-9a-f]++) && \1->__invoke\(\$this->(valueHolder[0-9a-f]++), (.*?), \1\);/', '$1 && ($1->__invoke(\$$2, $3, $1) || 1) && $this->$2 = \$$2;', - $this->classGenerator->generate($this->generateProxyClass($definition)) + $code ); + + if (version_compare(self::getProxyManagerVersion(), '2.2', '<')) { + $code = preg_replace( + '/((?:\$(?:this|initializer|instance)->)?(?:publicProperties|initializer|valueHolder))[0-9a-f]++/', + '${1}'.$this->getIdentifierSuffix($definition), + $code + ); + } + + return $code; + } + + private static function getProxyManagerVersion(): string + { + if (!\class_exists(Version::class)) { + return '0.0.1'; + } + + return defined(Version::class.'::VERSION') ? Version::VERSION : Version::getVersion(); } /** * Produces the proxy class name for the given definition. - * - * @return string */ - private function getProxyClassName(Definition $definition) + private function getProxyClassName(Definition $definition): string { - return preg_replace('/^.*\\\\/', '', $definition->getClass()).'_'.substr(hash('sha256', $definition->getClass().$this->salt), -7); + return preg_replace('/^.*\\\\/', '', $definition->getClass()).'_'.$this->getIdentifierSuffix($definition); } - /** - * @return ClassGenerator - */ - private function generateProxyClass(Definition $definition) + private function generateProxyClass(Definition $definition): ClassGenerator { $generatedClass = new ClassGenerator($this->getProxyClassName($definition)); @@ -116,4 +133,9 @@ EOF; return $generatedClass; } + + private function getIdentifierSuffix(Definition $definition): string + { + return substr(hash('sha256', $definition->getClass().$this->salt), -7); + } } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index 6dee01fcce..725d4246ba 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -58,6 +58,14 @@ class ProxyDumperTest extends TestCase ); } + public function testDeterministicProxyCode() + { + $definition = new Definition(__CLASS__); + $definition->setLazy(true); + + $this->assertSame($this->dumper->getProxyCode($definition), $this->dumper->getProxyCode($definition)); + } + public function testGetProxyFactoryCode() { $definition = new Definition(__CLASS__); diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index cc9dfe36de..bbc5e3a322 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -259,6 +259,6 @@ class CodeExtension extends AbstractExtension $line .= ''; } - return $line; + return trim($line); } } diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig index 12981e3462..7ecc26b7b9 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig @@ -3,11 +3,25 @@ {# Widgets #} {% block money_widget -%} - {% if not valid %} - {% set group_class = ' form-control is-invalid' %} - {% set valid = true %} - {% endif %} - {{- parent() -}} + {%- set prepend = not (money_pattern starts with '{{') -%} + {%- set append = not (money_pattern ends with '}}') -%} + {%- if prepend or append -%} +
+ {%- if prepend -%} +
+ {{ money_pattern|replace({ '{{ widget }}':''}) }} +
+ {%- endif -%} + {{- block('form_widget_simple') -}} + {%- if append -%} +
+ {{ money_pattern|replace({ '{{ widget }}':''}) }} +
+ {%- endif -%} +
+ {%- else -%} + {{- block('form_widget_simple') -}} + {%- endif -%} {%- endblock money_widget %} {% block datetime_widget -%} @@ -39,7 +53,6 @@ {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control is-invalid')|trim}) -%} {% set valid = true %} {%- endif -%} - {%- if widget == 'single_text' -%} {{- block('form_widget_simple') -}} {%- else -%} @@ -80,7 +93,9 @@
{% set valid = true %} {{- block('form_widget_simple') -}} - % +
+ % +
{%- endblock percent_widget %} @@ -93,7 +108,7 @@ {%- block widget_attributes -%} {%- if not valid %} - {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control is-invalid')|trim}) %} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' is-invalid')|trim}) %} {% endif -%} {{ parent() }} {%- endblock widget_attributes -%} @@ -105,15 +120,14 @@ {% block checkbox_widget -%} {%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%} - {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-check-input')|trim}) -%} - {% if 'checkbox-inline' in parent_label_class %} - {{- form_label(form, null, { widget: parent() }) -}} - {% elseif 'form-check-inline' in parent_label_class %} -
+ {%- if 'checkbox-custom' in parent_label_class -%} + {%- set attr = attr|merge({class: (attr.class|default('') ~ ' custom-control-input')|trim}) -%} +
{{- form_label(form, null, { widget: parent() }) -}}
- {% else -%} -
+ {%- else -%} + {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-check-input')|trim}) -%} +
{{- form_label(form, null, { widget: parent() }) -}}
{%- endif -%} @@ -121,18 +135,21 @@ {% block radio_widget -%} {%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%} - {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-check-input')|trim}) -%} - {%- if 'radio-inline' in parent_label_class -%} - {{- form_label(form, null, { widget: parent() }) -}} + {%- if 'radio-custom' in parent_label_class -%} + {%- set attr = attr|merge({class: (attr.class|default('') ~ ' custom-control-input')|trim}) -%} +
+ {{- form_label(form, null, { widget: parent() }) -}} +
{%- else -%} -
+ {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-check-input')|trim}) -%} +
{{- form_label(form, null, { widget: parent() }) -}}
{%- endif -%} {%- endblock radio_widget %} {% block choice_widget_expanded -%} - {% if '-inline' in label_attr.class|default('') -%} +
{%- for child in form %} {{- form_widget(child, { parent_label_class: label_attr.class|default(''), @@ -140,20 +157,7 @@ valid: valid, }) -}} {% endfor -%} - {%- else -%} - {%- if not valid -%} - {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-control is-invalid')|trim}) %} - {%- endif -%} -
- {%- for child in form %} - {{- form_widget(child, { - parent_label_class: label_attr.class|default(''), - translation_domain: choice_translation_domain, - valid: true, - }) -}} - {% endfor -%} -
- {%- endif %} +
{%- endblock choice_widget_expanded %} {# Labels #} @@ -162,7 +166,7 @@ {% if label is not same as(false) -%} {%- if compound is defined and compound -%} {%- set element = 'legend' -%} - {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-form-legend')|trim}) -%} + {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-form-label')|trim}) -%} {%- else -%} {%- set label_attr = label_attr|merge({for: id, class: (label_attr.class|default('') ~ ' form-control-label')|trim}) -%} {%- endif -%} @@ -179,19 +183,26 @@ {% set label = name|humanize %} {%- endif -%} {%- endif -%} - <{{ element|default('label') }}{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}{% block form_label_errors %}{{- form_errors(form) -}}{% endblock form_label_errors %} + <{{ element|default('label') }}{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}{{- form_errors(form) -}} {%- endif -%} {%- endblock form_label %} {% block checkbox_radio_label -%} {#- Do not display the label if widget is not defined in order to prevent double label rendering -#} {%- if widget is defined -%} - {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' form-check-label')|trim}) -%} + {%- if parent_label_class is defined and ('checkbox-custom' in parent_label_class or 'radio-custom' in parent_label_class) -%} + {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' custom-control-label')|trim}) -%} + {%- else %} + {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' form-check-label')|trim}) -%} + {%- endif %} + {%- if not compound -%} + {% set label_attr = label_attr|merge({'for': id}) %} + {%- endif -%} {%- if required -%} {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) -%} {%- endif -%} {%- if parent_label_class is defined -%} - {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|trim}) -%} + {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|replace({'checkbox-inline': '', 'radio-inline': '', 'checkbox-custom': '', 'radio-custom': ''})|trim}) -%} {%- endif -%} {%- if label is not same as(false) and label is empty -%} {%- if label_format is not empty -%} @@ -203,8 +214,11 @@ {%- set label = name|humanize -%} {%- endif -%} {%- endif -%} + + {{ widget|raw }} - {{- widget|raw }} {{ label is not same as(false) ? (translation_domain is same as(false) ? label : label|trans({}, translation_domain)) -}} + {{- label is not same as(false) ? (translation_domain is same as(false) ? label : label|trans({}, translation_domain)) -}} + {{- form_errors(form) -}} {%- endif -%} {%- endblock checkbox_radio_label %} @@ -225,12 +239,12 @@ {% block form_errors -%} {%- if errors|length > 0 -%} -
-
    - {%- for error in errors -%} -
  • {{ error.message }}
  • - {%- endfor -%} -
-
+
+
    + {%- for error in errors -%} +
  • {{ error.message }}
  • + {%- endfor -%} +
+
{%- endif %} {%- endblock form_errors %} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig index d57978220f..71aedf99ad 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig @@ -147,7 +147,7 @@ {% block choice_label -%} {# remove the checkbox-inline and radio-inline class, it's only useful for embed labels #} - {%- set label_attr = label_attr|merge({class: label_attr.class|default('')|replace({'checkbox-inline': '', 'radio-inline': ''})|trim}) -%} + {%- set label_attr = label_attr|merge({class: label_attr.class|default('')|replace({'checkbox-inline': '', 'radio-inline': '', 'checkbox-custom': '', 'radio-custom': ''})|trim}) -%} {{- block('form_label') -}} {% endblock choice_label %} diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 5c7d9bfd53..1d08eb246e 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -23,7 +23,7 @@ "symfony/asset": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/finder": "~3.4|~4.0", - "symfony/form": "~3.4|~4.0", + "symfony/form": "^3.4.5|^4.0.5", "symfony/http-foundation": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-intl-icu": "~1.0", @@ -41,7 +41,7 @@ "symfony/workflow": "~3.4|~4.0" }, "conflict": { - "symfony/form": "<3.4", + "symfony/form": "<3.4.5|<4.0.5,>=4.0", "symfony/console": "<3.4" }, "suggest": { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php index 35ea20a89a..4f09e52bdc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php @@ -26,16 +26,16 @@ class AddAnnotationsCachedReaderPass implements CompilerPassInterface { // "annotations.cached_reader" is wired late so that any passes using // "annotation_reader" at build time don't get any cache - if ($container->hasDefinition('annotations.cached_reader')) { - $reader = $container->getDefinition('annotations.cached_reader'); + foreach ($container->findTaggedServiceIds('annotations.cached_reader') as $id => $tags) { + $reader = $container->getDefinition($id); $properties = $reader->getProperties(); if (isset($properties['cacheProviderBackup'])) { $provider = $properties['cacheProviderBackup']->getValues()[0]; unset($properties['cacheProviderBackup']); $reader->setProperties($properties); - $container->set('annotations.cached_reader', null); - $container->setDefinition('annotations.cached_reader', $reader->replaceArgument(1, $provider)); + $container->set($id, null); + $container->setDefinition($id, $reader->replaceArgument(1, $provider)); } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index ecc4d9e563..cb3366a499 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; class UnusedTagsPass implements CompilerPassInterface { private $whitelist = array( + 'annotations.cached_reader', 'cache.pool.clearer', 'console.command', 'container.hot_path', diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 95d0264450..f9f3ddf040 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1116,7 +1116,9 @@ class FrameworkExtension extends Extension ->replaceArgument(2, $config['debug']) // temporary property to lazy-reference the cache provider without using it until AddAnnotationsCachedReaderPass runs ->setProperty('cacheProviderBackup', new ServiceClosureArgument(new Reference($cacheService))) + ->addTag('annotations.cached_reader') ; + $container->setAlias('annotation_reader', 'annotations.cached_reader')->setPrivate(true); $container->setAlias(Reader::class, new Alias('annotations.cached_reader', false)); } else { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index dbc21d897a..72010831a4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -25,6 +25,7 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\ProxyAdapter; use Symfony\Component\Cache\Adapter\RedisAdapter; use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; @@ -582,10 +583,12 @@ abstract class FrameworkExtensionTest extends TestCase public function testAnnotations() { - $container = $this->createContainerFromFile('full'); + $container = $this->createContainerFromFile('full', array(), true, false); + $container->addCompilerPass(new TestAnnotationsPass()); + $container->compile(); $this->assertEquals($container->getParameter('kernel.cache_dir').'/annotations', $container->getDefinition('annotations.filesystem_cache')->getArgument(0)); - $this->assertSame('annotations.filesystem_cache', (string) $container->getDefinition('annotations.cached_reader')->getArgument(1)); + $this->assertSame('annotations.filesystem_cache', (string) $container->getDefinition('annotation_reader')->getArgument(1)); } public function testFileLinkFormat() @@ -1029,10 +1032,10 @@ abstract class FrameworkExtensionTest extends TestCase ), $data))); } - protected function createContainerFromFile($file, $data = array(), $resetCompilerPasses = true) + protected function createContainerFromFile($file, $data = array(), $resetCompilerPasses = true, $compile = true) { $cacheKey = md5(get_class($this).$file.serialize($data)); - if (isset(self::$containerCache[$cacheKey])) { + if ($compile && isset(self::$containerCache[$cacheKey])) { return self::$containerCache[$cacheKey]; } $container = $this->createContainer($data); @@ -1043,7 +1046,12 @@ abstract class FrameworkExtensionTest extends TestCase $container->getCompilerPassConfig()->setOptimizationPasses(array()); $container->getCompilerPassConfig()->setRemovingPasses(array()); } - $container->getCompilerPassConfig()->setBeforeRemovingPasses(array(new AddAnnotationsCachedReaderPass(), new AddConstraintValidatorsPass(), new TranslatorPass('translator.default', 'translation.reader'))); + $container->getCompilerPassConfig()->setBeforeRemovingPasses(array(new AddConstraintValidatorsPass(), new TranslatorPass('translator.default', 'translation.reader'))); + $container->getCompilerPassConfig()->setAfterRemovingPasses(array(new AddAnnotationsCachedReaderPass())); + + if (!$compile) { + return $container; + } $container->compile(); return self::$containerCache[$cacheKey] = $container; @@ -1136,3 +1144,15 @@ abstract class FrameworkExtensionTest extends TestCase } } } + +/** + * Simulates ReplaceAliasByActualDefinitionPass. + */ +class TestAnnotationsPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $container->setDefinition('annotation_reader', $container->getDefinition('annotations.cached_reader')); + $container->removeDefinition('annotations.cached_reader'); + } +} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig index 903d009c98..a1ff4afd01 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -58,7 +58,7 @@
Route name - {{ collector.route|default('NONE') }} + {{ collector.route|default('n/a') }}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig index d0f5cda02d..f69406475a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig @@ -68,7 +68,7 @@ a.doc:hover { .anchor { position: relative; - display: block; + display: inline-block; top: -7em; visibility: hidden; } diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php index 23e6f141ca..5934479a90 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php @@ -53,7 +53,7 @@ abstract class AbstractBootstrap4HorizontalLayoutTest extends AbstractBootstrap4 $this->assertMatchesXpath($html, '/legend - [@class="col-form-label col-sm-2 col-form-legend required"] + [@class="col-form-label col-sm-2 col-form-label required"] [.="[trans]Name[/trans]"] ' ); @@ -144,7 +144,7 @@ abstract class AbstractBootstrap4HorizontalLayoutTest extends AbstractBootstrap4 $this->assertMatchesXpath($html, '/legend - [@class="col-sm-2 col-form-legend required"] + [@class="col-sm-2 col-form-label required"] [.="[trans]Custom label[/trans]"] ' ); diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php index fdaa07e1c1..197f3ab8d6 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php @@ -53,7 +53,7 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest $this->assertMatchesXpath($html, '/legend - [@class="col-form-legend required"] + [@class="col-form-label required"] [.="[trans]Name[/trans]"] ' ); @@ -144,7 +144,7 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest $this->assertMatchesXpath($html, '/legend - [@class="col-form-legend required"] + [@class="col-form-label required"] [.="[trans]Custom label[/trans]"] ' ); @@ -184,12 +184,10 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest '/div [@class="form-check"] [ - ./label - [.=" [trans]Name[/trans]"] + ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class form-check-input"][@checked="checked"][@value="1"] + /following-sibling::label + [.="[trans]Name[/trans]"] [@class="form-check-label required"] - [ - ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class form-check-input"][@checked="checked"][@value="1"] - ] ] ' ); @@ -234,20 +232,16 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]Choice&A[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label + [.="[trans]Choice&A[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&B[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label + [.="[trans]Choice&B[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -263,11 +257,9 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest '/div [@class="form-check"] [ - ./label - [.=" [trans]Name[/trans]"] - [ - ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class form-check-input"][not(@checked)] - ] + ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class form-check-input"][not(@checked)] + /following-sibling::label + [.="[trans]Name[/trans]"] ] ' ); @@ -283,11 +275,9 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest '/div [@class="form-check"] [ - ./label - [.=" [trans]Name[/trans]"] - [ - ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class form-check-input"][@value="foo&bar"] - ] + ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class form-check-input"][@value="foo&bar"] + /following-sibling::label + [.="[trans]Name[/trans]"] ] ' ); @@ -307,20 +297,16 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]Choice&A[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label + [.="[trans]Choice&A[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&B[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label + [.="[trans]Choice&B[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -343,18 +329,14 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label ] /following-sibling::div [@class="form-check"] [ - ./label - [ - ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -383,28 +365,22 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]label.&a[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label + [.="[trans]label.&a[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [ - ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]label.&c[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_2"][@value="&c"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_2"][@value="&c"][not(@checked)] + /following-sibling::label + [.="[trans]label.&c[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -429,18 +405,14 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label ] /following-sibling::div [@class="form-check"] [ - ./label - [ - ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -463,20 +435,16 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" Choice&A"] - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label + [.="Choice&A"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" Choice&B"] - [ - ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label + [.="Choice&B"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -499,20 +467,16 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]Choice&A[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label + [.="[trans]Choice&A[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&B[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)][@class="foo&bar form-check-input"] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)][@class="foo&bar form-check-input"] + /following-sibling::label + [.="[trans]Choice&B[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -536,29 +500,23 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]Test&Me[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + /following-sibling::label + [.="[trans]Test&Me[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&A[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label + [.="[trans]Choice&A[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&B[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label + [.="[trans]Choice&B[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -583,29 +541,23 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" Placeholder&Not&Translated"] - [ - ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + /following-sibling::label + [.="Placeholder&Not&Translated"] + ] + /following-sibling::div + [@class="form-check"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label + [.="Choice&A"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" Choice&A"] - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@checked] - ] - ] - /following-sibling::div - [@class="form-check"] - [ - ./label - [.=" Choice&B"] - [ - ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label + [.="Choice&B"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -627,20 +579,16 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]Choice&A[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_0"][@checked] - ] + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label + [.="[trans]Choice&A[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&B[/trans]"] - [ - ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] - ] + ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label + [.="[trans]Choice&B[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -663,29 +611,23 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]Choice&A[/trans]"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label + [.="[trans]Choice&A[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&B[/trans]"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + /following-sibling::label + [.="[trans]Choice&B[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&C[/trans]"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label + [.="[trans]Choice&C[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -708,18 +650,14 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label ] /following-sibling::div [@class="form-check"] [ - ./label - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -748,28 +686,22 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]label.&a[/trans]"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label + [.="[trans]label.&a[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]label.&c[/trans]"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@value="&c"][not(@checked)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@value="&c"][not(@checked)] + /following-sibling::label + [.="[trans]label.&c[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -794,18 +726,14 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label ] /following-sibling::div [@class="form-check"] [ - ./label - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -829,29 +757,23 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" Choice&A"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label + [.="Choice&A"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" Choice&B"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + /following-sibling::label + [.="Choice&B"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" Choice&C"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label + [.="Choice&C"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -875,29 +797,23 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./div [@class="form-check"] [ - ./label - [.=" [trans]Choice&A[/trans]"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label + [.="[trans]Choice&A[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&B[/trans]"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)][@class="foo&bar form-check-input"] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)][@class="foo&bar form-check-input"] + /following-sibling::label + [.="[trans]Choice&B[/trans]"] ] /following-sibling::div [@class="form-check"] [ - ./label - [.=" [trans]Choice&C[/trans]"] - [ - ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] - ] + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label + [.="[trans]Choice&C[/trans]"] ] /following-sibling::input[@type="hidden"][@id="name__token"] ] @@ -913,17 +829,15 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest '/div [@class="form-check"] [ - ./label + ./input + [@id="my&id"] + [@type="radio"] + [@name="name"] + [@class="my&class form-check-input"] + [@checked="checked"] + [@value="1"] + /following-sibling::label [@class="form-check-label required"] - [ - ./input - [@id="my&id"] - [@type="radio"] - [@name="name"] - [@class="my&class form-check-input"] - [@checked="checked"] - [@value="1"] - ] ] ' ); @@ -937,16 +851,14 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest '/div [@class="form-check"] [ - ./label + ./input + [@id="my&id"] + [@type="radio"] + [@name="name"] + [@class="my&class form-check-input"] + [not(@checked)] + /following-sibling::label [@class="form-check-label required"] - [ - ./input - [@id="my&id"] - [@type="radio"] - [@name="name"] - [@class="my&class form-check-input"] - [not(@checked)] - ] ] ' ); @@ -962,16 +874,15 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest '/div [@class="form-check"] [ - ./label + ./input + [@id="my&id"] + [@type="radio"] + [@name="name"] + [@class="my&class form-check-input"] + [@value="foo&bar"] + /following-sibling::label [@class="form-check-label required"] - [ - ./input - [@id="my&id"] - [@type="radio"] - [@name="name"] - [@class="my&class form-check-input"] - [@value="foo&bar"] - ] + [@for="my&id"] ] ' ); @@ -996,6 +907,61 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class form-control-file')), '/input [@type="file"] +' + ); + } + + public function testMoney() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\MoneyType', 1234.56, array( + 'currency' => 'EUR', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), + '/div + [@class="input-group"] + [ + ./div + [@class="input-group-prepend"] + [ + ./span + [@class="input-group-text"] + [contains(.., "€")] + ] + /following-sibling::input + [@id="my&id"] + [@type="text"] + [@name="name"] + [@class="my&class form-control"] + [@value="1234.56"] + ] +' + ); + } + + public function testPercent() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PercentType', 0.1); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), + '/div + [@class="input-group"] + [ + ./input + [@id="my&id"] + [@type="text"] + [@name="name"] + [@class="my&class form-control"] + [@value="10"] + /following-sibling::div + [@class="input-group-append"] + [ + ./span + [@class="input-group-text"] + [contains(.., "%")] + ] + + ] ' ); } diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php index 5f0ea5c0a9..0a153dd943 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php @@ -29,6 +29,8 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; */ abstract class AbstractTestSessionListener implements EventSubscriberInterface { + private $sessionId; + public function onKernelRequest(GetResponseEvent $event) { if (!$event->isMasterRequest()) { @@ -44,7 +46,8 @@ abstract class AbstractTestSessionListener implements EventSubscriberInterface $cookies = $event->getRequest()->cookies; if ($cookies->has($session->getName())) { - $session->setId($cookies->get($session->getName())); + $this->sessionId = $cookies->get($session->getName()); + $session->setId($this->sessionId); } } @@ -66,9 +69,10 @@ abstract class AbstractTestSessionListener implements EventSubscriberInterface $session->save(); } - if ($session instanceof Session ? !$session->isEmpty() : $wasStarted) { + if ($session instanceof Session ? !$session->isEmpty() || $session->getId() !== $this->sessionId : $wasStarted) { $params = session_get_cookie_params(); $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly'])); + $this->sessionId = $session->getId(); } } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php index 4452f48771..0a2263d5a8 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php @@ -13,8 +13,10 @@ namespace Symfony\Component\HttpKernel\Tests\EventListener; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\EventListener\SessionListener; @@ -86,6 +88,22 @@ class TestSessionListenerTest extends TestCase $this->assertSame(array(), $response->headers->getCookies()); } + public function testEmptySessionWithNewSessionIdDoesSendCookie() + { + $this->sessionHasBeenStarted(); + $this->sessionIsEmpty(); + $this->fixSessionId('456'); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('/', 'GET', array(), array(new Cookie('MOCKSESSID', '123'))); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + + $response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST); + + $this->assertNotEmpty($response->headers->getCookies()); + } + public function testUnstartedSessionIsNotSave() { $this->sessionHasNotBeenStarted(); @@ -150,6 +168,13 @@ class TestSessionListenerTest extends TestCase ->will($this->returnValue(true)); } + private function fixSessionId($sessionId) + { + $this->session->expects($this->any()) + ->method('getId') + ->will($this->returnValue($sessionId)); + } + private function getSession() { $mock = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session') diff --git a/src/Symfony/Component/Stopwatch/StopwatchEvent.php b/src/Symfony/Component/Stopwatch/StopwatchEvent.php index 410f935a26..de1259a9f1 100644 --- a/src/Symfony/Component/Stopwatch/StopwatchEvent.php +++ b/src/Symfony/Component/Stopwatch/StopwatchEvent.php @@ -150,7 +150,7 @@ class StopwatchEvent /** * Gets the relative time of the start of the first period. * - * @return int The time (in milliseconds) + * @return int|float The time (in milliseconds) */ public function getStartTime() { @@ -160,7 +160,7 @@ class StopwatchEvent /** * Gets the relative time of the end of the last period. * - * @return int The time (in milliseconds) + * @return int|float The time (in milliseconds) */ public function getEndTime() { @@ -172,7 +172,7 @@ class StopwatchEvent /** * Gets the duration of the events (including all periods). * - * @return int The duration (in milliseconds) + * @return int|float The duration (in milliseconds) */ public function getDuration() { diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 195d3185e4..6c4e55925e 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -372,54 +372,39 @@ class Parser // try to parse the value as a multi-line string as a last resort if (0 === $this->currentLineNb) { - $parseError = false; $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = false; $value = ''; foreach ($this->lines as $line) { - try { - if (isset($line[0]) && ('"' === $line[0] || "'" === $line[0])) { - $parsedLine = $line; - } else { - $parsedLine = Inline::parse($line, $flags, $this->refs); - } + if ('' === trim($line)) { + $value .= "\n"; + } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { + $value .= ' '; + } - if (!is_string($parsedLine)) { - $parseError = true; - break; - } + if ('' !== trim($line) && '\\' === substr($line, -1)) { + $value .= ltrim(substr($line, 0, -1)); + } elseif ('' !== trim($line)) { + $value .= trim($line); + } - if ('' === trim($parsedLine)) { - $value .= "\n"; - } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { - $value .= ' '; - } - - if ('' !== trim($parsedLine) && '\\' === substr($parsedLine, -1)) { - $value .= ltrim(substr($parsedLine, 0, -1)); - } elseif ('' !== trim($parsedLine)) { - $value .= trim($parsedLine); - } - - if ('' === trim($parsedLine)) { - $previousLineWasNewline = true; - $previousLineWasTerminatedWithBackslash = false; - } elseif ('\\' === substr($parsedLine, -1)) { - $previousLineWasNewline = false; - $previousLineWasTerminatedWithBackslash = true; - } else { - $previousLineWasNewline = false; - $previousLineWasTerminatedWithBackslash = false; - } - } catch (ParseException $e) { - $parseError = true; - break; + if ('' === trim($line)) { + $previousLineWasNewline = true; + $previousLineWasTerminatedWithBackslash = false; + } elseif ('\\' === substr($line, -1)) { + $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = true; + } else { + $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = false; } } - if (!$parseError) { + try { return Inline::parse(trim($value)); + } catch (ParseException $e) { + // fall-through to the ParseException thrown below } } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index fe3f079860..cd9085a76e 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -672,6 +672,42 @@ EOT; $this->assertSame($expected, $this->parser->parse($yaml)); } + public function testNonStringFollowedByCommentEmbeddedInMapping() + { + $yaml = <<<'EOT' +a: + b: + {} +# comment + d: + 1.1 +# another comment +EOT; + $expected = array( + 'a' => array( + 'b' => array(), + 'd' => 1.1, + ), + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + + public function testMultiLineStringLastResortParsing() + { + $yaml = <<<'EOT' +test: + You can have things that don't look like strings here + true + yes you can +EOT; + $expected = array( + 'test' => 'You can have things that don\'t look like strings here true yes you can', + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + /** * @expectedException \Symfony\Component\Yaml\Exception\ParseException */