From 162819fef366d9d466171ee7b5fc1850c29534f1 Mon Sep 17 00:00:00 2001 From: Arman Hosseini <44655055+Arman-Hosseini@users.noreply.github.com> Date: Sun, 21 Jul 2019 16:03:23 +0430 Subject: [PATCH 01/18] Avoid getting right to left style --- .../WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index 8953316de0..4012625e85 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -54,6 +54,7 @@ text-align: left; text-transform: none; z-index: 99999; + direction: ltr; /* neutralize the aliasing defined by external CSS styles */ -webkit-font-smoothing: subpixel-antialiased; From b946b11d5a199a726cbbdabd7d725c45f00ecb0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20ALFAIATE?= Date: Thu, 27 Jul 2017 11:03:55 +0200 Subject: [PATCH 02/18] [Form] Repeat preferred choices in the main list --- .../Form/ChoiceList/Factory/DefaultChoiceListFactory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php index 68da1b2516..7fe1f46247 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php @@ -157,9 +157,9 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface if ($isPreferred && false !== $preferredKey = $isPreferred($choice, $key, $value)) { $preferredViews[$nextIndex] = $view; $preferredViewsOrder[$nextIndex] = $preferredKey; - } else { - $otherViews[$nextIndex] = $view; } + + $otherViews[$nextIndex] = $view; } private static function addChoiceViewsFromStructuredValues($values, $label, $choices, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$preferredViewsOrder, &$otherViews) From 475c7a469ab373eaa0f1666f773ba2abd3602e96 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 22 Jul 2019 16:05:04 +0200 Subject: [PATCH 03/18] adapt tests --- .../Tests/Form/Type/EntityTypeTest.php | 4 +-- src/Symfony/Bridge/Doctrine/composer.json | 4 +-- .../AbstractBootstrap3LayoutTest.php | 11 +++++--- src/Symfony/Bridge/Twig/composer.json | 4 +-- src/Symfony/Component/Form/CHANGELOG.md | 1 + .../Form/Tests/AbstractLayoutTest.php | 11 +++++--- .../Factory/DefaultChoiceListFactoryTest.php | 26 +++++++++++++++++-- .../Extension/Core/Type/ChoiceTypeTest.php | 4 +++ 8 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 0a9bf739fc..90906ef6e0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -848,7 +848,7 @@ class EntityTypeTest extends BaseTypeTest ]); $this->assertEquals([3 => new ChoiceView($entity3, '3', 'Baz'), 2 => new ChoiceView($entity2, '2', 'Bar')], $field->createView()->vars['preferred_choices']); - $this->assertEquals([1 => new ChoiceView($entity1, '1', 'Foo')], $field->createView()->vars['choices']); + $this->assertEquals([1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar'), 3 => new ChoiceView($entity3, '3', 'Baz')], $field->createView()->vars['choices']); } public function testOverrideChoicesWithPreferredChoices() @@ -868,7 +868,7 @@ class EntityTypeTest extends BaseTypeTest ]); $this->assertEquals([3 => new ChoiceView($entity3, '3', 'Baz')], $field->createView()->vars['preferred_choices']); - $this->assertEquals([2 => new ChoiceView($entity2, '2', 'Bar')], $field->createView()->vars['choices']); + $this->assertEquals([2 => new ChoiceView($entity2, '2', 'Bar'), 3 => new ChoiceView($entity3, '3', 'Baz')], $field->createView()->vars['choices']); } public function testDisallowChoicesThatAreNotIncludedChoicesSingleIdentifier() diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index d022f57c32..3256c521fd 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -27,7 +27,7 @@ "symfony/stopwatch": "^3.4|^4.0|^5.0", "symfony/config": "^4.2|^5.0", "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/form": "^4.3|^5.0", + "symfony/form": "^4.4|^5.0", "symfony/http-kernel": "^3.4|^4.0|^5.0", "symfony/messenger": "^4.3|^5.0", "symfony/property-access": "^3.4|^4.0|^5.0", @@ -48,7 +48,7 @@ "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", "symfony/dependency-injection": "<3.4", - "symfony/form": "<4.3", + "symfony/form": "<4.4", "symfony/messenger": "<4.3" }, "suggest": { diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php index b332ff018d..f3a0a381cd 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php @@ -524,8 +524,9 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][.="[trans]Choice&B[/trans]"] ] - [count(./option)=3] + [count(./option)=4] ' ); } @@ -547,8 +548,9 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest [ ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][.="[trans]Choice&B[/trans]"] ] - [count(./option)=2] + [count(./option)=3] ' ); } @@ -571,8 +573,9 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@disabled="disabled"][not(@selected)][.=""] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][.="[trans]Choice&B[/trans]"] ] - [count(./option)=3] + [count(./option)=4] ' ); } @@ -589,7 +592,7 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']], '/select [@class="my&class form-control"] - [count(./option)=2] + [count(./option)=5] ' ); } diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index c4e6ab29d4..de4b75e05d 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -25,7 +25,7 @@ "symfony/asset": "^3.4|^4.0|^5.0", "symfony/dependency-injection": "^3.4|^4.0|^5.0", "symfony/finder": "^3.4|^4.0|^5.0", - "symfony/form": "^4.3|^5.0", + "symfony/form": "^4.4|^5.0", "symfony/http-foundation": "^4.3|^5.0", "symfony/http-kernel": "^3.4|^4.0|^5.0", "symfony/mime": "^4.3|^5.0", @@ -46,7 +46,7 @@ }, "conflict": { "symfony/console": "<3.4", - "symfony/form": "<4.3", + "symfony/form": "<4.4", "symfony/http-foundation": "<4.3", "symfony/translation": "<4.2", "symfony/workflow": "<4.3" diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 527f84b44a..a082f1fa60 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 4.4.0 ----- + * preferred choices are repeated in the list of all choices * deprecated using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` 4.3.0 diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index b03ac0f9fc..8154893fbf 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -741,8 +741,9 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][.="[trans]Choice&B[/trans]"] ] - [count(./option)=3] + [count(./option)=4] ' ); } @@ -763,8 +764,9 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase [ ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][.="[trans]Choice&B[/trans]"] ] - [count(./option)=2] + [count(./option)=3] ' ); } @@ -786,8 +788,9 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@disabled="disabled"][not(@selected)][.=""] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][.="[trans]Choice&B[/trans]"] ] - [count(./option)=3] + [count(./option)=4] ' ); } @@ -803,7 +806,7 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase $this->assertWidgetMatchesXpath($form->createView(), [], '/select - [count(./option)=2] + [count(./option)=5] ' ); } diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php index b065718054..7073890d6b 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php @@ -739,6 +739,8 @@ class DefaultChoiceListFactoryTest extends TestCase $this->assertEquals(new ChoiceListView( [ 0 => new ChoiceView($this->obj1, '0', 'A'), + 1 => new ChoiceView($this->obj2, '1', 'B'), + 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D'), ], [ 1 => new ChoiceView($this->obj2, '1', 'B'), @@ -752,6 +754,8 @@ class DefaultChoiceListFactoryTest extends TestCase $this->assertEquals(new ChoiceListView( [ 'w' => new ChoiceView($this->obj1, '0', 'A'), + 'x' => new ChoiceView($this->obj2, '1', 'B'), + 'y' => new ChoiceView($this->obj3, '2', 'C'), 'z' => new ChoiceView($this->obj4, '3', 'D'), ], [ 'x' => new ChoiceView($this->obj2, '1', 'B'), @@ -765,6 +769,18 @@ class DefaultChoiceListFactoryTest extends TestCase $this->assertEquals(new ChoiceListView( [ 0 => new ChoiceView($this->obj1, '0', 'A'), + 1 => new ChoiceView( + $this->obj2, + '1', + 'B', + ['attr1' => 'value1'] + ), + 2 => new ChoiceView( + $this->obj3, + '2', + 'C', + ['attr2' => 'value2'] + ), 3 => new ChoiceView($this->obj4, '3', 'D'), ], [ 1 => new ChoiceView( @@ -789,11 +805,17 @@ class DefaultChoiceListFactoryTest extends TestCase [ 'Group 1' => new ChoiceGroupView( 'Group 1', - [0 => new ChoiceView($this->obj1, '0', 'A')] + [ + 0 => new ChoiceView($this->obj1, '0', 'A'), + 1 => new ChoiceView($this->obj2, '1', 'B'), + ] ), 'Group 2' => new ChoiceGroupView( 'Group 2', - [3 => new ChoiceView($this->obj4, '3', 'D')] + [ + 2 => new ChoiceView($this->obj3, '2', 'C'), + 3 => new ChoiceView($this->obj4, '3', 'D'), + ] ), ], [ 'Group 1' => new ChoiceGroupView( diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 83633eed0d..b9ecf57583 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -1731,7 +1731,9 @@ class ChoiceTypeTest extends BaseTypeTest $this->assertEquals([ 0 => new ChoiceView('a', 'a', 'A'), + 1 => new ChoiceView('b', 'b', 'B'), 2 => new ChoiceView('c', 'c', 'C'), + 3 => new ChoiceView('d', 'd', 'D'), ], $view->vars['choices']); $this->assertEquals([ 1 => new ChoiceView('b', 'b', 'B'), @@ -1750,9 +1752,11 @@ class ChoiceTypeTest extends BaseTypeTest $this->assertEquals([ 'Symfony' => new ChoiceGroupView('Symfony', [ 0 => new ChoiceView('a', 'a', 'Bernhard'), + 1 => new ChoiceView('b', 'b', 'Fabien'), 2 => new ChoiceView('c', 'c', 'Kris'), ]), 'Doctrine' => new ChoiceGroupView('Doctrine', [ + 3 => new ChoiceView('d', 'd', 'Jon'), 4 => new ChoiceView('e', 'e', 'Roman'), ]), ], $view->vars['choices']); From 7d0793a94430ae83e67dae8fdf310bde61d2ed73 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 23 Jul 2019 14:07:40 +0200 Subject: [PATCH 04/18] relax some date parser patterns --- .../Intl/DateFormatter/DateFormat/DayTransformer.php | 2 +- .../DateFormatter/DateFormat/MonthTransformer.php | 2 +- .../Intl/DateFormatter/DateFormat/YearTransformer.php | 2 +- .../DateFormatter/AbstractIntlDateFormatterTest.php | 2 ++ .../Tests/DateFormatter/IntlDateFormatterTest.php | 11 +++++++++++ 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php index c07855df1c..8e805baff8 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php @@ -33,7 +33,7 @@ class DayTransformer extends Transformer */ public function getReverseMatchingRegExp($length) { - return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; + return 1 === $length ? '\d{1,2}' : '\d{1,'.$length.'}'; } /** diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php index 40ab1d67d7..04be7858c2 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php @@ -104,7 +104,7 @@ class MonthTransformer extends Transformer $regExp = '[JFMASOND]'; break; default: - $regExp = '\d{'.$length.'}'; + $regExp = '\d{1,'.$length.'}'; break; } diff --git a/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php b/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php index 043e6f321c..a892c664af 100644 --- a/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php +++ b/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php @@ -37,7 +37,7 @@ class YearTransformer extends Transformer */ public function getReverseMatchingRegExp($length) { - return 2 === $length ? '\d{2}' : '\d{4}'; + return 2 === $length ? '\d{2}' : '\d{1,4}'; } /** diff --git a/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php b/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php index a1ade6a42b..e472000974 100644 --- a/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php @@ -618,6 +618,7 @@ abstract class AbstractIntlDateFormatterTest extends TestCase { return [ ['y-M-d', '1970-1-1', 0], + ['y-MM-d', '1970-1-1', 0], ['y-MMM-d', '1970-Jan-1', 0], ['y-MMMM-d', '1970-January-1', 0], ]; @@ -636,6 +637,7 @@ abstract class AbstractIntlDateFormatterTest extends TestCase { return [ ['y-M-d', '1970-1-1', 0], + ['y-M-dd', '1970-1-1', 0], ['y-M-dd', '1970-1-01', 0], ['y-M-ddd', '1970-1-001', 0], ]; diff --git a/src/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php b/src/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php index 03c69d27c4..29f8918be8 100644 --- a/src/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php @@ -183,6 +183,17 @@ class IntlDateFormatterTest extends AbstractIntlDateFormatterTest return $this->notImplemented(parent::parseQuarterProvider()); } + public function testParseThreeDigitsYears() + { + if (PHP_INT_SIZE < 8) { + $this->markTestSkipped('Parsing three digits years requires a 64bit PHP.'); + } + + $formatter = $this->getDefaultDateFormatter('yyyy-M-d'); + $this->assertSame(-32157648000, $formatter->parse('950-12-19')); + $this->assertIsIntlSuccess($formatter, 'U_ZERO_ERROR', IntlGlobals::U_ZERO_ERROR); + } + protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null) { return new IntlDateFormatter($locale, $datetype, $timetype, $timezone, $calendar, $pattern); From 8b1d6cd1f03b62efc7c1256a517d964ce7f81fa2 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Tue, 23 Jul 2019 16:43:56 +0200 Subject: [PATCH 05/18] [Routing] Fix CHANGELOG --- src/Symfony/Component/Routing/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Routing/CHANGELOG.md b/src/Symfony/Component/Routing/CHANGELOG.md index 05ae44b5f1..5f133efd6e 100644 --- a/src/Symfony/Component/Routing/CHANGELOG.md +++ b/src/Symfony/Component/Routing/CHANGELOG.md @@ -12,7 +12,7 @@ CHANGELOG Instead of overwriting them, use `__serialize` and `__unserialize` as extension points which are forward compatible with the new serialization methods in PHP 7.4. * exposed `utf8` Route option, defaults "locale" and "format" in configuration loaders and configurators - * added support for invokable route loader services + * added support for invokable service route loaders 4.2.0 ----- From 653f46c7257d62e458d895f6c2cce30504b42772 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Tue, 23 Jul 2019 16:59:17 +0200 Subject: [PATCH 06/18] [4.3] Remove dead test fixtures --- .../Tests/Normalizer/AbstractObjectNormalizerTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index a3586152b4..af60cb99dc 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -380,7 +380,3 @@ class ArrayDenormalizerDummy implements DenormalizerInterface, SerializerAwareIn $this->serializer = $serializer; } } - -abstract class ObjectSerializerDenormalizer implements SerializerInterface, DenormalizerInterface -{ -} From 84b3359adcf741286b981196314b9164d99db9eb Mon Sep 17 00:00:00 2001 From: George Bateman Date: Tue, 23 Jul 2019 19:56:59 +0100 Subject: [PATCH 07/18] Typo in web profiler --- .../WebProfilerBundle/Resources/views/Collector/form.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index 720da85750..c7b99d2e42 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -646,7 +646,7 @@ {% else %}
-

No options where passed when constructing this form.

+

No options were passed when constructing this form.

{% endif %} From 7f4362bd464ff65d7f6d542726c541534e20059b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 Jul 2019 20:48:28 +0200 Subject: [PATCH 08/18] [HttpClient] rewind stream when using Psr18Client --- src/Symfony/Component/HttpClient/Psr18Client.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpClient/Psr18Client.php b/src/Symfony/Component/HttpClient/Psr18Client.php index a438b7ce94..9ec7a08490 100644 --- a/src/Symfony/Component/HttpClient/Psr18Client.php +++ b/src/Symfony/Component/HttpClient/Psr18Client.php @@ -65,9 +65,15 @@ final class Psr18Client implements ClientInterface public function sendRequest(RequestInterface $request): ResponseInterface { try { + $body = $request->getBody(); + + if ($body->isSeekable()) { + $body->seek(0); + } + $response = $this->client->request($request->getMethod(), (string) $request->getUri(), [ 'headers' => $request->getHeaders(), - 'body' => (string) $request->getBody(), + 'body' => $body->getContents(), 'http_version' => '1.0' === $request->getProtocolVersion() ? '1.0' : null, ]); @@ -79,7 +85,13 @@ final class Psr18Client implements ClientInterface } } - return $psrResponse->withBody($this->streamFactory->createStream($response->getContent(false))); + $body = $this->streamFactory->createStream($response->getContent(false)); + + if ($body->isSeekable()) { + $body->seek(0); + } + + return $psrResponse->withBody($body); } catch (TransportExceptionInterface $e) { if ($e instanceof \InvalidArgumentException) { throw new Psr18RequestException($e, $request); From a310bac624bcc6e32a651e9b677ba1b75947fb6c Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Tue, 23 Jul 2019 20:59:18 +0200 Subject: [PATCH 09/18] [PropertyAccess] Fix PropertyAccessorCollectionTest --- .../Tests/PropertyAccessorCollectionTest.php | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index 113144f2bf..f23239193e 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -43,6 +43,28 @@ class PropertyAccessorCollectionTest_Car } } +class PropertyAccessorCollectionTest_CarOnlyAdder +{ + public function addAxis($axis) + { + } + + public function getAxes() + { + } +} + +class PropertyAccessorCollectionTest_CarOnlyRemover +{ + public function removeAxis($axis) + { + } + + public function getAxes() + { + } +} + class PropertyAccessorCollectionTest_CarNoAdderAndRemover { public function getAxes() @@ -143,25 +165,25 @@ abstract class PropertyAccessorCollectionTest extends PropertyAccessorArrayAcces public function testIsWritableReturnsTrueIfAdderAndRemoverExists() { - $car = $this->getMockBuilder(__CLASS__.'_Car')->getMock(); + $car = new PropertyAccessorCollectionTest_Car(); $this->assertTrue($this->propertyAccessor->isWritable($car, 'axes')); } public function testIsWritableReturnsFalseIfOnlyAdderExists() { - $car = $this->getMockBuilder(__CLASS__.'_CarOnlyAdder')->getMock(); + $car = new PropertyAccessorCollectionTest_CarOnlyAdder(); $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes')); } public function testIsWritableReturnsFalseIfOnlyRemoverExists() { - $car = $this->getMockBuilder(__CLASS__.'_CarOnlyRemover')->getMock(); + $car = new PropertyAccessorCollectionTest_CarOnlyRemover(); $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes')); } public function testIsWritableReturnsFalseIfNoAdderNorRemoverExists() { - $car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock(); + $car = new PropertyAccessorCollectionTest_CarNoAdderAndRemover(); $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes')); } @@ -171,7 +193,7 @@ abstract class PropertyAccessorCollectionTest extends PropertyAccessorArrayAcces */ public function testSetValueFailsIfAdderAndRemoverExistButValueIsNotTraversable() { - $car = $this->getMockBuilder(__CLASS__.'_Car')->getMock(); + $car = new PropertyAccessorCollectionTest_Car(); $this->propertyAccessor->setValue($car, 'axes', 'Not an array or Traversable'); } From 33ed4e43c4eabcdb51c972a49c35ce6e6c9e69c7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 Jul 2019 21:13:48 +0200 Subject: [PATCH 10/18] [HttpClient] rewind streams created from strings --- src/Symfony/Component/HttpClient/HttplugClient.php | 8 +++++++- src/Symfony/Component/HttpClient/Psr18Client.php | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpClient/HttplugClient.php b/src/Symfony/Component/HttpClient/HttplugClient.php index 6c612ce13c..71eb5200ce 100644 --- a/src/Symfony/Component/HttpClient/HttplugClient.php +++ b/src/Symfony/Component/HttpClient/HttplugClient.php @@ -100,7 +100,13 @@ final class HttplugClient implements HttpClient, RequestFactory, StreamFactory, } if (\is_string($body ?? '')) { - return $this->client->createStream($body ?? ''); + $body = $this->client->createStream($body ?? ''); + + if ($body->isSeekable()) { + $body->seek(0); + } + + return $body; } if (\is_resource($body)) { diff --git a/src/Symfony/Component/HttpClient/Psr18Client.php b/src/Symfony/Component/HttpClient/Psr18Client.php index ee8c813b46..acc85b8ea2 100644 --- a/src/Symfony/Component/HttpClient/Psr18Client.php +++ b/src/Symfony/Component/HttpClient/Psr18Client.php @@ -125,7 +125,13 @@ final class Psr18Client implements ClientInterface, RequestFactoryInterface, Str */ public function createStream(string $content = ''): StreamInterface { - return $this->streamFactory->createStream($content); + $stream = $this->streamFactory->createStream($content); + + if ($stream->isSeekable()) { + $stream->seek(0); + } + + return $stream; } /** From 846d3e0e58821013327618a9489bce0c2404d5a5 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Tue, 23 Jul 2019 19:59:27 -0400 Subject: [PATCH 11/18] Decoupling TwigBundle and using the new ErrorRenderer mechanism --- UPGRADE-4.4.md | 2 +- UPGRADE-5.0.md | 5 ++ .../Bundle/WebProfilerBundle/CHANGELOG.md | 1 + .../Controller/ExceptionController.php | 4 ++ .../Controller/ExceptionErrorController.php | 59 +++++++++++++++++++ .../Resources/config/profiler.xml | 8 ++- .../Resources/config/routing/profiler.xml | 4 +- .../views/Collector/exception.css.twig | 2 - .../views/Collector/exception.html.twig | 1 + .../views/Collector/messenger.html.twig | 4 +- .../Resources/views/Profiler/base.html.twig | 2 +- .../views/Profiler/toolbar_redirect.html.twig | 2 +- .../views/images/icon-minus-square.svg | 1 + .../views/images/icon-plus-square.svg | 1 + .../WebProfilerExtensionTest.php | 8 ++- .../Bundle/WebProfilerBundle/composer.json | 1 + 16 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionErrorController.php create mode 100644 src/Symfony/Bundle/WebProfilerBundle/Resources/views/images/icon-minus-square.svg create mode 100644 src/Symfony/Bundle/WebProfilerBundle/Resources/views/images/icon-plus-square.svg diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md index 492591a5d4..ad0bb2420c 100644 --- a/UPGRADE-4.4.md +++ b/UPGRADE-4.4.md @@ -169,7 +169,7 @@ Validator WebProfilerBundle ----------------- - * Deprecated the `ExceptionController::templateExists()` method + * Deprecated the `ExceptionController` class in favor of `ExceptionErrorController` * Deprecated the `TemplateManager::templateExists()` method WebServerBundle diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index ce0ad3e656..678416c586 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -577,6 +577,11 @@ Yaml * The parser is now stricter and will throw a `ParseException` when a mapping is found inside a multi-line string. +WebProfilerBundle +----------------- + + * Removed the `ExceptionController` class, use `ExceptionErrorController` instead. + WebServerBundle --------------- diff --git a/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md b/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md index 821a86b756..f44dd269b2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * Added button to clear the ajax request tab * Deprecated the `ExceptionController::templateExists()` method * Deprecated the `TemplateManager::templateExists()` method + * Deprecated the `ExceptionController` in favor of `ExceptionErrorController` 4.3.0 ----- diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php index f9298965bf..15c54e0c63 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php @@ -20,10 +20,14 @@ use Twig\Environment; use Twig\Error\LoaderError; use Twig\Loader\ExistsLoaderInterface; +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ExceptionController::class, ExceptionErrorController::class), E_USER_DEPRECATED); + /** * ExceptionController. * * @author Fabien Potencier + * + * @deprecated since Symfony 4.4, use the ExceptionErrorController instead. */ class ExceptionController { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionErrorController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionErrorController.php new file mode 100644 index 0000000000..c0833067cf --- /dev/null +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionErrorController.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Controller; + +use Symfony\Component\ErrorRenderer\ErrorRenderer\HtmlErrorRenderer; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Profiler\Profiler; + +/** + * Renders the exception panel. + * + * @author Yonel Ceruto + */ +class ExceptionErrorController +{ + private $htmlErrorRenderer; + private $profiler; + + public function __construct(HtmlErrorRenderer $htmlErrorRenderer, ?Profiler $profiler) + { + $this->htmlErrorRenderer = $htmlErrorRenderer; + $this->profiler = $profiler; + } + + /** + * Renders the exception panel stacktrace for the given token. + */ + public function body(string $token): Response + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $exception = $this->profiler->loadProfile($token) + ->getCollector('exception') + ->getException() + ; + + return new Response($this->htmlErrorRenderer->getBody($exception)); + } + + /** + * Renders the exception panel stylesheet. + */ + public function stylesheet(): Response + { + return new Response($this->htmlErrorRenderer->getStylesheet()); + } +} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml index dcacc51032..962e1418bd 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml @@ -27,7 +27,13 @@ %kernel.debug% - + + The "%service_id%" service is deprecated since Symfony 4.4, use the "web_profiler.controller.exception_error" service instead. + + + + + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml index 0bc9a9ec4f..0eb4ea72ff 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml @@ -37,11 +37,11 @@ - web_profiler.controller.exception::showAction + web_profiler.controller.exception_error::body - web_profiler.controller.exception::cssAction + web_profiler.controller.exception_error::stylesheet diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig index 78752853b9..ea028e026f 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig @@ -1,5 +1,3 @@ -{{ include('@Twig/exception.css.twig') }} - .container { max-width: auto; margin: 0; diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig index 94dfbb6aca..261d5cc2b1 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig @@ -4,6 +4,7 @@ {% if collector.hasexception %} {% endif %} {{ parent() }} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig index 779f1259ed..6f2af8dd45 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig @@ -119,8 +119,8 @@ exception {% endif %} - {{ include('@Twig/images/icon-minus-square.svg') }} - {{ include('@Twig/images/icon-plus-square.svg') }} + {{ include('@WebProfiler/images/icon-minus-square.svg') }} + {{ include('@WebProfiler/images/icon-plus-square.svg') }} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig index 580b3b5b0e..0b13f57509 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig @@ -4,7 +4,7 @@ - Symfony Profiler + {% block title %}Symfony Profiler{% endblock %} {% block head %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_redirect.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_redirect.html.twig index 35b6e90eb5..18d43b2253 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_redirect.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_redirect.html.twig @@ -1,4 +1,4 @@ -{% extends '@Twig/layout.html.twig' %} +{% extends '@WebProfiler/Profiler/base.html.twig' %} {% block title 'Redirection Intercepted' %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/images/icon-minus-square.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/images/icon-minus-square.svg new file mode 100644 index 0000000000..471c2741c7 --- /dev/null +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/images/icon-minus-square.svg @@ -0,0 +1 @@ + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/images/icon-plus-square.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/images/icon-plus-square.svg new file mode 100644 index 0000000000..2f5c3b3583 --- /dev/null +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/images/icon-plus-square.svg @@ -0,0 +1 @@ + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php index cfbee00bd0..c2ac0a75ef 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php @@ -54,7 +54,7 @@ class WebProfilerExtensionTest extends TestCase $this->kernel = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\KernelInterface')->getMock(); $this->container = new ContainerBuilder(); - $this->container->register('error_renderer.renderer.html', HtmlErrorRenderer::class); + $this->container->register('error_renderer.renderer.html', HtmlErrorRenderer::class)->setPublic(true); $this->container->register('event_dispatcher', EventDispatcher::class)->setPublic(true); $this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface'))->setPublic(true); $this->container->register('twig', 'Twig\Environment')->setPublic(true); @@ -92,10 +92,11 @@ class WebProfilerExtensionTest extends TestCase $extension = new WebProfilerExtension(); $extension->load([[]], $this->container); + $this->container->removeDefinition('web_profiler.controller.exception'); $this->assertFalse($this->container->has('web_profiler.debug_toolbar')); - $this->assertSaneContainer($this->getCompiledContainer()); + self::assertSaneContainer($this->getCompiledContainer()); } /** @@ -105,10 +106,11 @@ class WebProfilerExtensionTest extends TestCase { $extension = new WebProfilerExtension(); $extension->load([['toolbar' => $toolbarEnabled, 'intercept_redirects' => $interceptRedirects]], $this->container); + $this->container->removeDefinition('web_profiler.controller.exception'); $this->assertSame($listenerInjected, $this->container->has('web_profiler.debug_toolbar')); - $this->assertSaneContainer($this->getCompiledContainer(), '', ['web_profiler.csp.handler']); + self::assertSaneContainer($this->getCompiledContainer(), '', ['web_profiler.csp.handler']); if ($listenerInjected) { $this->assertSame($listenerEnabled, $this->container->get('web_profiler.debug_toolbar')->isEnabled()); diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index 190fc6d117..d0cf3153cd 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -18,6 +18,7 @@ "require": { "php": "^7.1.3", "symfony/config": "^4.2|^5.0", + "symfony/error-renderer": "^4.4|^5.0", "symfony/http-kernel": "^4.4", "symfony/routing": "^3.4|^4.0|^5.0", "symfony/twig-bundle": "^4.2|^5.0", From 35b0d57692baa0f8c6e5c4d79dac61ecf815b62c Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Wed, 24 Jul 2019 08:18:47 +0200 Subject: [PATCH 12/18] [WebProfilerBundle] mark all classes as internal those classes are not meant as extension point --- src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md | 1 + .../Bundle/WebProfilerBundle/Controller/ExceptionController.php | 2 ++ .../Bundle/WebProfilerBundle/Controller/ProfilerController.php | 2 ++ .../Bundle/WebProfilerBundle/Controller/RouterController.php | 2 ++ .../Bundle/WebProfilerBundle/Profiler/TemplateManager.php | 2 ++ .../Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php | 2 ++ 6 files changed, 11 insertions(+) diff --git a/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md b/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md index 821a86b756..23b2d0986e 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * Added button to clear the ajax request tab * Deprecated the `ExceptionController::templateExists()` method * Deprecated the `TemplateManager::templateExists()` method + * Marked all classes of the WebProfilerBundle as internal 4.3.0 ----- diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php index f9298965bf..c6504ee322 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php @@ -24,6 +24,8 @@ use Twig\Loader\ExistsLoaderInterface; * ExceptionController. * * @author Fabien Potencier + * + * @internal since Symfony 4.4 */ class ExceptionController { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 410f202871..c8a560295d 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -24,6 +24,8 @@ use Twig\Environment; /** * @author Fabien Potencier + * + * @internal since Symfony 4.4 */ class ProfilerController { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php index f3f68fe5d8..cedcb9f9d4 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php @@ -26,6 +26,8 @@ use Twig\Environment; * RouterController. * * @author Fabien Potencier + * + * @internal since Symfony 4.4 */ class RouterController { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php index 77cf4073d3..5a33d01cd8 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php @@ -24,6 +24,8 @@ use Twig\Loader\SourceContextLoaderInterface; * * @author Fabien Potencier * @author Artur Wielogórski + * + * @internal since Symfony 4.4 */ class TemplateManager { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php index 4494783633..80c597e040 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php @@ -22,6 +22,8 @@ use Twig\TwigFunction; * Twig extension for the profiler. * * @author Fabien Potencier + * + * @internal since Symfony 4.4 */ class WebProfilerExtension extends ProfilerExtension { From e4fc5c07abadff3ae26f59011dfa9e2262132a1c Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 23 Jul 2019 15:54:33 +0200 Subject: [PATCH 13/18] [Messenger][Profiler] Remove cutting caster to dump full objects --- .../DataCollector/MessengerDataCollector.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Symfony/Component/Messenger/DataCollector/MessengerDataCollector.php b/src/Symfony/Component/Messenger/DataCollector/MessengerDataCollector.php index 8c7f3cbadb..ea32e06cde 100644 --- a/src/Symfony/Component/Messenger/DataCollector/MessengerDataCollector.php +++ b/src/Symfony/Component/Messenger/DataCollector/MessengerDataCollector.php @@ -79,6 +79,19 @@ class MessengerDataCollector extends DataCollector implements LateDataCollectorI } } + /** + * {@inheritdoc} + */ + protected function getCasters() + { + $casters = parent::getCasters(); + + // Unset the default caster truncating collectors data. + unset($casters['*']); + + return $casters; + } + private function collectMessage(string $busName, array $tracedMessage) { $message = $tracedMessage['message']; From 015fca74050da493cfb7a95e48ddc42d7b177e2a Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Wed, 24 Jul 2019 09:56:23 +0200 Subject: [PATCH 14/18] [Messenger] Flatten collection of stamps collected by the traceable middleware --- .../Component/Messenger/Tests/TraceableMessageBusTest.php | 2 +- src/Symfony/Component/Messenger/TraceableMessageBus.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php b/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php index 23e47d28d0..06bf641e9e 100644 --- a/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php +++ b/src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php @@ -56,7 +56,7 @@ class TraceableMessageBusTest extends TestCase $this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages()); $this->assertArraySubset([ 'message' => $message, - 'stamps' => [[$stamp]], + 'stamps' => [$stamp], 'caller' => [ 'name' => 'TraceableMessageBusTest.php', 'file' => __FILE__, diff --git a/src/Symfony/Component/Messenger/TraceableMessageBus.php b/src/Symfony/Component/Messenger/TraceableMessageBus.php index 1d910d4540..77eefddb5e 100644 --- a/src/Symfony/Component/Messenger/TraceableMessageBus.php +++ b/src/Symfony/Component/Messenger/TraceableMessageBus.php @@ -33,7 +33,7 @@ class TraceableMessageBus implements MessageBusInterface { $envelope = $message instanceof Envelope ? $message : new Envelope($message); $context = [ - 'stamps' => array_values($envelope->all()), + 'stamps' => array_merge([], ...array_values($envelope->all())), 'message' => $envelope->getMessage(), 'caller' => $this->getCaller(), 'callTime' => microtime(true), From c5c67d913db90ced8954098c9e9d312695dbe284 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 24 Jul 2019 09:56:35 +0200 Subject: [PATCH 15/18] [HttpClient] fix canceling responses in a streaming loop --- .../HttpClient/Response/MockResponse.php | 18 +++++++++++++++--- .../HttpClient/Response/ResponseTrait.php | 2 +- .../HttpClient/Test/HttpClientTestCase.php | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpClient/Response/MockResponse.php b/src/Symfony/Component/HttpClient/Response/MockResponse.php index 47fc084d37..90bb0df339 100644 --- a/src/Symfony/Component/HttpClient/Response/MockResponse.php +++ b/src/Symfony/Component/HttpClient/Response/MockResponse.php @@ -78,6 +78,15 @@ class MockResponse implements ResponseInterface return null !== $type ? $this->info[$type] ?? null : $this->info; } + /** + * {@inheritdoc} + */ + public function cancel(): void + { + $this->info['error'] = 'Response has been canceled.'; + $this->body = null; + } + /** * {@inheritdoc} */ @@ -150,8 +159,11 @@ class MockResponse implements ResponseInterface foreach ($responses as $response) { $id = $response->id; - if (!$response->body) { - // Last chunk + if (null === $response->body) { + // Canceled response + $response->body = []; + } elseif ([] === $response->body) { + // Error chunk $multi->handlesActivity[$id][] = null; $multi->handlesActivity[$id][] = null !== $response->info['error'] ? new TransportException($response->info['error']) : null; } elseif (null === $chunk = array_shift($response->body)) { @@ -242,7 +254,7 @@ class MockResponse implements ResponseInterface // populate info related to headers $info = $mock->getInfo() ?: []; - $response->info['http_code'] = ($info['http_code'] ?? 0) ?: $mock->getStatusCode(false) ?: 200; + $response->info['http_code'] = ($info['http_code'] ?? 0) ?: $mock->getStatusCode() ?: 200; $response->addResponseHeaders($info['response_headers'] ?? [], $response->info, $response->headers); $dlSize = isset($response->headers['content-encoding']) ? 0 : (int) ($response->headers['content-length'][0] ?? 0); diff --git a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php index cd44443992..bf7ba3a746 100644 --- a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php +++ b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php @@ -327,7 +327,7 @@ trait ResponseTrait unset($multi->handlesActivity[$j]); - if ($chunk instanceof FirstChunk && null === $response->initializer) { + if ($chunk instanceof FirstChunk && null === $response->initializer && null === $response->info['error']) { // Ensure the HTTP status code is always checked $response->getHeaders(true); } elseif ($chunk instanceof ErrorChunk && !$chunk->didThrow()) { diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php index c0acd55cea..075f73a2c1 100644 --- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php +++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php @@ -505,6 +505,21 @@ abstract class HttpClientTestCase extends TestCase $response->getHeaders(); } + public function testCancelInStream() + { + $client = $this->getHttpClient(__FUNCTION__); + $response = $client->request('GET', 'http://localhost:8057/404'); + + foreach ($client->stream($response) as $chunk) { + $response->cancel(); + } + + $this->expectException(TransportExceptionInterface::class); + + foreach ($client->stream($response) as $chunk) { + } + } + public function testOnProgressCancel() { $client = $this->getHttpClient(__FUNCTION__); From 07590aeb9553538c7d14cfea2b2d089d5f30d130 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 4 Jul 2019 22:22:47 +0200 Subject: [PATCH 16/18] fix inline handling when dumping tagged values --- src/Symfony/Component/Yaml/Dumper.php | 17 +++- .../Component/Yaml/Tests/DumperTest.php | 89 +++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index d2a2ebcf88..0012df4a35 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Yaml; +use Symfony\Component\Yaml\Tag\TaggedValue; + /** * Dumper dumps PHP variables to YAML strings. * @@ -91,7 +93,7 @@ class Dumper $dumpObjectAsInlineMap = empty((array) $input); } - if ($inline <= 0 || (!\is_array($input) && $dumpObjectAsInlineMap) || empty($input)) { + if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || empty($input)) { $output .= $prefix.Inline::dump($input, $flags); } else { $dumpAsMap = Inline::isHash($input); @@ -110,6 +112,19 @@ class Dumper continue; } + if ($value instanceof TaggedValue) { + $output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag()); + + if ($inline - 1 <= 0) { + $output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; + } else { + $output .= "\n"; + $output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags); + } + + continue; + } + $dumpObjectAsInlineMap = true; if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \ArrayObject || $value instanceof \stdClass)) { diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index e7a763ab96..1a9cac6f73 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Yaml\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Yaml\Dumper; use Symfony\Component\Yaml\Parser; +use Symfony\Component\Yaml\Tag\TaggedValue; use Symfony\Component\Yaml\Yaml; class DumperTest extends TestCase @@ -434,6 +435,94 @@ outer2: inner2: c inner3: { deep1: d, deep2: e } +YAML; + $this->assertSame($expected, $yaml); + } + + public function testDumpingTaggedValueSequenceRespectsInlineLevel() + { + $data = [ + new TaggedValue('user', [ + 'username' => 'jane', + ]), + new TaggedValue('user', [ + 'username' => 'john', + ]), + ]; + + $yaml = $this->dumper->dump($data, 2); + + $expected = <<assertSame($expected, $yaml); + } + + public function testDumpingTaggedValueSequenceWithInlinedTagValues() + { + $data = [ + new TaggedValue('user', [ + 'username' => 'jane', + ]), + new TaggedValue('user', [ + 'username' => 'john', + ]), + ]; + + $yaml = $this->dumper->dump($data, 1); + + $expected = <<assertSame($expected, $yaml); + } + + public function testDumpingTaggedValueMapRespectsInlineLevel() + { + $data = [ + 'user1' => new TaggedValue('user', [ + 'username' => 'jane', + ]), + 'user2' => new TaggedValue('user', [ + 'username' => 'john', + ]), + ]; + + $yaml = $this->dumper->dump($data, 2); + + $expected = <<assertSame($expected, $yaml); + } + + public function testDumpingTaggedValueMapWithInlinedTagValues() + { + $data = [ + 'user1' => new TaggedValue('user', [ + 'username' => 'jane', + ]), + 'user2' => new TaggedValue('user', [ + 'username' => 'john', + ]), + ]; + + $yaml = $this->dumper->dump($data, 1); + + $expected = <<assertSame($expected, $yaml); } From df7afa00ee520199352e4b6a5f57918b343975c4 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 24 Jul 2019 15:33:23 +0200 Subject: [PATCH 17/18] [Security/Core] align defaults for sodium with PHP 7.4 --- .../Component/Security/Core/Encoder/NativePasswordEncoder.php | 2 +- .../Component/Security/Core/Encoder/SodiumPasswordEncoder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php index 94d9f5ca51..1e09992afe 100644 --- a/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php @@ -30,7 +30,7 @@ final class NativePasswordEncoder implements PasswordEncoderInterface, SelfSalti public function __construct(int $opsLimit = null, int $memLimit = null, int $cost = null) { $cost = $cost ?? 13; - $opsLimit = $opsLimit ?? max(6, \defined('SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE') ? \SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE : 6); + $opsLimit = $opsLimit ?? max(4, \defined('SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE : 4); $memLimit = $memLimit ?? max(64 * 1024 * 1024, \defined('SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE : 64 * 1024 * 1024); if (3 > $opsLimit) { diff --git a/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php index 4f3f048bbf..934a3fdfca 100644 --- a/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php @@ -34,7 +34,7 @@ final class SodiumPasswordEncoder implements PasswordEncoderInterface, SelfSalti throw new LogicException('Libsodium is not available. You should either install the sodium extension, upgrade to PHP 7.2+ or use a different encoder.'); } - $this->opsLimit = $opsLimit ?? max(6, \defined('SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE') ? \SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE : 6); + $this->opsLimit = $opsLimit ?? max(4, \defined('SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE : 4); $this->memLimit = $memLimit ?? max(64 * 1024 * 1024, \defined('SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE') ? \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE : 64 * 1024 * 2014); if (3 > $this->opsLimit) { From 5a7b737ea387cf7e7f792e326d4166a14c74a540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20LESCALLIER?= Date: Wed, 17 Jul 2019 20:50:10 +0200 Subject: [PATCH 18/18] [Form][Validator] Generate accept attribute with file constraint and mime types option --- src/Symfony/Component/Form/CHANGELOG.md | 1 + .../Validator/ValidatorTypeGuesser.php | 7 +++- .../Validator/ValidatorTypeGuesserTest.php | 38 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 527f84b44a..c820d88585 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG ----- * deprecated using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` + * The type guesser guesses the HTML accept attribute when a mime type is configured in the File or Image constraint. 4.3.0 ----- diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php index 22cc7726d4..6dd15d7e69 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -122,7 +122,12 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface case 'Symfony\Component\Validator\Constraints\File': case 'Symfony\Component\Validator\Constraints\Image': - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\FileType', [], Guess::HIGH_CONFIDENCE); + $options = []; + if ($constraint->mimeTypes) { + $options = ['attr' => ['accept' => implode(',', (array) $constraint->mimeTypes)]]; + } + + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\FileType', $options, Guess::HIGH_CONFIDENCE); case 'Symfony\Component\Validator\Constraints\Language': return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\LanguageType', [], Guess::HIGH_CONFIDENCE); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php index fd11342bea..ef3e711258 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser; use Symfony\Component\Form\Guess\Guess; use Symfony\Component\Form\Guess\ValueGuess; use Symfony\Component\Validator\Constraints\Email; +use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\Constraints\IsTrue; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; @@ -111,6 +112,43 @@ class ValidatorTypeGuesserTest extends TestCase $this->assertNull($result); } + public function testGuessMimeTypesForConstraintWithMimeTypesValue() + { + $mineTypes = ['image/png', 'image/jpeg']; + $constraint = new File(['mimeTypes' => $mineTypes]); + $typeGuess = $this->guesser->guessTypeForConstraint($constraint); + $this->assertInstanceOf('Symfony\Component\Form\Guess\TypeGuess', $typeGuess); + $this->assertArrayHasKey('attr', $typeGuess->getOptions()); + $this->assertArrayHasKey('accept', $typeGuess->getOptions()['attr']); + $this->assertEquals(implode(',', $mineTypes), $typeGuess->getOptions()['attr']['accept']); + } + + public function testGuessMimeTypesForConstraintWithoutMimeTypesValue() + { + $constraint = new File(); + $typeGuess = $this->guesser->guessTypeForConstraint($constraint); + $this->assertInstanceOf('Symfony\Component\Form\Guess\TypeGuess', $typeGuess); + $this->assertArrayNotHasKey('attr', $typeGuess->getOptions()); + } + + public function testGuessMimeTypesForConstraintWithMimeTypesStringValue() + { + $constraint = new File(['mimeTypes' => 'image/*']); + $typeGuess = $this->guesser->guessTypeForConstraint($constraint); + $this->assertInstanceOf('Symfony\Component\Form\Guess\TypeGuess', $typeGuess); + $this->assertArrayHasKey('attr', $typeGuess->getOptions()); + $this->assertArrayHasKey('accept', $typeGuess->getOptions()['attr']); + $this->assertEquals('image/*', $typeGuess->getOptions()['attr']['accept']); + } + + public function testGuessMimeTypesForConstraintWithMimeTypesEmptyStringValue() + { + $constraint = new File(['mimeTypes' => '']); + $typeGuess = $this->guesser->guessTypeForConstraint($constraint); + $this->assertInstanceOf('Symfony\Component\Form\Guess\TypeGuess', $typeGuess); + $this->assertArrayNotHasKey('attr', $typeGuess->getOptions()); + } + public function maxLengthTypeProvider() { return [