From ffa5db74c18293bf25767c2aa80515f49045f1e7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 10 Jan 2019 21:23:54 +0100 Subject: [PATCH 01/21] fix comparisons with null values at property paths --- .../GreaterThanOrEqualValidator.php | 2 +- .../Constraints/GreaterThanValidator.php | 2 +- .../Constraints/LessThanOrEqualValidator.php | 2 +- .../Constraints/LessThanValidator.php | 2 +- .../AbstractComparisonValidatorTestCase.php | 27 +++++++++++++++++++ .../Constraints/EqualToValidatorTest.php | 7 +++++ .../GreaterThanOrEqualValidatorTest.php | 7 +++++ .../Constraints/GreaterThanValidatorTest.php | 7 +++++ .../Constraints/IdenticalToValidatorTest.php | 7 +++++ .../LessThanOrEqualValidatorTest.php | 7 +++++ .../Constraints/LessThanValidatorTest.php | 7 +++++ .../Constraints/NotEqualToValidatorTest.php | 7 +++++ .../NotIdenticalToValidatorTest.php | 7 +++++ 13 files changed, 87 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqualValidator.php b/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqualValidator.php index e196e688f3..290408ac0c 100644 --- a/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqualValidator.php +++ b/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqualValidator.php @@ -24,7 +24,7 @@ class GreaterThanOrEqualValidator extends AbstractComparisonValidator */ protected function compareValues($value1, $value2) { - return $value1 >= $value2; + return null === $value2 || $value1 >= $value2; } /** diff --git a/src/Symfony/Component/Validator/Constraints/GreaterThanValidator.php b/src/Symfony/Component/Validator/Constraints/GreaterThanValidator.php index 9029e8fc46..062503ab3f 100644 --- a/src/Symfony/Component/Validator/Constraints/GreaterThanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/GreaterThanValidator.php @@ -24,7 +24,7 @@ class GreaterThanValidator extends AbstractComparisonValidator */ protected function compareValues($value1, $value2) { - return $value1 > $value2; + return null === $value2 || $value1 > $value2; } /** diff --git a/src/Symfony/Component/Validator/Constraints/LessThanOrEqualValidator.php b/src/Symfony/Component/Validator/Constraints/LessThanOrEqualValidator.php index 54281eef5a..f7f4c8be5f 100644 --- a/src/Symfony/Component/Validator/Constraints/LessThanOrEqualValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LessThanOrEqualValidator.php @@ -24,7 +24,7 @@ class LessThanOrEqualValidator extends AbstractComparisonValidator */ protected function compareValues($value1, $value2) { - return $value1 <= $value2; + return null === $value2 || $value1 <= $value2; } /** diff --git a/src/Symfony/Component/Validator/Constraints/LessThanValidator.php b/src/Symfony/Component/Validator/Constraints/LessThanValidator.php index ef7535fc99..64e107547a 100644 --- a/src/Symfony/Component/Validator/Constraints/LessThanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LessThanValidator.php @@ -24,7 +24,7 @@ class LessThanValidator extends AbstractComparisonValidator */ protected function compareValues($value1, $value2) { - return $value1 < $value2; + return null === $value2 || $value1 < $value2; } /** diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php index b02e57cfa2..8baf45e16b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -237,6 +237,31 @@ abstract class AbstractComparisonValidatorTestCase extends ConstraintValidatorTe ]; } + /** + * @dataProvider provideComparisonsToNullValueAtPropertyPath + */ + public function testCompareWithNullValueAtPropertyAt($dirtyValue, $dirtyValueAsString, $isValid) + { + $constraint = $this->createConstraint(['propertyPath' => 'value']); + $constraint->message = 'Constraint Message'; + + $object = new ComparisonTest_Class(null); + $this->setObject($object); + + $this->validator->validate($dirtyValue, $constraint); + + if ($isValid) { + $this->assertNoViolation(); + } else { + $this->buildViolation('Constraint Message') + ->setParameter('{{ value }}', $dirtyValueAsString) + ->setParameter('{{ compared_value }}', 'null') + ->setParameter('{{ compared_value_type }}', 'NULL') + ->setCode($this->getErrorCode()) + ->assertRaised(); + } + } + /** * @return array */ @@ -258,6 +283,8 @@ abstract class AbstractComparisonValidatorTestCase extends ConstraintValidatorTe */ abstract public function provideInvalidComparisons(); + abstract public function provideComparisonsToNullValueAtPropertyPath(); + /** * @param array|null $options Options for the constraint * diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php index c1eb2f93ad..880dbd7795 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php @@ -75,4 +75,11 @@ class EqualToValidatorTest extends AbstractComparisonValidatorTestCase [new ComparisonTest_Class(4), '4', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ]; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + return [ + [5, '5', false], + ]; + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php index d8d8eab8bd..043c02e7a7 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php @@ -78,4 +78,11 @@ class GreaterThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCas ['b', '"b"', 'c', '"c"', 'string'], ]; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + return [ + [5, '5', true], + ]; + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php index e678496c41..119c162edb 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php @@ -80,4 +80,11 @@ class GreaterThanValidatorTest extends AbstractComparisonValidatorTestCase ['22', '"22"', '22', '"22"', 'string'], ]; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + return [ + [5, '5', true], + ]; + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php index c96ac16a91..1d3662f49a 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php @@ -93,4 +93,11 @@ class IdenticalToValidatorTest extends AbstractComparisonValidatorTestCase [new ComparisonTest_Class(4), '4', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ]; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + return [ + [5, '5', false], + ]; + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php index b77deff616..9311706e7d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php @@ -81,4 +81,11 @@ class LessThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCase ['c', '"c"', 'b', '"b"', 'string'], ]; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + return [ + [5, '5', true], + ]; + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php index 7d209ed5d4..c40389440d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php @@ -79,4 +79,11 @@ class LessThanValidatorTest extends AbstractComparisonValidatorTestCase ['333', '"333"', '22', '"22"', 'string'], ]; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + return [ + [5, '5', true], + ]; + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php index 810f7a175f..8577159583 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php @@ -75,4 +75,11 @@ class NotEqualToValidatorTest extends AbstractComparisonValidatorTestCase [new ComparisonTest_Class(5), '5', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ]; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + return [ + [5, '5', true], + ]; + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php index 0cb9ec5431..f14c5bd0dc 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php @@ -93,4 +93,11 @@ class NotIdenticalToValidatorTest extends AbstractComparisonValidatorTestCase return $comparisons; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + return [ + [5, '5', true], + ]; + } } From 8eb29a7b73c2d4e410a7fcdaef199326d1308fbe Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Mon, 9 Dec 2019 10:10:02 +0100 Subject: [PATCH 02/21] [FrameworkBundle] Add info & example to auto_mapping config --- .../FrameworkBundle/DependencyInjection/Configuration.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 0f575b90d6..6833610d0c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -855,6 +855,11 @@ class Configuration implements ConfigurationInterface ->end() ->end() ->arrayNode('auto_mapping') + ->info('A collection of namespaces for which auto-mapping will be enabled.') + ->example([ + 'App\\Entity\\' => [], + 'App\\WithSpecificLoaders\\' => ['validator.property_info_loader'], + ]) ->useAttributeAsKey('namespace') ->normalizeKeys(false) ->beforeNormalization() From 09bb1e49d87aaf5f57f1b99da56286dfc0ca1ab7 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Mon, 9 Dec 2019 20:19:22 +0100 Subject: [PATCH 03/21] [DoctrineBridge] Fixed submitting invalid ids when using queries with limit --- .../Form/ChoiceList/ORMQueryBuilderLoader.php | 15 +++++++++++ .../Tests/Form/Type/EntityTypeTest.php | 25 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php index 96f5e2f5f1..875b08dae9 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php @@ -55,6 +55,21 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface */ public function getEntitiesByIds($identifier, array $values) { + if (null !== $this->queryBuilder->getMaxResults() || null !== $this->queryBuilder->getFirstResult()) { + // an offset or a limit would apply on results including the where clause with submitted id values + // that could make invalid choices valid + $choices = []; + $metadata = $this->queryBuilder->getEntityManager()->getClassMetadata(current($this->queryBuilder->getRootEntities())); + + foreach ($this->getEntities() as $entity) { + if (\in_array(current($metadata->getIdentifierValues($entity)), $values, true)) { + $choices[] = $entity; + } + } + + return $choices; + } + $qb = clone $this->queryBuilder; $alias = current($qb->getRootAliases()); $parameter = 'ORMQueryBuilderLoader_getEntitiesByIds_'.$identifier; diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 147be98b90..5a2c6e561b 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -955,6 +955,31 @@ class EntityTypeTest extends BaseTypeTest $this->assertNull($field->getData()); } + public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleIdentifierWithLimit() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist([$entity1, $entity2, $entity3]); + + $repository = $this->em->getRepository(self::SINGLE_IDENT_CLASS); + + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => $repository->createQueryBuilder('e') + ->where('e.id IN (1, 2, 3)') + ->setMaxResults(1), + 'choice_label' => 'name', + ]); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleAssocIdentifier() { $innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo'); From 3b1b994cb3be504b0abcb42f07e852cd20d63a42 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Mon, 9 Dec 2019 22:46:40 +0100 Subject: [PATCH 04/21] [Validator][ConstraintValidator] Safe fail on invalid timezones Co-authored-by: Scott Dawson --- .../Component/Validator/ConstraintValidator.php | 14 +++++--------- .../Validator/Tests/ConstraintValidatorTest.php | 12 ++++++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Validator/ConstraintValidator.php b/src/Symfony/Component/Validator/ConstraintValidator.php index 8469926561..458351fe2e 100644 --- a/src/Symfony/Component/Validator/ConstraintValidator.php +++ b/src/Symfony/Component/Validator/ConstraintValidator.php @@ -87,16 +87,12 @@ abstract class ConstraintValidator implements ConstraintValidatorInterface { if (($format & self::PRETTY_DATE) && $value instanceof \DateTimeInterface) { if (class_exists('IntlDateFormatter')) { - $locale = \Locale::getDefault(); - $formatter = new \IntlDateFormatter($locale, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, $value->getTimezone()); + $formatter = new \IntlDateFormatter(\Locale::getDefault(), \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, 'UTC'); - // neither the native nor the stub IntlDateFormatter support - // DateTimeImmutable as of yet - if (!$value instanceof \DateTime) { - $value = new \DateTime($value->format('Y-m-d H:i:s.u e')); - } - - return $formatter->format($value); + return $formatter->format(new \DateTime( + $value->format('Y-m-d H:i:s.u'), + new \DateTimeZone('UTC') + )); } return $value->format('Y-m-d H:i:s'); diff --git a/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php index 96af6f13eb..6ca3eab41f 100644 --- a/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php @@ -27,6 +27,9 @@ final class ConstraintValidatorTest extends TestCase public function formatValueProvider() { + $defaultTimezone = date_default_timezone_get(); + date_default_timezone_set('Europe/Moscow'); // GMT+3 + $data = [ ['true', true], ['false', false], @@ -36,10 +39,15 @@ final class ConstraintValidatorTest extends TestCase ['array', []], ['object', $toString = new TestToStringObject()], ['ccc', $toString, ConstraintValidator::OBJECT_TO_STRING], - ['object', $dateTime = (new \DateTimeImmutable('@0'))->setTimezone(new \DateTimeZone('UTC'))], - [class_exists(\IntlDateFormatter::class) ? 'Jan 1, 1970, 12:00 AM' : '1970-01-01 00:00:00', $dateTime, ConstraintValidator::PRETTY_DATE], + ['object', $dateTime = new \DateTimeImmutable('1971-02-02T08:00:00UTC')], + [class_exists(\IntlDateFormatter::class) ? 'Oct 4, 2019, 11:02 AM' : '2019-10-04 11:02:03', new \DateTimeImmutable('2019-10-04T11:02:03+09:00'), ConstraintValidator::PRETTY_DATE], + [class_exists(\IntlDateFormatter::class) ? 'Feb 2, 1971, 8:00 AM' : '1971-02-02 08:00:00', $dateTime, ConstraintValidator::PRETTY_DATE], + [class_exists(\IntlDateFormatter::class) ? 'Jan 1, 1970, 6:00 AM' : '1970-01-01 06:00:00', new \DateTimeImmutable('1970-01-01T06:00:00Z'), ConstraintValidator::PRETTY_DATE], + [class_exists(\IntlDateFormatter::class) ? 'Jan 1, 1970, 3:00 PM' : '1970-01-01 15:00:00', (new \DateTimeImmutable('1970-01-01T23:00:00'))->setTimezone(new \DateTimeZone('America/New_York')), ConstraintValidator::PRETTY_DATE], ]; + date_default_timezone_set($defaultTimezone); + return $data; } } From 130df8ca8c38f9a1a16a856a13e8daecd71dbe98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 10 Dec 2019 13:06:58 +0100 Subject: [PATCH 05/21] Fix invalid Windows path normalization --- .../Bundle/FrameworkBundle/Templating/TemplateNameParser.php | 2 +- .../Tests/Templating/TemplateNameParserTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php index 22dbe3c972..ccfd3b5b3d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php @@ -44,7 +44,7 @@ class TemplateNameParser extends BaseTemplateNameParser } // normalize name - $name = str_replace(':/', ':', preg_replace('#/{2,}#', '/', str_replace('\\', '/', $name))); + $name = preg_replace('#/{2,}#', '/', str_replace('\\', '/', $name)); if (false !== strpos($name, '..')) { throw new \RuntimeException(sprintf('Template name "%s" contains invalid characters.', $name)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php index b9cb308592..0b1d6f376f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php @@ -98,8 +98,8 @@ class TemplateNameParserTest extends TestCase { return [ ['/path/to/section/index.html.php', '/path/to/section/index.html.php', '/path/to/section/index.html.php', new BaseTemplateReference('/path/to/section/index.html.php', 'php')], - ['C:\\path\\to\\section\\name.html.php', 'C:path/to/section/name.html.php', 'C:path/to/section/name.html.php', new BaseTemplateReference('C:path/to/section/name.html.php', 'php')], - ['C:\\path\\to\\section\\name:foo.html.php', 'C:path/to/section/name:foo.html.php', 'C:path/to/section/name:foo.html.php', new BaseTemplateReference('C:path/to/section/name:foo.html.php', 'php')], + ['C:\\path\\to\\section\\name.html.php', 'C:/path/to/section/name.html.php', 'C:/path/to/section/name.html.php', new BaseTemplateReference('C:/path/to/section/name.html.php', 'php')], + ['C:\\path\\to\\section\\name:foo.html.php', 'C:/path/to/section/name:foo.html.php', 'C:/path/to/section/name:foo.html.php', new BaseTemplateReference('C:/path/to/section/name:foo.html.php', 'php')], ['\\path\\to\\section\\name.html.php', '/path/to/section/name.html.php', '/path/to/section/name.html.php', new BaseTemplateReference('/path/to/section/name.html.php', 'php')], ['/path/to/section/name.php', '/path/to/section/name.php', '/path/to/section/name.php', new BaseTemplateReference('/path/to/section/name.php', 'php')], ]; From 201af4f31d2ca062dcb8f032ae4b7f038e2490f1 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 13 Dec 2019 09:10:41 +0100 Subject: [PATCH 06/21] fix constructor argument type declaration --- src/Symfony/Component/Lock/Factory.php | 2 +- .../Component/Lock/Tests/LockFactoryTest.php | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Lock/Factory.php b/src/Symfony/Component/Lock/Factory.php index 2bc7d9304b..40e80f39c9 100644 --- a/src/Symfony/Component/Lock/Factory.php +++ b/src/Symfony/Component/Lock/Factory.php @@ -28,7 +28,7 @@ class Factory implements LoggerAwareInterface private $store; - public function __construct(StoreInterface $store) + public function __construct(PersistingStoreInterface $store) { $this->store = $store; diff --git a/src/Symfony/Component/Lock/Tests/LockFactoryTest.php b/src/Symfony/Component/Lock/Tests/LockFactoryTest.php index 0ec4fd3976..a867b8cfae 100644 --- a/src/Symfony/Component/Lock/Tests/LockFactoryTest.php +++ b/src/Symfony/Component/Lock/Tests/LockFactoryTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\LockInterface; +use Symfony\Component\Lock\PersistingStoreInterface; use Symfony\Component\Lock\StoreInterface; /** @@ -23,6 +24,21 @@ use Symfony\Component\Lock\StoreInterface; class LockFactoryTest extends TestCase { public function testCreateLock() + { + $store = $this->getMockBuilder(PersistingStoreInterface::class)->getMock(); + $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $factory = new LockFactory($store); + $factory->setLogger($logger); + + $lock = $factory->createLock('foo'); + + $this->assertInstanceOf(LockInterface::class, $lock); + } + + /** + * @group legacy + */ + public function testCreateLockWithLegacyStoreImplementation() { $store = $this->getMockBuilder(StoreInterface::class)->getMock(); $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); From 81ba07aa26cc876f5a5e1f835988c3bab7b1a233 Mon Sep 17 00:00:00 2001 From: Jan Christoph Beyer Date: Fri, 13 Dec 2019 12:27:55 +0100 Subject: [PATCH 07/21] fix redis multi host dsn not recognized --- .../Session/Storage/Handler/SessionHandlerFactory.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php index f4feeac092..1f017c8afd 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php @@ -50,13 +50,13 @@ class SessionHandlerFactory case 0 === strpos($connection, 'file://'): return new StrictSessionHandler(new NativeFileSessionHandler(substr($connection, 7))); - case 0 === strpos($connection, 'redis://'): - case 0 === strpos($connection, 'rediss://'): - case 0 === strpos($connection, 'memcached://'): + case 0 === strpos($connection, 'redis:'): + case 0 === strpos($connection, 'rediss:'): + case 0 === strpos($connection, 'memcached:'): if (!class_exists(AbstractAdapter::class)) { throw new InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require symfony/cache".', $connection)); } - $handlerClass = 0 === strpos($connection, 'memcached://') ? MemcachedSessionHandler::class : RedisSessionHandler::class; + $handlerClass = 0 === strpos($connection, 'memcached:') ? MemcachedSessionHandler::class : RedisSessionHandler::class; $connection = AbstractAdapter::createConnection($connection, ['lazy' => true]); return new $handlerClass($connection); From 41b56eac29aedf3a59f8d1d099921f687d515504 Mon Sep 17 00:00:00 2001 From: Stefan Kruppa Date: Fri, 13 Dec 2019 11:59:31 +0100 Subject: [PATCH 08/21] [DI] Improve performance of processDefinition --- .../Compiler/ResolveInstanceofConditionalsPass.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php index 69e3796df0..6268ed9ed0 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php @@ -64,9 +64,10 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface $definition->setInstanceofConditionals([]); $parent = $shared = null; $instanceofTags = []; + $reflectionClass = null; foreach ($conditionals as $interface => $instanceofDefs) { - if ($interface !== $class && (!$container->getReflectionClass($class, false))) { + if ($interface !== $class && !(null === $reflectionClass ? $reflectionClass = ($container->getReflectionClass($class, false) ?: false) : $reflectionClass)) { continue; } From 56f542c36bd7d3204139978c9e530895f1ac8167 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Tue, 10 Dec 2019 16:24:44 +0100 Subject: [PATCH 09/21] [FrameworkBundle][Secrets] Hook configured local dotenv file --- .../FrameworkBundle/DependencyInjection/Configuration.php | 2 +- .../DependencyInjection/FrameworkExtension.php | 4 +++- .../Bundle/FrameworkBundle/Resources/config/secrets.xml | 6 +++--- .../Tests/DependencyInjection/ConfigurationTest.php | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 827b306bcf..c4a3c6f534 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -129,7 +129,7 @@ class Configuration implements ConfigurationInterface ->canBeDisabled() ->children() ->scalarNode('vault_directory')->defaultValue('%kernel.project_dir%/config/secrets/%kernel.environment%')->cannotBeEmpty()->end() - ->scalarNode('local_dotenv_file')->defaultValue('%kernel.project_dir%/.env.local')->end() + ->scalarNode('local_dotenv_file')->defaultValue('%kernel.project_dir%/.env.%kernel.environment%.local')->end() ->scalarNode('decryption_env_var')->defaultValue('base64:default::SYMFONY_DECRYPTION_SECRET')->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 1832d962d2..6c282f1ed5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1478,7 +1478,9 @@ class FrameworkExtension extends Extension $container->getDefinition('secrets.vault')->replaceArgument(0, $config['vault_directory']); - if (!$config['local_dotenv_file']) { + if ($config['local_dotenv_file']) { + $container->getDefinition('secrets.local_vault')->replaceArgument(0, $config['local_dotenv_file']); + } else { $container->removeDefinition('secrets.local_vault'); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml index f70243dd84..65fd1073fd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml @@ -7,12 +7,12 @@ - %kernel.project_dir%/config/secrets/%kernel.environment% - %env(base64:default::SYMFONY_DECRYPTION_SECRET)% + + - %kernel.project_dir%/.env.local + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 5356ffe486..aff4e5ea41 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -519,7 +519,7 @@ class ConfigurationTest extends TestCase 'secrets' => [ 'enabled' => true, 'vault_directory' => '%kernel.project_dir%/config/secrets/%kernel.environment%', - 'local_dotenv_file' => '%kernel.project_dir%/.env.local', + 'local_dotenv_file' => '%kernel.project_dir%/.env.%kernel.environment%.local', 'decryption_env_var' => 'base64:default::SYMFONY_DECRYPTION_SECRET', ], ]; From 9d306f9046cb63f6a09b8b4a6c1bce8c97a0332c Mon Sep 17 00:00:00 2001 From: Diego Aguiar Date: Thu, 12 Dec 2019 11:50:33 -0700 Subject: [PATCH 10/21] [SECURITY] Revert "AbstractAuthenticationListener.php error instead info. Rebase of #28462" This reverts commit 867eb78cfea8d45334faddbbf1ad62b52fe5ed1a. --- .../Security/Http/Firewall/AbstractAuthenticationListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php index 8b70a24344..e400f4a7cd 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php @@ -184,7 +184,7 @@ abstract class AbstractAuthenticationListener extends AbstractListener implement private function onFailure(Request $request, AuthenticationException $failed): Response { if (null !== $this->logger) { - $this->logger->error('Authentication request failed.', ['exception' => $failed]); + $this->logger->info('Authentication request failed.', ['exception' => $failed]); } $token = $this->tokenStorage->getToken(); From a0f581ba9dba877df1ba8a24adc4db9fbb57d7c1 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Wed, 11 Dec 2019 14:12:38 +0100 Subject: [PATCH 11/21] [FrameworkBundle][DependencyInjection] Skip removed ids in the lint container command and its associated pass --- .../Command/ContainerLintCommand.php | 45 ++++++++++++++++--- .../Compiler/CheckTypeDeclarationsPass.php | 13 ++++-- .../CheckTypeDeclarationsPassTest.php | 12 +++++ 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php index 10b3cf1b0d..1f4ffe59af 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php @@ -14,13 +14,17 @@ namespace Symfony\Bundle\FrameworkBundle\Command; use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\FileLocator; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\HttpKernel\Kernel; final class ContainerLintCommand extends Command { @@ -47,13 +51,18 @@ final class ContainerLintCommand extends Command */ protected function execute(InputInterface $input, OutputInterface $output): int { - $container = $this->getContainerBuilder(); + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + + try { + $container = $this->getContainerBuilder(); + } catch (RuntimeException $e) { + $errorIo->error($e->getMessage()); + + return 2; + } - $container->setParameter('container.build_hash', 'lint_container'); $container->setParameter('container.build_time', time()); - $container->setParameter('container.build_id', 'lint_container'); - - $container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100); $container->compile(); @@ -67,22 +76,44 @@ final class ContainerLintCommand extends Command } $kernel = $this->getApplication()->getKernel(); + $kernelContainer = $kernel->getContainer(); + + if (!$kernel->isDebug() || !(new ConfigCache($kernelContainer->getParameter('debug.container.dump'), true))->isFresh()) { + if (!$kernel instanceof Kernel) { + throw new RuntimeException("This command does not support the console application's kernel."); + } - if (!$kernel->isDebug() || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) { $buildContainer = \Closure::bind(function (): ContainerBuilder { $this->initializeBundles(); return $this->buildContainer(); }, $kernel, \get_class($kernel)); $container = $buildContainer(); + + $skippedIds = []; } else { - (new XmlFileLoader($container = new ContainerBuilder($parameterBag = new EnvPlaceholderParameterBag()), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump')); + if (!$kernelContainer instanceof Container) { + throw new RuntimeException("This command does not support the console application kernel's container."); + } + + (new XmlFileLoader($container = new ContainerBuilder($parameterBag = new EnvPlaceholderParameterBag()), new FileLocator()))->load($kernelContainer->getParameter('debug.container.dump')); $refl = new \ReflectionProperty($parameterBag, 'resolved'); $refl->setAccessible(true); $refl->setValue($parameterBag, true); + + $passConfig = $container->getCompilerPassConfig(); + $passConfig->setRemovingPasses([]); + $passConfig->setAfterRemovingPasses([]); + + $skippedIds = $kernelContainer->getRemovedIds(); } + $container->setParameter('container.build_hash', 'lint_container'); + $container->setParameter('container.build_id', 'lint_container'); + + $container->addCompilerPass(new CheckTypeDeclarationsPass(true, $skippedIds), PassConfig::TYPE_AFTER_REMOVING, -100); + return $this->containerBuilder = $container; } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index 5a3dc72b43..0743cbbb5f 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -42,16 +42,19 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass private const SCALAR_TYPES = ['int', 'float', 'bool', 'string']; private $autoload; + private $skippedIds; private $expressionLanguage; /** - * @param bool $autoload Whether services who's class in not loaded should be checked or not. - * Defaults to false to save loading code during compilation. + * @param bool $autoload Whether services who's class in not loaded should be checked or not. + * Defaults to false to save loading code during compilation. + * @param array $skippedIds An array indexed by the service ids to skip */ - public function __construct(bool $autoload = false) + public function __construct(bool $autoload = false, array $skippedIds = []) { $this->autoload = $autoload; + $this->skippedIds = $skippedIds; } /** @@ -59,6 +62,10 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass */ protected function processValue($value, $isRoot = false) { + if (isset($this->skippedIds[$this->currentId])) { + return $value; + } + if (!$value instanceof Definition || $value->hasErrors()) { return parent::processValue($value, $isRoot); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php index 4729c43b93..13c34e156c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php @@ -669,4 +669,16 @@ class CheckTypeDeclarationsPassTest extends TestCase $this->addToAssertionCount(1); } + + public function testProcessSkipSkippedIds() + { + $container = new ContainerBuilder(); + $container + ->register('foobar', BarMethodCall::class) + ->addMethodCall('setArray', ['a string']); + + (new CheckTypeDeclarationsPass(true, ['foobar' => true]))->process($container); + + $this->addToAssertionCount(1); + } } From be2eb6fcc74b462ab1cd4d33999270d941367752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 12 Dec 2019 16:55:12 +0100 Subject: [PATCH 12/21] [Messenger][AMQP] Use delivery_mode=2 by default --- .../Transport/AmqpExt/ConnectionTest.php | 27 ++++++++++++++----- .../Transport/AmqpExt/Connection.php | 1 + 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/ConnectionTest.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/ConnectionTest.php index 614cbb82e7..489020a6dc 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/ConnectionTest.php @@ -227,7 +227,7 @@ class ConnectionTest extends TestCase ); $amqpExchange->expects($this->once())->method('declareExchange'); - $amqpExchange->expects($this->once())->method('publish')->with('body', null, AMQP_NOPARAM, ['headers' => []]); + $amqpExchange->expects($this->once())->method('publish')->with('body', null, AMQP_NOPARAM, ['headers' => [], 'delivery_mode' => 2]); $amqpQueue->expects($this->once())->method('declareQueue'); $amqpQueue->expects($this->once())->method('bind')->with(self::DEFAULT_EXCHANGE_NAME, null); @@ -250,7 +250,7 @@ class ConnectionTest extends TestCase $factory->method('createQueue')->will($this->onConsecutiveCalls($amqpQueue0, $amqpQueue1)); $amqpExchange->expects($this->once())->method('declareExchange'); - $amqpExchange->expects($this->once())->method('publish')->with('body', 'routing_key', AMQP_NOPARAM, ['headers' => []]); + $amqpExchange->expects($this->once())->method('publish')->with('body', 'routing_key', AMQP_NOPARAM, ['headers' => [], 'delivery_mode' => 2]); $amqpQueue0->expects($this->once())->method('declareQueue'); $amqpQueue0->expects($this->exactly(2))->method('bind')->withConsecutive( [self::DEFAULT_EXCHANGE_NAME, 'binding_key0'], @@ -373,7 +373,7 @@ class ConnectionTest extends TestCase $delayQueue->expects($this->once())->method('declareQueue'); $delayQueue->expects($this->once())->method('bind')->with('delays', 'delay_messages__5000'); - $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages__5000', AMQP_NOPARAM, ['headers' => ['x-some-headers' => 'foo']]); + $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages__5000', AMQP_NOPARAM, ['headers' => ['x-some-headers' => 'foo'], 'delivery_mode' => 2]); $connection = Connection::fromDsn('amqp://localhost', [], $factory); $connection->publish('{}', ['x-some-headers' => 'foo'], 5000); @@ -415,7 +415,7 @@ class ConnectionTest extends TestCase $delayQueue->expects($this->once())->method('declareQueue'); $delayQueue->expects($this->once())->method('bind')->with('delays', 'delay_messages__120000'); - $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages__120000', AMQP_NOPARAM, ['headers' => []]); + $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages__120000', AMQP_NOPARAM, ['headers' => [], 'delivery_mode' => 2]); $connection->publish('{}', [], 120000); } @@ -447,12 +447,27 @@ class ConnectionTest extends TestCase $amqpExchange = $this->createMock(\AMQPExchange::class) ); - $amqpExchange->expects($this->once())->method('publish')->with('body', null, AMQP_NOPARAM, ['headers' => ['Foo' => 'X', 'Bar' => 'Y']]); + $amqpExchange->expects($this->once())->method('publish')->with('body', null, AMQP_NOPARAM, ['headers' => ['Foo' => 'X', 'Bar' => 'Y'], 'delivery_mode' => 2]); $connection = Connection::fromDsn('amqp://localhost', [], $factory); $connection->publish('body', ['Foo' => 'X'], 0, new AmqpStamp(null, AMQP_NOPARAM, ['headers' => ['Bar' => 'Y']])); } + public function testAmqpStampDelireryModeIsUsed() + { + $factory = new TestAmqpFactory( + $this->createMock(\AMQPConnection::class), + $this->createMock(\AMQPChannel::class), + $this->createMock(\AMQPQueue::class), + $amqpExchange = $this->createMock(\AMQPExchange::class) + ); + + $amqpExchange->expects($this->once())->method('publish')->with('body', null, AMQP_NOPARAM, ['headers' => [], 'delivery_mode' => 1]); + + $connection = Connection::fromDsn('amqp://localhost', [], $factory); + $connection->publish('body', [], 0, new AmqpStamp(null, AMQP_NOPARAM, ['delivery_mode' => 1])); + } + public function testItCanPublishWithTheDefaultRoutingKey() { $factory = new TestAmqpFactory( @@ -519,7 +534,7 @@ class ConnectionTest extends TestCase $delayQueue->expects($this->once())->method('declareQueue'); $delayQueue->expects($this->once())->method('bind')->with('delays', 'delay_messages_routing_key_120000'); - $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages_routing_key_120000', AMQP_NOPARAM, ['headers' => []]); + $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages_routing_key_120000', AMQP_NOPARAM, ['headers' => [], 'delivery_mode' => 2]); $connection->publish('{}', [], 120000, new AmqpStamp('routing_key')); } diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php index 4124eb3604..51cc3877d7 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php @@ -231,6 +231,7 @@ class Connection { $attributes = $amqpStamp ? $amqpStamp->getAttributes() : []; $attributes['headers'] = array_merge($attributes['headers'] ?? [], $headers); + $attributes['delivery_mode'] = $attributes['delivery_mode'] ?? 2; $exchange->publish( $body, From 0904e57b852d5126356e192ea32f6223fda40fc6 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 13 Dec 2019 17:57:31 +0100 Subject: [PATCH 13/21] [SecurityBundle][FirewallMap] Remove unused property --- src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php index 3bb16b26c7..9e20f4e527 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php @@ -107,13 +107,11 @@ class _FirewallMap { private $container; private $map; - private $contexts; public function __construct(ContainerInterface $container, $map) { $this->container = $container; $this->map = $map; - $this->contexts = new \SplObjectStorage(); } public function getListeners(Request $request) From 20f9a5bbb0ae0d695682ce2a86f6cf1d98074c35 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Fri, 13 Dec 2019 20:25:45 +0100 Subject: [PATCH 14/21] Fixed syntax in comment --- .../Component/Messenger/Handler/MessageSubscriberInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php b/src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php index f1fe29c568..64ac1d5134 100644 --- a/src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php +++ b/src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php @@ -30,7 +30,7 @@ interface MessageSubscriberInterface extends MessageHandlerInterface * It can also change the priority per classes. * * yield FirstMessage::class => ['priority' => 0]; - * yield SecondMessage::class => ['priority => -10]; + * yield SecondMessage::class => ['priority' => -10]; * * It can also specify a method, a priority, a bus and/or a transport per message: * From cf1d7b2ed1c65c46eae8cc3ea69d25b291d14789 Mon Sep 17 00:00:00 2001 From: Guilliam Xavier Date: Fri, 13 Dec 2019 21:47:04 +0100 Subject: [PATCH 15/21] Fix regex lookahead syntax in ApplicationTest --- src/Symfony/Component/Console/Tests/ApplicationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 1ef2ed3d78..30c0688ac7 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -558,7 +558,7 @@ class ApplicationTest extends TestCase $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); - $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); + $this->assertNotRegExp('/foo:bar(?!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); } } From c7115a3087fe2f9392868bae1f504c4310955928 Mon Sep 17 00:00:00 2001 From: Anto Date: Sat, 14 Dec 2019 08:41:21 +0100 Subject: [PATCH 16/21] [Cache] Fix wrong classname in deprecation message --- src/Symfony/Component/Cache/Simple/ApcuCache.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Component/Cache/Simple/ApcuCache.php b/src/Symfony/Component/Cache/Simple/ApcuCache.php index c22771e822..f1eb946ff5 100644 --- a/src/Symfony/Component/Cache/Simple/ApcuCache.php +++ b/src/Symfony/Component/Cache/Simple/ApcuCache.php @@ -11,7 +11,9 @@ namespace Symfony\Component\Cache\Simple; +use Symfony\Component\Cache\Adapter\ApcuAdapter; use Symfony\Component\Cache\Traits\ApcuTrait; +use Symfony\Contracts\Cache\CacheInterface; @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ApcuCache::class, ApcuAdapter::class, CacheInterface::class), E_USER_DEPRECATED); From 4db5f022a10e109da6c1e94f48d2fdeed6d3c036 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 14 Dec 2019 21:11:39 +0100 Subject: [PATCH 17/21] stop using deprecated Doctrine persistence classes --- src/Symfony/Bridge/Doctrine/ManagerRegistry.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index ba07c8f84a..852aa3898a 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -12,17 +12,32 @@ namespace Symfony\Bridge\Doctrine; use Doctrine\Common\Persistence\AbstractManagerRegistry as LegacyAbstractManagerRegistry; +use Doctrine\Persistence\AbstractManagerRegistry; use ProxyManager\Proxy\LazyLoadingInterface; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; +if (class_exists(AbstractManagerRegistry::class)) { + abstract class ManagerRegistry extends AbstractManagerRegistry implements ContainerAwareInterface + { + use ManagerRegistryTrait; + } +} else { + abstract class ManagerRegistry extends LegacyAbstractManagerRegistry implements ContainerAwareInterface + { + use ManagerRegistryTrait; + } +} + /** * References Doctrine connections and entity/document managers. * * @author Lukas Kahwe Smith + * + * @internal */ -abstract class ManagerRegistry extends LegacyAbstractManagerRegistry implements ContainerAwareInterface +trait ManagerRegistryTrait { /** * @var Container From 1ed8e42d15deff3325dddf8fa4c2608cd322b0a9 Mon Sep 17 00:00:00 2001 From: Valentin Udaltsov Date: Tue, 3 Dec 2019 21:17:46 +0300 Subject: [PATCH 18/21] [Serializer] Skip uninitialized (PHP 7.4) properties in PropertyNormalizer and ObjectNormalizer --- .../Normalizer/ObjectNormalizer.php | 6 +++++ .../Normalizer/PropertyNormalizer.php | 11 ++++++++++ .../Serializer/Tests/Fixtures/Php74Dummy.php | 22 +++++++++++++++++++ .../Tests/Normalizer/ObjectNormalizerTest.php | 13 +++++++++++ .../Normalizer/PropertyNormalizerTest.php | 13 +++++++++++ 5 files changed, 65 insertions(+) create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/Php74Dummy.php diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index c1b3dd260e..5c2cd7af1c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -83,8 +83,14 @@ class ObjectNormalizer extends AbstractObjectNormalizer } } + $checkPropertyInitialization = \PHP_VERSION_ID >= 70400; + // properties foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) { + if ($checkPropertyInitialization && !$reflProperty->isInitialized($object)) { + continue; + } + if ($reflProperty->isStatic() || !$this->isAllowedAttribute($object, $reflProperty->name, $format, $context)) { continue; } diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 46faa1e7e9..be6634b493 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -99,9 +99,20 @@ class PropertyNormalizer extends AbstractObjectNormalizer { $reflectionObject = new \ReflectionObject($object); $attributes = []; + $checkPropertyInitialization = \PHP_VERSION_ID >= 70400; do { foreach ($reflectionObject->getProperties() as $property) { + if ($checkPropertyInitialization) { + if (!$property->isPublic()) { + $property->setAccessible(true); + } + + if (!$property->isInitialized($object)) { + continue; + } + } + if (!$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context)) { continue; } diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/Php74Dummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/Php74Dummy.php new file mode 100644 index 0000000000..e8cebd9b45 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/Php74Dummy.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +/** + * @author Valentin Udaltsov + */ +final class Php74Dummy +{ + public string $uninitializedProperty; + + public string $initializedProperty = 'defaultValue'; +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 765db4c82b..330a86130f 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -28,6 +28,7 @@ use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy; use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy; use Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy; +use Symfony\Component\Serializer\Tests\Fixtures\Php74Dummy; use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder; /** @@ -81,6 +82,18 @@ class ObjectNormalizerTest extends TestCase ); } + /** + * @requires PHP 7.4 + */ + public function testNormalizeObjectWithUninitializedProperties() + { + $obj = new Php74Dummy(); + $this->assertEquals( + ['initializedProperty' => 'defaultValue'], + $this->normalizer->normalize($obj, 'any') + ); + } + public function testDenormalize() { $obj = $this->normalizer->denormalize( diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php index 4b138fca7b..5bc62a6d9e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php @@ -22,6 +22,7 @@ use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy; use Symfony\Component\Serializer\Tests\Fixtures\GroupDummyChild; use Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy; +use Symfony\Component\Serializer\Tests\Fixtures\Php74Dummy; use Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy; use Symfony\Component\Serializer\Tests\Fixtures\PropertySiblingHolder; @@ -55,6 +56,18 @@ class PropertyNormalizerTest extends TestCase ); } + /** + * @requires PHP 7.4 + */ + public function testNormalizeObjectWithUninitializedProperties() + { + $obj = new Php74Dummy(); + $this->assertEquals( + ['initializedProperty' => 'defaultValue'], + $this->normalizer->normalize($obj, 'any') + ); + } + public function testDenormalize() { $obj = $this->normalizer->denormalize( From 6986394042f83bcace33d2fdaf88556057589029 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sun, 15 Dec 2019 18:41:18 +0100 Subject: [PATCH 19/21] [FrameworkBundle][ContainerLintCommand] Improve messages when the kernel or the container is not supported --- .../Bundle/FrameworkBundle/Command/ContainerLintCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php index 1f4ffe59af..5e6277567e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php @@ -80,7 +80,7 @@ final class ContainerLintCommand extends Command if (!$kernel->isDebug() || !(new ConfigCache($kernelContainer->getParameter('debug.container.dump'), true))->isFresh()) { if (!$kernel instanceof Kernel) { - throw new RuntimeException("This command does not support the console application's kernel."); + throw new RuntimeException(sprintf('This command does not support the application kernel: "%s" does not extend "%s".', \get_class($kernel), Kernel::class)); } $buildContainer = \Closure::bind(function (): ContainerBuilder { @@ -93,7 +93,7 @@ final class ContainerLintCommand extends Command $skippedIds = []; } else { if (!$kernelContainer instanceof Container) { - throw new RuntimeException("This command does not support the console application kernel's container."); + throw new RuntimeException(sprintf('This command does not support the application container: "%s" does not extend "%s".', \get_class($kernelContainer), Container::class)); } (new XmlFileLoader($container = new ContainerBuilder($parameterBag = new EnvPlaceholderParameterBag()), new FileLocator()))->load($kernelContainer->getParameter('debug.container.dump')); From b3742ec493c37b597e507b2eeaf12bb5c7fca794 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Sun, 15 Dec 2019 22:30:08 +0100 Subject: [PATCH 20/21] CS --- .../Authorization/AccessDecisionManager.php | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php index 82e7cd459f..aefd111b29 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php @@ -86,17 +86,13 @@ class AccessDecisionManager implements AccessDecisionManagerInterface $deny = 0; foreach ($this->voters as $voter) { $result = $this->vote($voter, $token, $object, $attributes); - switch ($result) { - case VoterInterface::ACCESS_GRANTED: - return true; - case VoterInterface::ACCESS_DENIED: - ++$deny; + if (VoterInterface::ACCESS_GRANTED === $result) { + return true; + } - break; - - default: - break; + if (VoterInterface::ACCESS_DENIED === $result) { + ++$deny; } } @@ -128,16 +124,10 @@ class AccessDecisionManager implements AccessDecisionManagerInterface foreach ($this->voters as $voter) { $result = $this->vote($voter, $token, $object, $attributes); - switch ($result) { - case VoterInterface::ACCESS_GRANTED: - ++$grant; - - break; - - case VoterInterface::ACCESS_DENIED: - ++$deny; - - break; + if (VoterInterface::ACCESS_GRANTED === $result) { + ++$grant; + } elseif (VoterInterface::ACCESS_DENIED === $result) { + ++$deny; } } @@ -169,17 +159,12 @@ class AccessDecisionManager implements AccessDecisionManagerInterface foreach ($attributes as $attribute) { $result = $this->vote($voter, $token, $object, [$attribute]); - switch ($result) { - case VoterInterface::ACCESS_GRANTED: - ++$grant; + if (VoterInterface::ACCESS_DENIED === $result) { + return false; + } - break; - - case VoterInterface::ACCESS_DENIED: - return false; - - default: - break; + if (VoterInterface::ACCESS_GRANTED === $result) { + ++$grant; } } } From 64410edfb340a7e818881960ba06013205d8d11f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 16 Dec 2019 11:44:01 +0100 Subject: [PATCH 21/21] fix merge --- .../Validator/Tests/Constraints/DivisibleByValidatorTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/DivisibleByValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/DivisibleByValidatorTest.php index 99a969d51d..4dff952f19 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/DivisibleByValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/DivisibleByValidatorTest.php @@ -97,4 +97,9 @@ class DivisibleByValidatorTest extends AbstractComparisonValidatorTestCase [\ArrayIterator::class, new \ArrayIterator(), 12], ]; } + + public function provideComparisonsToNullValueAtPropertyPath() + { + $this->markTestSkipped('DivisibleByValidator rejects null values.'); + } }