diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 52531183458b8..f24caa613bcca 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -21,6 +21,17 @@ jobs:
- 5432:5432
env:
POSTGRES_PASSWORD: 'password'
+ ldap:
+ image: bitnami/openldap
+ ports:
+ - 3389:3389
+ env:
+ LDAP_ADMIN_USERNAME: admin
+ LDAP_ADMIN_PASSWORD: symfony
+ LDAP_ROOT: dc=symfony,dc=com
+ LDAP_PORT_NUMBER: 3389
+ LDAP_USERS: a
+ LDAP_PASSWORDS: a
redis:
image: redis:6.0.0
ports:
@@ -35,9 +46,16 @@ jobs:
- 7004:7004
- 7005:7005
- 7006:7006
- - 7007:7007
env:
- STANDALONE: true
+ STANDALONE: 1
+ redis-sentinel:
+ image: bitnami/redis-sentinel:6.0
+ ports:
+ - 26379:26379
+ env:
+ REDIS_MASTER_HOST: redis
+ REDIS_MASTER_SET: redis_sentinel
+ REDIS_SENTINEL_QUORUM: 1
memcached:
image: memcached:1.6.5
ports:
@@ -105,11 +123,17 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: "none"
- extensions: "json,couchbase,memcached,mongodb,redis,rdkafka,xsl"
+ extensions: "json,couchbase,memcached,mongodb,redis,rdkafka,xsl,ldap"
ini-values: "memory_limit=-1"
php-version: "${{ matrix.php }}"
tools: pecl
+ - name: Load fixtures
+ uses: docker://bitnami/openldap
+ with:
+ entrypoint: /bin/bash
+ args: -c "(ldapwhoami -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony||sleep 5) && ldapadd -h ldap:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif && ldapdelete -h ldap:3389 -D cn=admin,dc=symfony,dc=com -w symfony cn=a,ou=users,dc=symfony,dc=com"
+
- name: Configure composer
run: |
COMPOSER_HOME="$(composer config home)"
@@ -138,15 +162,19 @@ jobs:
echo "::endgroup::"
- name: Run tests
- run: ./phpunit --group integration
+ run: ./phpunit --group integration -v
env:
REDIS_HOST: localhost
REDIS_CLUSTER_HOSTS: 'localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005'
+ REDIS_SENTINEL_HOSTS: 'localhost:26379'
+ REDIS_SENTINEL_SERVICE: redis_sentinel
MESSENGER_REDIS_DSN: redis://127.0.0.1:7006/messages
MESSENGER_AMQP_DSN: amqp://localhost/%2f/messages
MESSENGER_SQS_DSN: "sqs://localhost:9494/messages?sslmode=disable&poll_timeout=0.01"
MESSENGER_SQS_FIFO_QUEUE_DSN: "sqs://localhost:9494/messages.fifo?sslmode=disable&poll_timeout=0.01"
MEMCACHED_HOST: localhost
+ LDAP_HOST: localhost
+ LDAP_PORT: 3389
MONGODB_HOST: localhost
KAFKA_BROKER: localhost:9092
POSTGRES_HOST: localhost
diff --git a/.travis.yml b/.travis.yml
index 8d284a7a121bc..e59103e3a27a1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,8 +9,6 @@ addons:
apt_packages:
- parallel
- language-pack-fr-base
- - ldap-utils
- - slapd
- zookeeperd
- libzookeeper-mt-dev
@@ -55,11 +53,6 @@ before_install:
# General configuration
set -e
stty cols 120
- mkdir /tmp/slapd
- if [ ! -e /tmp/slapd-modules ]; then
- [ -d /usr/lib/openldap ] && ln -s /usr/lib/openldap /tmp/slapd-modules || ln -s /usr/lib/ldap /tmp/slapd-modules
- fi
- slapd -f src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf -h ldap://localhost:3389 &
cp .github/composer-config.json "$(composer config home)/config.json"
export PHPUNIT=$(readlink -f ./phpunit)
export PHPUNIT_X="$PHPUNIT --exclude-group tty,benchmark,intl-data"
@@ -169,13 +162,6 @@ before_install:
tfold ext.redis tpecl redis-5.2.3 redis.so $INI "no"
done
- - |
- # Load fixtures
- if [[ ! $skip ]]; then
- ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif &&
- ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif
- fi
-
install:
- |
# Install the phpunit-bridge from a PR if required
diff --git a/CHANGELOG-5.2.md b/CHANGELOG-5.2.md
index 4708969f2be28..2a83a9f8b5649 100644
--- a/CHANGELOG-5.2.md
+++ b/CHANGELOG-5.2.md
@@ -7,6 +7,22 @@ in 5.2 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.2.0...v5.2.1
+* 5.2.0-RC2 (2020-11-21)
+
+ * bug #39113 [DoctrineBridge] drop binary variants of UID types (nicolas-grekas)
+ * feature #39111 [Security] Update password upgrader listener to work with the new UserBadge (wouterj)
+ * bug #39083 [Dotenv] Check if method inheritEnvironmentVariables exists (Chi-teck)
+ * bug #39094 [Ldap] Fix undefined variable $con (derrabus)
+ * bug #39091 [Config] Recheck glob brace support after GlobResource was serialized (wouterj)
+ * bug #39092 Fix critical extension when reseting paged control (jderusse)
+ * bug #38614 [HttpFoundation] Fix for virtualhosts based on URL path (mvorisek)
+ * bug #39072 [FrameworkBundle] [Notifier] fix firebase transport factory DI tag type (xabbuh)
+ * bug #39070 [Validator] Remove IsinValidator's validator dependency (derrabus)
+ * bug #38387 [Validator] prevent hash collisions caused by reused object hashes (fancyweb, xabbuh)
+ * bug #38999 [DependencyInjection] autoconfigure behavior describing tags on decorators (xabbuh)
+ * bug #39058 [DependencyInjection] Fix circular detection with multiple paths (jderusse)
+ * bug #39059 [Filesystem] fix cleaning up tmp files when dumpFile() fails (nicolas-grekas)
+
* 5.2.0-RC1 (2020-11-10)
* bug #39004 [Messenger] Fix JSON deserialization of ErrorDetailsStamp and normalization of FlattenException::$statusText (Jean85)
diff --git a/composer.json b/composer.json
index 3034f796f7084..3ca90e56d1d68 100644
--- a/composer.json
+++ b/composer.json
@@ -175,6 +175,6 @@
],
"minimum-stability": "dev",
"extra": {
- "branch-version": "5.x"
+ "branch-version": "5.2"
}
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index d2179994d502e..5bf27434011c1 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md
index 574db63d5ec07..ea6918de35f1c 100644
--- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md
+++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md
@@ -4,7 +4,7 @@ CHANGELOG
5.2.0
-----
- * added support for symfony/uid as `UlidType`, `UuidType`, `UlidBinaryType` and `UuidBinaryType` as Doctrine types
+ * added support for symfony/uid as `UlidType` and `UuidType` as Doctrine types
* added `UlidGenerator`, `UUidV1Generator`, `UuidV4Generator` and `UuidV6Generator`
5.0.0
diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php
index f5fea85d14e9c..e30fe44429670 100644
--- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php
+++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php
@@ -11,9 +11,7 @@
namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass;
-use Symfony\Bridge\Doctrine\Types\UlidBinaryType;
use Symfony\Bridge\Doctrine\Types\UlidType;
-use Symfony\Bridge\Doctrine\Types\UuidBinaryType;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -40,14 +38,6 @@ public function process(ContainerBuilder $container)
$typeDefinition['ulid'] = ['class' => UlidType::class];
}
- if (!isset($typeDefinition['uuid_binary'])) {
- $typeDefinition['uuid_binary'] = ['class' => UuidBinaryType::class];
- }
-
- if (!isset($typeDefinition['ulid_binary'])) {
- $typeDefinition['ulid_binary'] = ['class' => UlidBinaryType::class];
- }
-
$container->setParameter('doctrine.dbal.connection_factory.types', $typeDefinition);
}
}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidBinaryTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidBinaryTypeTest.php
deleted file mode 100644
index 4141dfa55e540..0000000000000
--- a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidBinaryTypeTest.php
+++ /dev/null
@@ -1,114 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Doctrine\Tests\Types;
-
-use Doctrine\DBAL\Platforms\AbstractPlatform;
-use Doctrine\DBAL\Types\ConversionException;
-use Doctrine\DBAL\Types\Type;
-use PHPUnit\Framework\TestCase;
-use Symfony\Bridge\Doctrine\Types\UlidBinaryType;
-use Symfony\Component\Uid\Ulid;
-
-class UlidBinaryTypeTest extends TestCase
-{
- private const DUMMY_ULID = '01EEDQEK6ZAZE93J8KG5B4MBJC';
-
- private $platform;
-
- /** @var UlidBinaryType */
- private $type;
-
- public static function setUpBeforeClass(): void
- {
- Type::addType('ulid_binary', UlidBinaryType::class);
- }
-
- protected function setUp(): void
- {
- $this->platform = $this->createMock(AbstractPlatform::class);
- $this->platform
- ->method('getBinaryTypeDeclarationSQL')
- ->willReturn('DUMMYBINARY(16)');
-
- $this->type = Type::getType('ulid_binary');
- }
-
- public function testUlidConvertsToDatabaseValue()
- {
- $uuid = Ulid::fromString(self::DUMMY_ULID);
-
- $expected = $uuid->toBinary();
- $actual = $this->type->convertToDatabaseValue($uuid, $this->platform);
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testStringUlidConvertsToDatabaseValue()
- {
- $expected = Ulid::fromString(self::DUMMY_ULID)->toBinary();
- $actual = $this->type->convertToDatabaseValue(self::DUMMY_ULID, $this->platform);
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testNotSupportedTypeConversionForDatabaseValue()
- {
- $this->expectException(ConversionException::class);
-
- $this->type->convertToDatabaseValue(new \stdClass(), $this->platform);
- }
-
- public function testNullConversionForDatabaseValue()
- {
- $this->assertNull($this->type->convertToDatabaseValue(null, $this->platform));
- }
-
- public function testUlidConvertsToPHPValue()
- {
- $uuid = $this->type->convertToPHPValue(self::DUMMY_ULID, $this->platform);
-
- $this->assertEquals(self::DUMMY_ULID, $uuid->__toString());
- }
-
- public function testInvalidUlidConversionForPHPValue()
- {
- $this->expectException(ConversionException::class);
-
- $this->type->convertToPHPValue('abcdefg', $this->platform);
- }
-
- public function testNullConversionForPHPValue()
- {
- $this->assertNull($this->type->convertToPHPValue(null, $this->platform));
- }
-
- public function testReturnValueIfUlidForPHPValue()
- {
- $uuid = new Ulid();
- $this->assertSame($uuid, $this->type->convertToPHPValue($uuid, $this->platform));
- }
-
- public function testGetName()
- {
- $this->assertEquals('ulid_binary', $this->type->getName());
- }
-
- public function testGetGuidTypeDeclarationSQL()
- {
- $this->assertEquals('DUMMYBINARY(16)', $this->type->getSqlDeclaration(['length' => 36], $this->platform));
- }
-
- public function testRequiresSQLCommentHint()
- {
- $this->assertTrue($this->type->requiresSQLCommentHint($this->platform));
- }
-}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php
index f3969bcb4c725..36aace3ed843d 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php
@@ -37,6 +37,9 @@ public static function setUpBeforeClass(): void
protected function setUp(): void
{
$this->platform = $this->createMock(AbstractPlatform::class);
+ $this->platform
+ ->method('hasNativeGuidType')
+ ->willReturn(true);
$this->platform
->method('getGuidTypeDeclarationSQL')
->willReturn('DUMMYVARCHAR()');
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidBinaryTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidBinaryTypeTest.php
deleted file mode 100644
index b44a52578c5d2..0000000000000
--- a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidBinaryTypeTest.php
+++ /dev/null
@@ -1,123 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Doctrine\Tests\Types;
-
-use Doctrine\DBAL\Platforms\AbstractPlatform;
-use Doctrine\DBAL\Types\ConversionException;
-use Doctrine\DBAL\Types\Type;
-use PHPUnit\Framework\TestCase;
-use Symfony\Bridge\Doctrine\Types\UuidBinaryType;
-use Symfony\Component\Uid\Uuid;
-
-class UuidBinaryTypeTest extends TestCase
-{
- private const DUMMY_UUID = '9f755235-5a2d-4aba-9605-e9962b312e50';
-
- private $platform;
-
- /** @var UuidBinaryType */
- private $type;
-
- public static function setUpBeforeClass(): void
- {
- Type::addType('uuid_binary', UuidBinaryType::class);
- }
-
- protected function setUp(): void
- {
- $this->platform = $this->createMock(AbstractPlatform::class);
- $this->platform
- ->method('getBinaryTypeDeclarationSQL')
- ->willReturn('DUMMYBINARY(16)');
-
- $this->type = Type::getType('uuid_binary');
- }
-
- public function testUuidConvertsToDatabaseValue()
- {
- $uuid = Uuid::fromString(self::DUMMY_UUID);
-
- $expected = uuid_parse(self::DUMMY_UUID);
- $actual = $this->type->convertToDatabaseValue($uuid, $this->platform);
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testStringUuidConvertsToDatabaseValue()
- {
- $uuid = self::DUMMY_UUID;
-
- $expected = uuid_parse(self::DUMMY_UUID);
- $actual = $this->type->convertToDatabaseValue($uuid, $this->platform);
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testInvalidUuidConversionForDatabaseValue()
- {
- $this->expectException(ConversionException::class);
-
- $this->type->convertToDatabaseValue('abcdefg', $this->platform);
- }
-
- public function testNullConversionForDatabaseValue()
- {
- $this->assertNull($this->type->convertToDatabaseValue(null, $this->platform));
- }
-
- public function testUuidConvertsToPHPValue()
- {
- $uuid = $this->type->convertToPHPValue(uuid_parse(self::DUMMY_UUID), $this->platform);
-
- $this->assertEquals(self::DUMMY_UUID, $uuid->__toString());
- }
-
- public function testInvalidUuidConversionForPHPValue()
- {
- $this->expectException(ConversionException::class);
-
- $this->type->convertToPHPValue('abcdefg', $this->platform);
- }
-
- public function testNotSupportedTypeConversionForDatabaseValue()
- {
- $this->expectException(ConversionException::class);
-
- $this->type->convertToDatabaseValue(new \stdClass(), $this->platform);
- }
-
- public function testNullConversionForPHPValue()
- {
- $this->assertNull($this->type->convertToPHPValue(null, $this->platform));
- }
-
- public function testReturnValueIfUuidForPHPValue()
- {
- $uuid = Uuid::v4();
- $this->assertSame($uuid, $this->type->convertToPHPValue($uuid, $this->platform));
- }
-
- public function testGetName()
- {
- $this->assertEquals('uuid_binary', $this->type->getName());
- }
-
- public function testGetGuidTypeDeclarationSQL()
- {
- $this->assertEquals('DUMMYBINARY(16)', $this->type->getSqlDeclaration(['length' => 36], $this->platform));
- }
-
- public function testRequiresSQLCommentHint()
- {
- $this->assertTrue($this->type->requiresSQLCommentHint($this->platform));
- }
-}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php
index da775ca81573c..45e2ee0d5dc71 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php
@@ -37,6 +37,9 @@ public static function setUpBeforeClass(): void
protected function setUp(): void
{
$this->platform = $this->createMock(AbstractPlatform::class);
+ $this->platform
+ ->method('hasNativeGuidType')
+ ->willReturn(true);
$this->platform
->method('getGuidTypeDeclarationSQL')
->willReturn('DUMMYVARCHAR()');
diff --git a/src/Symfony/Bridge/Doctrine/Types/AbstractBinaryUidType.php b/src/Symfony/Bridge/Doctrine/Types/AbstractBinaryUidType.php
deleted file mode 100644
index 1dbb70abf556a..0000000000000
--- a/src/Symfony/Bridge/Doctrine/Types/AbstractBinaryUidType.php
+++ /dev/null
@@ -1,86 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Doctrine\Types;
-
-use Doctrine\DBAL\Platforms\AbstractPlatform;
-use Doctrine\DBAL\Types\ConversionException;
-use Doctrine\DBAL\Types\Type;
-use Symfony\Component\Uid\AbstractUid;
-
-abstract class AbstractBinaryUidType extends Type
-{
- abstract protected function getUidClass(): string;
-
- public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
- {
- return $platform->getBinaryTypeDeclarationSQL([
- 'length' => '16',
- 'fixed' => true,
- ]);
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws ConversionException
- */
- public function convertToPHPValue($value, AbstractPlatform $platform): ?AbstractUid
- {
- if ($value instanceof AbstractUid || null === $value) {
- return $value;
- }
-
- if (!\is_string($value)) {
- throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'string', AbstractUid::class]);
- }
-
- try {
- return $this->getUidClass()::fromString($value);
- } catch (\InvalidArgumentException $e) {
- throw ConversionException::conversionFailed($value, $this->getName(), $e);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws ConversionException
- */
- public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
- {
- if ($value instanceof AbstractUid) {
- return $value->toBinary();
- }
-
- if (null === $value || '' === $value) {
- return null;
- }
-
- if (!\is_string($value)) {
- throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'string', AbstractUid::class]);
- }
-
- try {
- return $this->getUidClass()::fromString($value)->toBinary();
- } catch (\InvalidArgumentException $e) {
- throw ConversionException::conversionFailed($value, $this->getName());
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function requiresSQLCommentHint(AbstractPlatform $platform): bool
- {
- return true;
- }
-}
diff --git a/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php b/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php
index 4b57bc6b852f6..ec0c6ef6f8078 100644
--- a/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php
+++ b/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php
@@ -25,7 +25,14 @@ abstract protected function getUidClass(): string;
*/
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
{
- return $platform->getGuidTypeDeclarationSQL($column);
+ if ($platform->hasNativeGuidType()) {
+ return $platform->getGuidTypeDeclarationSQL($column);
+ }
+
+ return $platform->getBinaryTypeDeclarationSQL([
+ 'length' => '16',
+ 'fixed' => true,
+ ]);
}
/**
@@ -57,8 +64,10 @@ public function convertToPHPValue($value, AbstractPlatform $platform): ?Abstract
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{
+ $toString = $platform->hasNativeGuidType() ? 'toRfc4122' : 'toBinary';
+
if ($value instanceof AbstractUid) {
- return $value->toRfc4122();
+ return $value->$toString();
}
if (null === $value || '' === $value) {
@@ -70,7 +79,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?str
}
try {
- return $this->getUidClass()::fromString($value)->toRfc4122();
+ return $this->getUidClass()::fromString($value)->$toString();
} catch (\InvalidArgumentException $e) {
throw ConversionException::conversionFailed($value, $this->getName());
}
diff --git a/src/Symfony/Bridge/Doctrine/Types/UlidBinaryType.php b/src/Symfony/Bridge/Doctrine/Types/UlidBinaryType.php
deleted file mode 100644
index 34077d24494e0..0000000000000
--- a/src/Symfony/Bridge/Doctrine/Types/UlidBinaryType.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Doctrine\Types;
-
-use Symfony\Component\Uid\Ulid;
-
-final class UlidBinaryType extends AbstractBinaryUidType
-{
- public function getName(): string
- {
- return 'ulid_binary';
- }
-
- protected function getUidClass(): string
- {
- return Ulid::class;
- }
-}
diff --git a/src/Symfony/Bridge/Doctrine/Types/UuidBinaryType.php b/src/Symfony/Bridge/Doctrine/Types/UuidBinaryType.php
deleted file mode 100644
index 9e161a8ccba76..0000000000000
--- a/src/Symfony/Bridge/Doctrine/Types/UuidBinaryType.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Doctrine\Types;
-
-use Symfony\Component\Uid\Uuid;
-
-final class UuidBinaryType extends AbstractBinaryUidType
-{
- public function getName(): string
- {
- return 'uuid_binary';
- }
-
- protected function getUidClass(): string
- {
- return Uuid::class;
- }
-}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFindTest.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFoundTest.php
similarity index 100%
rename from src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFindTest.php
rename to src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFoundTest.php
diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php
index 3362aab9fb5eb..684ff36581776 100644
--- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php
+++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php
@@ -76,7 +76,7 @@ public function & __get($name)
$targetObject = $this->valueHolder%s;
- $backtrace = debug_backtrace(false);
+ $backtrace = debug_backtrace(false%S);
trigger_error(
sprintf(
'Undefined property: %s::$%s in %s on line %s',
@@ -115,8 +115,7 @@ public function __unset($name)
$targetObject = $this->valueHolder%s;
unset($targetObject->$name);
-return;
- }
+%a }
public function __clone()
{
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index ea5e139e3fe64..a599a684af7cd 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -533,6 +533,14 @@ public function load(array $configs, ContainerBuilder $container)
$container->registerForAutoconfiguration(RouteLoaderInterface::class)
->addTag('routing.route_loader');
+
+ $container->setParameter('container.behavior_describing_tags', [
+ 'container.service_locator',
+ 'container.service_subscriber',
+ 'kernel.event_subscriber',
+ 'kernel.locale_aware',
+ 'kernel.reset',
+ ]);
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.php
index 0d874a07ed321..a7e9bbd912746 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.php
@@ -11,11 +11,16 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+use Symfony\Component\Mime\MimeTypeGuesserInterface;
use Symfony\Component\Mime\MimeTypes;
+use Symfony\Component\Mime\MimeTypesInterface;
return static function (ContainerConfigurator $container) {
$container->services()
->set('mime_types', MimeTypes::class)
->call('setDefault', [service('mime_types')])
+
+ ->alias(MimeTypesInterface::class, 'mime_types')
+ ->alias(MimeTypeGuesserInterface::class, 'mime_types')
;
};
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php
index cff7f0d0c91dc..be40329cc9ebc 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php
@@ -73,7 +73,7 @@
->set('notifier.transport_factory.firebase', FirebaseTransportFactory::class)
->parent('notifier.transport_factory.abstract')
- ->tag('texter.transport_factory')
+ ->tag('chatter.transport_factory')
->set('notifier.transport_factory.freemobile', FreeMobileTransportFactory::class)
->parent('notifier.transport_factory.abstract')
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index b902d3b636a2f..e9d141d528757 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -1622,6 +1622,20 @@ public function testHttpClientMockResponseFactory()
$this->assertSame('my_response_factory', (string) $argument);
}
+ public function testRegisterParameterCollectingBehaviorDescribingTags()
+ {
+ $container = $this->createContainerFromFile('default_config');
+
+ $this->assertTrue($container->hasParameter('container.behavior_describing_tags'));
+ $this->assertEquals([
+ 'container.service_locator',
+ 'container.service_subscriber',
+ 'kernel.event_subscriber',
+ 'kernel.locale_aware',
+ 'kernel.reset',
+ ], $container->getParameter('container.behavior_describing_tags'));
+ }
+
protected function createContainer(array $data = [])
{
return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([
diff --git a/src/Symfony/Component/BrowserKit/CHANGELOG.md b/src/Symfony/Component/BrowserKit/CHANGELOG.md
index 323166a3d6cc5..8506ad8efe73c 100644
--- a/src/Symfony/Component/BrowserKit/CHANGELOG.md
+++ b/src/Symfony/Component/BrowserKit/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+5.2.0
+-----
+
+ * [BC BREAK] Request parameters are now casted to string in `Request::__construct()`.
+
4.3.0
-----
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php
index 09f563036bc2f..82b9f08b65474 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php
@@ -37,7 +37,7 @@ public static function setUpBeforeClass(): void
public function testInvalidDSNHasBothClusterAndSentinel()
{
$this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('Invalid Redis DSN: cannot use both redis_cluster and redis_sentinel at the same time');
+ $this->expectExceptionMessage('Cannot use both "redis_cluster" and "redis_sentinel" at the same time:');
$dsn = 'redis:?host[redis1]&host[redis2]&host[redis3]&redis_cluster=1&redis_sentinel=mymaster';
RedisAdapter::createConnection($dsn);
}
diff --git a/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php
new file mode 100644
index 0000000000000..f704bbfe0e49f
--- /dev/null
+++ b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php
@@ -0,0 +1,98 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Cache\Tests\Marshaller;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Cache\Adapter\TraceableAdapter;
+use Symfony\Component\Cache\DataCollector\CacheDataCollector;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class CacheDataCollectorTest extends TestCase
+{
+ private const INSTANCE_NAME = 'test';
+
+ public function testEmptyDataCollector()
+ {
+ $statistics = $this->getCacheDataCollectorStatisticsFromEvents([]);
+
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 0, 'calls');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 0, 'reads');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes');
+ }
+
+ public function testOneEventDataCollector()
+ {
+ $traceableAdapterEvent = new \stdClass();
+ $traceableAdapterEvent->name = 'getItem';
+ $traceableAdapterEvent->start = 0;
+ $traceableAdapterEvent->end = 0;
+ $traceableAdapterEvent->hits = 0;
+
+ $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]);
+
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 1, 'misses');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes');
+ }
+
+ public function testHitedEventDataCollector()
+ {
+ $traceableAdapterEvent = new \stdClass();
+ $traceableAdapterEvent->name = 'hasItem';
+ $traceableAdapterEvent->start = 0;
+ $traceableAdapterEvent->end = 0;
+ $traceableAdapterEvent->hits = 1;
+ $traceableAdapterEvent->misses = 0;
+ $traceableAdapterEvent->result = ['foo' => false];
+
+ $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]);
+
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 1, 'hits');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes');
+ }
+
+ public function testSavedEventDataCollector()
+ {
+ $traceableAdapterEvent = new \stdClass();
+ $traceableAdapterEvent->name = 'save';
+ $traceableAdapterEvent->start = 0;
+ $traceableAdapterEvent->end = 0;
+
+ $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]);
+
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 0, 'reads');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses');
+ $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 1, 'writes');
+ }
+
+ private function getCacheDataCollectorStatisticsFromEvents(array $traceableAdapterEvents)
+ {
+ $traceableAdapterMock = $this->createMock(TraceableAdapter::class);
+ $traceableAdapterMock->method('getCalls')->willReturn($traceableAdapterEvents);
+
+ $cacheDataCollector = new CacheDataCollector();
+ $cacheDataCollector->addInstance(self::INSTANCE_NAME, $traceableAdapterMock);
+ $cacheDataCollector->collect(new Request(), new Response());
+
+ return $cacheDataCollector->getStatistics();
+ }
+}
diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json
index 600f63e1d2e24..b6a41417a8339 100644
--- a/src/Symfony/Component/Cache/composer.json
+++ b/src/Symfony/Component/Cache/composer.json
@@ -38,6 +38,7 @@
"symfony/config": "^4.4|^5.0",
"symfony/dependency-injection": "^4.4|^5.0",
"symfony/filesystem": "^4.4|^5.0",
+ "symfony/http-kernel": "^4.4|^5.0",
"symfony/messenger": "^4.4|^5.0",
"symfony/var-dumper": "^4.4|^5.0"
},
diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php
index 21777e648eb45..c77e837c245f9 100644
--- a/src/Symfony/Component/Config/Resource/GlobResource.php
+++ b/src/Symfony/Component/Config/Resource/GlobResource.php
@@ -94,6 +94,14 @@ public function __sleep(): array
return ['prefix', 'pattern', 'recursive', 'hash', 'forExclusion', 'excludedPrefixes'];
}
+ /**
+ * @internal
+ */
+ public function __wakeup(): void
+ {
+ $this->globBrace = \defined('GLOB_BRACE') ? \GLOB_BRACE : 0;
+ }
+
public function getIterator(): \Traversable
{
if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) {
diff --git a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php
index 2b6609d740c86..a30fbe8c4339b 100644
--- a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php
+++ b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php
@@ -194,4 +194,17 @@ public function testUnbalancedBraceFallback()
$this->assertSame([], array_keys(iterator_to_array($resource)));
}
+
+ public function testSerializeUnserialize()
+ {
+ $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures';
+ $resource = new GlobResource($dir, '/Resource', true);
+
+ $newResource = unserialize(serialize($resource));
+
+ $p = new \ReflectionProperty($resource, 'globBrace');
+ $p->setAccessible(true);
+
+ $this->assertEquals($p->getValue($resource), $p->getValue($newResource));
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php
index 60d059fb29445..4aa33aad1ac26 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php
@@ -35,12 +35,22 @@ public function process(ContainerBuilder $container)
}
}
+ $tagsToKeep = [];
+
+ if ($container->hasParameter('container.behavior_describing_tags')) {
+ $tagsToKeep = $container->getParameter('container.behavior_describing_tags');
+ }
+
foreach ($container->getDefinitions() as $id => $definition) {
- $container->setDefinition($id, $this->processDefinition($container, $id, $definition));
+ $container->setDefinition($id, $this->processDefinition($container, $id, $definition, $tagsToKeep));
+ }
+
+ if ($container->hasParameter('container.behavior_describing_tags')) {
+ $container->getParameterBag()->remove('container.behavior_describing_tags');
}
}
- private function processDefinition(ContainerBuilder $container, string $id, Definition $definition): Definition
+ private function processDefinition(ContainerBuilder $container, string $id, Definition $definition, array $tagsToKeep): Definition
{
$instanceofConditionals = $definition->getInstanceofConditionals();
$autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : [];
@@ -114,10 +124,10 @@ private function processDefinition(ContainerBuilder $container, string $id, Defi
}
// Don't add tags to service decorators
- if (null === $definition->getDecoratedService()) {
- $i = \count($instanceofTags);
- while (0 <= --$i) {
- foreach ($instanceofTags[$i] as $k => $v) {
+ $i = \count($instanceofTags);
+ while (0 <= --$i) {
+ foreach ($instanceofTags[$i] as $k => $v) {
+ if (null === $definition->getDecoratedService() || \in_array($k, $tagsToKeep, true)) {
foreach ($v as $v) {
if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) {
continue;
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 7b0ea433033f6..9e71d955a227e 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -433,14 +433,13 @@ private function analyzeReferences()
$this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences);
}
- private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array $path = [], bool $byConstructor = true): void
+ private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$loops = [], array $path = [], bool $byConstructor = true): void
{
$path[$sourceId] = $byConstructor;
$checkedNodes[$sourceId] = true;
foreach ($edges as $edge) {
$node = $edge->getDestNode();
$id = $node->getId();
-
if (!($definition = $node->getValue()) instanceof Definition || $sourceId === $id || ($edge->isLazy() && ($this->proxyDumper ?? $this->getProxyDumper())->isProxyCandidate($definition)) || $edge->isWeak()) {
continue;
}
@@ -448,9 +447,12 @@ private function collectCircularReferences(string $sourceId, array $edges, array
if (isset($path[$id])) {
$loop = null;
$loopByConstructor = $edge->isReferencedByConstructor();
+ $pathInLoop = [$id, []];
foreach ($path as $k => $pathByConstructor) {
if (null !== $loop) {
$loop[] = $k;
+ $pathInLoop[1][$k] = $pathByConstructor;
+ $loops[$k][] = &$pathInLoop;
$loopByConstructor = $loopByConstructor && $pathByConstructor;
} elseif ($k === $id) {
$loop = [];
@@ -458,7 +460,39 @@ private function collectCircularReferences(string $sourceId, array $edges, array
}
$this->addCircularReferences($id, $loop, $loopByConstructor);
} elseif (!isset($checkedNodes[$id])) {
- $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $path, $edge->isReferencedByConstructor());
+ $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $loops, $path, $edge->isReferencedByConstructor());
+ } elseif (isset($loops[$id])) {
+ // we already had detected loops for this edge
+ // let's check if we have a common ancestor in one of the detected loops
+ foreach ($loops[$id] as [$first, $loopPath]) {
+ if (!isset($path[$first])) {
+ continue;
+ }
+ // We have a common ancestor, let's fill the current path
+ $fillPath = null;
+ foreach ($loopPath as $k => $pathByConstructor) {
+ if (null !== $fillPath) {
+ $fillPath[$k] = $pathByConstructor;
+ } elseif ($k === $id) {
+ $fillPath = $path;
+ $fillPath[$k] = $pathByConstructor;
+ }
+ }
+
+ // we can now build the loop
+ $loop = null;
+ $loopByConstructor = $edge->isReferencedByConstructor();
+ foreach ($fillPath as $k => $pathByConstructor) {
+ if (null !== $loop) {
+ $loop[] = $k;
+ $loopByConstructor = $loopByConstructor && $pathByConstructor;
+ } elseif ($k === $first) {
+ $loop = [];
+ }
+ }
+ $this->addCircularReferences($first, $loop, true);
+ break;
+ }
}
}
unset($path[$sourceId]);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
index 3acfbed776f1f..99aa65b13869b 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
@@ -12,12 +12,16 @@
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\ResourceInterface;
+use Symfony\Component\Config\ResourceCheckerInterface;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Contracts\Service\ResetInterface;
+use Symfony\Contracts\Service\ServiceSubscriberInterface;
class ResolveInstanceofConditionalsPassTest extends TestCase
{
@@ -325,4 +329,60 @@ public function testDecoratorsAreNotAutomaticallyTagged()
$this->assertSame(['manual' => [[]]], $container->getDefinition('decorator')->getTags());
}
+
+ public function testDecoratorsKeepBehaviorDescribingTags()
+ {
+ $container = new ContainerBuilder();
+
+ $container->setParameter('container.behavior_describing_tags', [
+ 'container.service_subscriber',
+ 'kernel.reset',
+ ]);
+
+ $container->register('decorator', DecoratorWithBehavior::class)
+ ->setAutoconfigured(true)
+ ->setDecoratedService('decorated')
+ ;
+
+ $container->registerForAutoconfiguration(ResourceCheckerInterface::class)
+ ->addTag('config_cache.resource_checker')
+ ;
+ $container->registerForAutoconfiguration(ServiceSubscriberInterface::class)
+ ->addTag('container.service_subscriber')
+ ;
+ $container->registerForAutoconfiguration(ResetInterface::class)
+ ->addTag('kernel.reset', ['method' => 'reset'])
+ ;
+
+ (new ResolveInstanceofConditionalsPass())->process($container);
+
+ $this->assertEquals([
+ 'container.service_subscriber' => [0 => []],
+ 'kernel.reset' => [
+ [
+ 'method' => 'reset',
+ ],
+ ],
+ ], $container->getDefinition('decorator')->getTags());
+ $this->assertFalse($container->hasParameter('container.behavior_describing_tags'));
+ }
+}
+
+class DecoratorWithBehavior implements ResetInterface, ResourceCheckerInterface, ServiceSubscriberInterface
+{
+ public function reset()
+ {
+ }
+
+ public function supports(ResourceInterface $metadata)
+ {
+ }
+
+ public function isFresh(ResourceInterface $resource, $timestamp)
+ {
+ }
+
+ public static function getSubscribedServices()
+ {
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index df6f06653edcb..005ea3230ad40 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -1395,6 +1395,9 @@ public function testAlmostCircular($visibility)
$container = include __DIR__.'/Fixtures/containers/container_almost_circular.php';
$container->compile();
+ $pA = $container->get('pA');
+ $this->assertEquals(new \stdClass(), $pA);
+
$logger = $container->get('monolog.logger');
$this->assertEquals(new \stdClass(), $logger->handler);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 3b77b7ed6eb2d..e68b4ceb228c3 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -1054,6 +1054,9 @@ public function testAlmostCircular($visibility)
$container = new $container();
+ $pA = $container->get('pA');
+ $this->assertEquals(new \stdClass(), $pA);
+
$logger = $container->get('monolog.logger');
$this->assertEquals(new \stdClass(), $logger->handler);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php
index 96c714493e8f3..6a0b2da766cdd 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php
@@ -9,6 +9,21 @@
$public = 'public' === $visibility;
$container = new ContainerBuilder();
+// multiple path detection
+
+$container->register('pA', 'stdClass')->setPublic(true)
+ ->addArgument(new Reference('pB'))
+ ->addArgument(new Reference('pC'));
+
+$container->register('pB', 'stdClass')->setPublic($public)
+ ->setProperty('d', new Reference('pD'));
+$container->register('pC', 'stdClass')->setPublic($public)
+ ->setLazy(true)
+ ->setProperty('d', new Reference('pD'));
+
+$container->register('pD', 'stdClass')->setPublic($public)
+ ->addArgument(new Reference('pA'));
+
// monolog-like + handler that require monolog
$container->register('monolog.logger', 'stdClass')->setPublic(true)
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php
index 8bc0146fb13f7..6d9985af3265b 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php
@@ -37,6 +37,7 @@ public function __construct()
'manager2' => 'getManager2Service',
'manager3' => 'getManager3Service',
'monolog.logger' => 'getMonolog_LoggerService',
+ 'pA' => 'getPAService',
'root' => 'getRootService',
'subscriber' => 'getSubscriberService',
];
@@ -84,6 +85,9 @@ public function getRemovedIds(): array
'manager4' => true,
'monolog.logger_2' => true,
'multiuse1' => true,
+ 'pB' => true,
+ 'pC' => true,
+ 'pD' => true,
'subscriber2' => true,
];
}
@@ -371,6 +375,28 @@ protected function getMonolog_LoggerService()
return $instance;
}
+ /**
+ * Gets the public 'pA' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getPAService()
+ {
+ $a = new \stdClass();
+
+ $b = ($this->privates['pC'] ?? $this->getPCService());
+
+ if (isset($this->services['pA'])) {
+ return $this->services['pA'];
+ }
+
+ $this->services['pA'] = $instance = new \stdClass($a, $b);
+
+ $a->d = ($this->privates['pD'] ?? $this->getPDService());
+
+ return $instance;
+ }
+
/**
* Gets the public 'root' shared service.
*
@@ -478,4 +504,34 @@ protected function getManager4Service($lazyLoad = true)
return $instance;
}
+
+ /**
+ * Gets the private 'pC' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getPCService($lazyLoad = true)
+ {
+ $this->privates['pC'] = $instance = new \stdClass();
+
+ $instance->d = ($this->privates['pD'] ?? $this->getPDService());
+
+ return $instance;
+ }
+
+ /**
+ * Gets the private 'pD' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getPDService()
+ {
+ $a = ($this->services['pA'] ?? $this->getPAService());
+
+ if (isset($this->privates['pD'])) {
+ return $this->privates['pD'];
+ }
+
+ return $this->privates['pD'] = new \stdClass($a);
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php
index 4540459203d00..2e5d5281e0b79 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php
@@ -50,6 +50,10 @@ public function __construct()
'manager3' => 'getManager3Service',
'monolog.logger' => 'getMonolog_LoggerService',
'monolog.logger_2' => 'getMonolog_Logger2Service',
+ 'pA' => 'getPAService',
+ 'pB' => 'getPBService',
+ 'pC' => 'getPCService',
+ 'pD' => 'getPDService',
'root' => 'getRootService',
'subscriber' => 'getSubscriberService',
];
@@ -559,6 +563,71 @@ protected function getMonolog_Logger2Service()
return $instance;
}
+ /**
+ * Gets the public 'pA' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getPAService()
+ {
+ $a = ($this->services['pB'] ?? $this->getPBService());
+
+ if (isset($this->services['pA'])) {
+ return $this->services['pA'];
+ }
+ $b = ($this->services['pC'] ?? $this->getPCService());
+
+ if (isset($this->services['pA'])) {
+ return $this->services['pA'];
+ }
+
+ return $this->services['pA'] = new \stdClass($a, $b);
+ }
+
+ /**
+ * Gets the public 'pB' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getPBService()
+ {
+ $this->services['pB'] = $instance = new \stdClass();
+
+ $instance->d = ($this->services['pD'] ?? $this->getPDService());
+
+ return $instance;
+ }
+
+ /**
+ * Gets the public 'pC' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getPCService($lazyLoad = true)
+ {
+ $this->services['pC'] = $instance = new \stdClass();
+
+ $instance->d = ($this->services['pD'] ?? $this->getPDService());
+
+ return $instance;
+ }
+
+ /**
+ * Gets the public 'pD' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getPDService()
+ {
+ $a = ($this->services['pA'] ?? $this->getPAService());
+
+ if (isset($this->services['pD'])) {
+ return $this->services['pD'];
+ }
+
+ return $this->services['pD'] = new \stdClass($a);
+ }
+
/**
* Gets the public 'root' shared service.
*
diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php
index 8cb2ba82c278c..21aa729091a7b 100644
--- a/src/Symfony/Component/Dotenv/Dotenv.php
+++ b/src/Symfony/Component/Dotenv/Dotenv.php
@@ -456,7 +456,7 @@ private function resolveCommands(string $value, array $loadedVars): string
$process = method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline('echo '.$matches[0]) : new Process('echo '.$matches[0]);
- if (!method_exists(Process::class, 'fromShellCommandline')) {
+ if (!method_exists(Process::class, 'fromShellCommandline') && method_exists(Process::class, 'inheritEnvironmentVariables')) {
// Symfony 3.4 does not inherit env vars by default:
$process->inheritEnvironmentVariables();
}
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index d9609f5484c2d..a1c53d88bb4b5 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -658,13 +658,17 @@ public function dumpFile(string $filename, $content)
// when the filesystem supports chmod.
$tmpFile = $this->tempnam($dir, basename($filename));
- if (false === @file_put_contents($tmpFile, $content)) {
- throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename);
- }
+ try {
+ if (false === @file_put_contents($tmpFile, $content)) {
+ throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename);
+ }
- @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask());
+ @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask());
- $this->rename($tmpFile, $filename, true);
+ $this->rename($tmpFile, $filename, true);
+ } finally {
+ @unlink($tmpFile);
+ }
}
/**
diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
index 68542d7b5c97d..4158f88f85aa9 100644
--- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
+++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
@@ -1720,6 +1720,18 @@ public function testAppendToFileCreateTheFileIfNotExists()
$this->assertStringEqualsFile($filename, 'bar');
}
+ public function testDumpRemovesTmpFilesOnFailure()
+ {
+ $expected = scandir(__DIR__, \SCANDIR_SORT_ASCENDING);
+
+ try {
+ $this->filesystem->dumpFile(__DIR__.'/Fixtures', 'bar');
+ $this->fail('IOException expected.');
+ } catch (IOException $e) {
+ $this->assertSame($expected, scandir(__DIR__, \SCANDIR_SORT_ASCENDING));
+ }
+ }
+
public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile()
{
$this->markAsSkippedIfChmodIsMissing();
diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php
index 2b13a73ceb42c..10cd62aacd1ba 100644
--- a/src/Symfony/Component/Finder/Finder.php
+++ b/src/Symfony/Component/Finder/Finder.php
@@ -655,7 +655,7 @@ public function append(iterable $iterator)
}
/**
- * Check if the any results were found.
+ * Check if any results were found.
*
* @return bool
*/
diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
index 1c63ecf5f4586..43eaeee4c829c 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
@@ -25,7 +25,6 @@
class FormValidator extends ConstraintValidator
{
private $resolvedGroups;
- private $fieldFormConstraints;
/**
* {@inheritdoc}
@@ -68,7 +67,6 @@ public function validate($form, Constraint $formConstraint)
if ($hasChildren && $form->isRoot()) {
$this->resolvedGroups = new \SplObjectStorage();
- $this->fieldFormConstraints = [];
}
if ($groups instanceof GroupSequence) {
@@ -93,7 +91,6 @@ public function validate($form, Constraint $formConstraint)
$this->resolvedGroups[$field] = (array) $group;
$fieldFormConstraint = new Form();
$fieldFormConstraint->groups = $group;
- $this->fieldFormConstraints[] = $fieldFormConstraint;
$this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath());
$validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint, $group);
}
@@ -139,10 +136,8 @@ public function validate($form, Constraint $formConstraint)
foreach ($form->all() as $field) {
if ($field->isSubmitted()) {
$this->resolvedGroups[$field] = $groups;
- $fieldFormConstraint = new Form();
- $this->fieldFormConstraints[] = $fieldFormConstraint;
$this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath());
- $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint);
+ $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $formConstraint);
}
}
}
@@ -150,7 +145,6 @@ public function validate($form, Constraint $formConstraint)
if ($hasChildren && $form->isRoot()) {
// destroy storage to avoid memory leaks
$this->resolvedGroups = new \SplObjectStorage();
- $this->fieldFormConstraints = [];
}
} elseif (!$form->isSynchronized()) {
$childrenSynchronized = true;
@@ -159,11 +153,8 @@ public function validate($form, Constraint $formConstraint)
foreach ($form as $child) {
if (!$child->isSynchronized()) {
$childrenSynchronized = false;
-
- $fieldFormConstraint = new Form();
- $this->fieldFormConstraints[] = $fieldFormConstraint;
$this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath());
- $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $fieldFormConstraint);
+ $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $formConstraint);
}
}
diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json
index b17ef9804901d..8d9d8f9623588 100644
--- a/src/Symfony/Component/Form/composer.json
+++ b/src/Symfony/Component/Form/composer.json
@@ -29,7 +29,7 @@
},
"require-dev": {
"doctrine/collections": "~1.0",
- "symfony/validator": "^4.4.12|^5.1.6",
+ "symfony/validator": "^4.4.17|^5.1.9",
"symfony/dependency-injection": "^4.4|^5.0",
"symfony/expression-language": "^4.4|^5.0",
"symfony/config": "^4.4|^5.0",
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index 34cd4924656ff..7df9a962be21e 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -595,7 +595,7 @@ public function overrideGlobals()
public static function setTrustedProxies(array $proxies, int $trustedHeaderSet)
{
if (self::HEADER_X_FORWARDED_ALL === $trustedHeaderSet) {
- trigger_deprecation('symfony/http-fundation', '5.2', 'The "HEADER_X_FORWARDED_ALL" constant is deprecated, use either "HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO" or "HEADER_X_FORWARDED_AWS_ELB" or "HEADER_X_FORWARDED_TRAEFIK" constants instead.');
+ trigger_deprecation('symfony/http-foundation', '5.2', 'The "HEADER_X_FORWARDED_ALL" constant is deprecated, use either "HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO" or "HEADER_X_FORWARDED_AWS_ELB" or "HEADER_X_FORWARDED_TRAEFIK" constants instead.');
}
self::$trustedProxies = array_reduce($proxies, function ($proxies, $proxy) {
if ('REMOTE_ADDR' !== $proxy) {
@@ -1907,9 +1907,15 @@ protected function prepareBaseUrl()
}
$basename = basename($baseUrl);
- if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
- // no match whatsoever; set it blank
- return '';
+ if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri).'/', '/'.$basename.'/')) {
+ // strip autoindex filename, for virtualhost based on URL path
+ $baseUrl = \dirname($baseUrl).'/';
+
+ $basename = basename($baseUrl);
+ if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri).'/', '/'.$basename.'/')) {
+ // no match whatsoever; set it blank
+ return '';
+ }
}
// If using mod_rewrite or ISAPI_Rewrite strip the script filename
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index 6b01446c2f611..0b9cfefa27699 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
@@ -1785,6 +1785,36 @@ public function getBaseUrlData()
'/foo',
'/bar+baz',
],
+ [
+ '/sub/foo/bar',
+ [
+ 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php',
+ 'SCRIPT_NAME' => '/foo/app.php',
+ 'PHP_SELF' => '/foo/app.php',
+ ],
+ '/sub/foo',
+ '/bar',
+ ],
+ [
+ '/sub/foo/app.php/bar',
+ [
+ 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php',
+ 'SCRIPT_NAME' => '/foo/app.php',
+ 'PHP_SELF' => '/foo/app.php',
+ ],
+ '/sub/foo/app.php',
+ '/bar',
+ ],
+ [
+ '/sub/foo/bar/baz',
+ [
+ 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app2.phpx',
+ 'SCRIPT_NAME' => '/foo/app2.phpx',
+ 'PHP_SELF' => '/foo/app2.phpx',
+ ],
+ '/sub/foo',
+ '/bar/baz',
+ ],
];
}
@@ -2473,7 +2503,7 @@ public function preferSafeContentData()
*/
public function testXForwarededAllConstantDeprecated()
{
- $this->expectDeprecation('Since symfony/http-fundation 5.2: The "HEADER_X_FORWARDED_ALL" constant is deprecated, use either "HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO" or "HEADER_X_FORWARDED_AWS_ELB" or "HEADER_X_FORWARDED_TRAEFIK" constants instead.');
+ $this->expectDeprecation('Since symfony/http-foundation 5.2: The "HEADER_X_FORWARDED_ALL" constant is deprecated, use either "HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO" or "HEADER_X_FORWARDED_AWS_ELB" or "HEADER_X_FORWARDED_TRAEFIK" constants instead.');
Request::setTrustedProxies([], Request::HEADER_X_FORWARDED_ALL);
}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 5f604808da955..aa069bb7e952e 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -74,12 +74,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private static $freshCache = [];
- const VERSION = '5.2.0-RC1';
+ const VERSION = '5.2.0-RC2';
const VERSION_ID = 50200;
const MAJOR_VERSION = 5;
const MINOR_VERSION = 2;
const RELEASE_VERSION = 0;
- const EXTRA_VERSION = 'RC1';
+ const EXTRA_VERSION = 'RC2';
const END_OF_MAINTENANCE = '07/2021';
const END_OF_LIFE = '07/2021';
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
index fd67af368b740..62179ba154fcd 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
@@ -239,6 +239,7 @@ public function testResponseIsExpirableButNotValidateableWhenMasterResponseCombi
}
/**
+ * @group time-sensitive
* @dataProvider cacheControlMergingProvider
*/
public function testCacheControlMerging(array $expects, array $master, array $surrogates)
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
index 33d3bb957f808..7aa9789e98b95 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
@@ -151,8 +151,9 @@ public function applyOperations(string $dn, iterable $operations): void
$operationsMapped[] = $modification->toArray();
}
- if (!@ldap_modify_batch($this->getConnectionResource(), $dn, $operationsMapped)) {
- throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": ', $dn).ldap_error($this->getConnectionResource()), ldap_errno($con));
+ $con = $this->getConnectionResource();
+ if (!@ldap_modify_batch($con, $dn, $operationsMapped)) {
+ throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": ', $dn).ldap_error($con), ldap_errno($con));
}
}
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
index 6af544fb5b5c6..497c8313156ba 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
@@ -100,7 +100,7 @@ public function execute()
$cookie = '';
do {
if ($pageControl) {
- $this->controlPagedResult($con, $pageSize, $cookie);
+ $this->controlPagedResult($con, $pageSize, true, $cookie);
}
$sizeLimit = $itemsLeft;
if ($pageSize > 0 && $sizeLimit >= $pageSize) {
@@ -174,7 +174,7 @@ public function getResources(): array
private function resetPagination()
{
$con = $this->connection->getResource();
- $this->controlPagedResult($con, 0, '');
+ $this->controlPagedResult($con, 0, false, '');
$this->serverctrls = [];
// This is a workaround for a bit of a bug in the above invocation
@@ -204,15 +204,15 @@ private function resetPagination()
*
* @param resource $con
*/
- private function controlPagedResult($con, int $pageSize, string $cookie): bool
+ private function controlPagedResult($con, int $pageSize, bool $critical, string $cookie): bool
{
if (\PHP_VERSION_ID < 70300) {
- return ldap_control_paged_result($con, $pageSize, true, $cookie);
+ return ldap_control_paged_result($con, $pageSize, $critical, $cookie);
}
$this->serverctrls = [
[
'oid' => \LDAP_CONTROL_PAGEDRESULTS,
- 'isCritical' => true,
+ 'isCritical' => $critical,
'value' => [
'size' => $pageSize,
'cookie' => $cookie,
diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php
index 0c2253ee00142..566ba215dc6d8 100644
--- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php
+++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php
@@ -22,15 +22,10 @@
/**
* @requires extension ldap
+ * @group integration
*/
class AdapterTest extends LdapTestCase
{
- private const PAGINATION_REQUIRED_CONFIG = [
- 'options' => [
- 'protocol_version' => 3,
- ],
- ];
-
public function testLdapEscape()
{
$ldap = new Adapter();
@@ -122,7 +117,7 @@ public function testLdapQueryScopeOneLevel()
public function testLdapPagination()
{
- $ldap = new Adapter(array_merge($this->getLdapConfig(), static::PAGINATION_REQUIRED_CONFIG));
+ $ldap = new Adapter($this->getLdapConfig());
$ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony');
$entries = $this->setupTestUsers($ldap);
@@ -153,7 +148,7 @@ public function testLdapPagination()
$this->assertEquals(\count($fully_paged_query->getResources()), 1);
$this->assertEquals(\count($paged_query->getResources()), 5);
- if (\PHP_MAJOR_VERSION > 7 || (\PHP_MAJOR_VERSION == 7 && \PHP_MINOR_VERSION >= 2)) {
+ if (\PHP_VERSION_ID >= 70200) {
// This last query is to ensure that we haven't botched the state of our connection
// by not resetting pagination properly. extldap <= PHP 7.1 do not implement the necessary
// bits to work around an implementation flaw, so we simply can't guarantee this to work there.
@@ -205,7 +200,7 @@ private function destroyEntries($ldap, $entries)
public function testLdapPaginationLimits()
{
- $ldap = new Adapter(array_merge($this->getLdapConfig(), static::PAGINATION_REQUIRED_CONFIG));
+ $ldap = new Adapter($this->getLdapConfig());
$ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony');
$entries = $this->setupTestUsers($ldap);
diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php
index e23e8018547bf..bfdd7d5342ff5 100644
--- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php
+++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php
@@ -15,7 +15,6 @@
use Symfony\Component\Ldap\Adapter\ExtLdap\Collection;
use Symfony\Component\Ldap\Adapter\ExtLdap\UpdateOperation;
use Symfony\Component\Ldap\Entry;
-use Symfony\Component\Ldap\Exception\AlreadyExistsException;
use Symfony\Component\Ldap\Exception\LdapException;
use Symfony\Component\Ldap\Exception\NotBoundException;
use Symfony\Component\Ldap\Exception\UpdateOperationException;
@@ -23,6 +22,7 @@
/**
* @requires extension ldap
+ * @group integration
*/
class LdapManagerTest extends LdapTestCase
{
@@ -82,7 +82,7 @@ public function testLdapAddInvalidEntry()
*/
public function testLdapAddDouble()
{
- $this->expectException(AlreadyExistsException::class);
+ $this->expectException(LdapException::class);
$this->executeSearchQuery(1);
$entry = new Entry('cn=Elsa Amrouche,dc=symfony,dc=com', [
@@ -94,7 +94,11 @@ public function testLdapAddDouble()
$em = $this->adapter->getEntryManager();
$em->add($entry);
- $em->add($entry);
+ try {
+ $em->add($entry);
+ } finally {
+ $em->remove($entry);
+ }
}
/**
@@ -210,11 +214,12 @@ public function testLdapRenameWithoutRemovingOldRdn()
$newEntry = $result[0];
$originalCN = $entry->getAttribute('cn')[0];
- $this->assertStringContainsString($originalCN, $newEntry->getAttribute('cn'));
-
- $entryManager->rename($newEntry, 'cn='.$originalCN);
-
- $this->executeSearchQuery(1);
+ try {
+ $this->assertContains($originalCN, $newEntry->getAttribute('cn'));
+ $this->assertContains('Kevin', $newEntry->getAttribute('cn'));
+ } finally {
+ $entryManager->rename($newEntry, 'cn='.$originalCN);
+ }
}
public function testLdapAddRemoveAttributeValues()
@@ -372,7 +377,7 @@ public function testLdapMove()
$result = $this->executeSearchQuery(1);
$entry = $result[0];
- $this->assertNotContains('ou=Ldap', $entry->getDn());
+ $this->assertStringNotContainsString('ou=Ldap', $entry->getDn());
$entryManager = $this->adapter->getEntryManager();
$entryManager->move($entry, 'ou=Ldap,ou=Components,dc=symfony,dc=com');
@@ -380,5 +385,8 @@ public function testLdapMove()
$result = $this->executeSearchQuery(1);
$movedEntry = $result[0];
$this->assertStringContainsString('ou=Ldap', $movedEntry->getDn());
+
+ // Move back entry
+ $entryManager->move($movedEntry, 'dc=symfony,dc=com');
}
}
diff --git a/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf b/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf
deleted file mode 100644
index 24eebcb2dbe33..0000000000000
--- a/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf
+++ /dev/null
@@ -1,18 +0,0 @@
-# See slapd.conf(5) for details on configuration options.
-include /etc/ldap/schema/core.schema
-include /etc/ldap/schema/cosine.schema
-include /etc/ldap/schema/inetorgperson.schema
-include /etc/ldap/schema/nis.schema
-
-pidfile /tmp/slapd/slapd.pid
-argsfile /tmp/slapd/slapd.args
-
-modulepath /tmp/slapd-modules
-moduleload back_hdb
-
-database hdb
-directory /tmp/slapd
-
-suffix "dc=symfony,dc=com"
-rootdn "cn=admin,dc=symfony,dc=com"
-rootpw {SSHA}btWUi971ytYpVMbZLkaQ2A6ETh3VA0lL
diff --git a/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif b/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif
deleted file mode 100644
index 25abb296c9a6c..0000000000000
--- a/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif
+++ /dev/null
@@ -1,4 +0,0 @@
-dn: dc=symfony,dc=com
-objectClass: dcObject
-objectClass: organizationalUnit
-ou: Organization
diff --git a/src/Symfony/Component/Ldap/Tests/LdapTestCase.php b/src/Symfony/Component/Ldap/Tests/LdapTestCase.php
index 9a1424a62e8f7..606065e491e36 100644
--- a/src/Symfony/Component/Ldap/Tests/LdapTestCase.php
+++ b/src/Symfony/Component/Ldap/Tests/LdapTestCase.php
@@ -9,6 +9,7 @@ class LdapTestCase extends TestCase
protected function getLdapConfig()
{
$h = @ldap_connect(getenv('LDAP_HOST'), getenv('LDAP_PORT'));
+ @ldap_set_option($h, LDAP_OPT_PROTOCOL_VERSION, 3);
if (!$h || !@ldap_bind($h)) {
$this->markTestSkipped('No server is listening on LDAP_HOST:LDAP_PORT');
diff --git a/src/Symfony/Component/Ldap/phpunit.xml.dist b/src/Symfony/Component/Ldap/phpunit.xml.dist
index e9861f822170b..a679848078856 100644
--- a/src/Symfony/Component/Ldap/phpunit.xml.dist
+++ b/src/Symfony/Component/Ldap/phpunit.xml.dist
@@ -10,7 +10,7 @@
>
-
+
diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md
index b6e7ca04dcd68..566625a7c1f7c 100644
--- a/src/Symfony/Component/Security/CHANGELOG.md
+++ b/src/Symfony/Component/Security/CHANGELOG.md
@@ -15,6 +15,7 @@ CHANGELOG
* Added `LoginThrottlingListener`.
* Added `LoginLinkAuthenticator`.
* Moved methods `supports()` and `authenticate()` from `AbstractListener` to `FirewallListenerInterface`.
+ * [BC break] `PasswordUpgradeBadge::getPasswordUpgrader()` changed its return type to return null or a `PasswordUpgraderInterface` implementation.
5.1.0
-----
diff --git a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php
index 5d88513af7ca0..cfffe9d307b78 100644
--- a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php
+++ b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php
@@ -30,10 +30,10 @@ class PasswordUpgradeBadge implements BadgeInterface
private $passwordUpgrader;
/**
- * @param string $plaintextPassword The presented password, used in the rehash
- * @param PasswordUpgraderInterface $passwordUpgrader The password upgrader, usually the UserProvider
+ * @param string $plaintextPassword The presented password, used in the rehash
+ * @param PasswordUpgraderInterface|null $passwordUpgrader The password upgrader, defaults to the UserProvider if null
*/
- public function __construct(string $plaintextPassword, PasswordUpgraderInterface $passwordUpgrader)
+ public function __construct(string $plaintextPassword, ?PasswordUpgraderInterface $passwordUpgrader = null)
{
$this->plaintextPassword = $plaintextPassword;
$this->passwordUpgrader = $passwordUpgrader;
@@ -51,7 +51,7 @@ public function getAndErasePlaintextPassword(): string
return $password;
}
- public function getPasswordUpgrader(): PasswordUpgraderInterface
+ public function getPasswordUpgrader(): ?PasswordUpgraderInterface
{
return $this->passwordUpgrader;
}
diff --git a/src/Symfony/Component/Security/Http/EventListener/PasswordMigratingListener.php b/src/Symfony/Component/Security/Http/EventListener/PasswordMigratingListener.php
index 140552b3f3097..d667b5826eaca 100644
--- a/src/Symfony/Component/Security/Http/EventListener/PasswordMigratingListener.php
+++ b/src/Symfony/Component/Security/Http/EventListener/PasswordMigratingListener.php
@@ -13,7 +13,9 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
+use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
+use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
@@ -53,7 +55,19 @@ public function onLoginSuccess(LoginSuccessEvent $event): void
return;
}
- $badge->getPasswordUpgrader()->upgradePassword($user, $passwordEncoder->encodePassword($plaintextPassword, $user->getSalt()));
+ $passwordUpgrader = $badge->getPasswordUpgrader();
+ if (null === $passwordUpgrader) {
+ /** @var UserBadge $userBadge */
+ $userBadge = $passport->getBadge(UserBadge::class);
+ $userLoader = $userBadge->getUserLoader();
+ if (\is_array($userLoader) && $userLoader[0] instanceof PasswordUpgraderInterface) {
+ $passwordUpgrader = $userLoader[0];
+ } else {
+ return;
+ }
+ }
+
+ $passwordUpgrader->upgradePassword($user, $passwordEncoder->encodePassword($plaintextPassword, $user->getSalt()));
}
public static function getSubscribedEvents(): array
diff --git a/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php b/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php
index cb0d8fcdae114..5b862e6c0a755 100644
--- a/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php
+++ b/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php
@@ -16,6 +16,9 @@
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
/**
+ * Configures the user provider as user loader, if no user load
+ * has been explicitly set.
+ *
* @author Wouter de Jong
*
* @final
diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php
index bf90aa3be6d5e..42607ca853328 100644
--- a/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php
@@ -18,6 +18,7 @@
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
@@ -34,9 +35,14 @@ class PasswordMigratingListenerTest extends TestCase
protected function setUp(): void
{
+ $this->user = $this->createMock(UserInterface::class);
+ $this->user->expects($this->any())->method('getPassword')->willReturn('old-encoded-password');
+ $encoder = $this->createMock(PasswordEncoderInterface::class);
+ $encoder->expects($this->any())->method('needsRehash')->willReturn(true);
+ $encoder->expects($this->any())->method('encodePassword')->with('pa$$word', null)->willReturn('new-encoded-password');
$this->encoderFactory = $this->createMock(EncoderFactoryInterface::class);
+ $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->user)->willReturn($encoder);
$this->listener = new PasswordMigratingListener($this->encoderFactory);
- $this->user = $this->createMock(UserInterface::class);
}
/**
@@ -61,16 +67,8 @@ public function provideUnsupportedEvents()
yield [$this->createEvent($this->createMock(PassportInterface::class))];
}
- public function testUpgrade()
+ public function testUpgradeWithUpgrader()
{
- $encoder = $this->createMock(PasswordEncoderInterface::class);
- $encoder->expects($this->any())->method('needsRehash')->willReturn(true);
- $encoder->expects($this->any())->method('encodePassword')->with('pa$$word', null)->willReturn('new-encoded-password');
-
- $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->user)->willReturn($encoder);
-
- $this->user->expects($this->any())->method('getPassword')->willReturn('old-encoded-password');
-
$passwordUpgrader = $this->createPasswordUpgrader();
$passwordUpgrader->expects($this->once())
->method('upgradePassword')
@@ -81,6 +79,20 @@ public function testUpgrade()
$this->listener->onLoginSuccess($event);
}
+ public function testUpgradeWithoutUpgrader()
+ {
+ $userLoader = $this->createMock(MigratingUserProvider::class);
+ $userLoader->expects($this->any())->method('loadUserByUsername')->willReturn($this->user);
+
+ $userLoader->expects($this->once())
+ ->method('upgradePassword')
+ ->with($this->user, 'new-encoded-password')
+ ;
+
+ $event = $this->createEvent(new SelfValidatingPassport(new UserBadge('test', [$userLoader, 'loadUserByUsername']), [new PasswordUpgradeBadge('pa$$word')]));
+ $this->listener->onLoginSuccess($event);
+ }
+
private function createPasswordUpgrader()
{
return $this->createMock(PasswordUpgraderInterface::class);
@@ -91,3 +103,7 @@ private function createEvent(PassportInterface $passport)
return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), $passport, $this->createMock(TokenInterface::class), new Request(), null, 'main');
}
}
+
+abstract class MigratingUserProvider implements UserProviderInterface, PasswordUpgraderInterface
+{
+}
diff --git a/src/Symfony/Component/Validator/Constraints/IsinValidator.php b/src/Symfony/Component/Validator/Constraints/IsinValidator.php
index 9ae31acb14669..d5e4d9dbc9736 100644
--- a/src/Symfony/Component/Validator/Constraints/IsinValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/IsinValidator.php
@@ -15,7 +15,6 @@
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
-use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* @author Laurent Masforné
@@ -24,16 +23,6 @@
*/
class IsinValidator extends ConstraintValidator
{
- /**
- * @var ValidatorInterface
- */
- private $validator;
-
- public function __construct(ValidatorInterface $validator)
- {
- $this->validator = $validator;
- }
-
/**
* {@inheritdoc}
*/
@@ -87,6 +76,6 @@ private function isCorrectChecksum(string $input): bool
}
$number = implode('', $characters);
- return 0 === $this->validator->validate($number, new Luhn())->count();
+ return 0 === $this->context->getValidator()->validate($number, new Luhn())->count();
}
}
diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php
index 74ed391034f15..4560793eb5da8 100644
--- a/src/Symfony/Component/Validator/Context/ExecutionContext.php
+++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php
@@ -128,6 +128,7 @@ class ExecutionContext implements ExecutionContextInterface
* @var array
*/
private $initializedObjects;
+ private $cachedObjectsRefs;
/**
* @param mixed $root The root value of the validated object graph
@@ -141,6 +142,7 @@ public function __construct(ValidatorInterface $validator, $root, TranslatorInte
$this->translator = $translator;
$this->translationDomain = $translationDomain;
$this->violations = new ConstraintViolationList();
+ $this->cachedObjectsRefs = new \SplObjectStorage();
}
/**
@@ -346,4 +348,20 @@ public function isObjectInitialized(string $cacheKey): bool
{
return isset($this->initializedObjects[$cacheKey]);
}
+
+ /**
+ * @internal
+ *
+ * @param object $object
+ *
+ * @return string
+ */
+ public function generateCacheKey($object)
+ {
+ if (!isset($this->cachedObjectsRefs[$object])) {
+ $this->cachedObjectsRefs[$object] = spl_object_hash($object);
+ }
+
+ return $this->cachedObjectsRefs[$object];
+ }
}
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IsinValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IsinValidatorTest.php
index 0822fb5ad65f9..0a219401bcbe4 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/IsinValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/IsinValidatorTest.php
@@ -4,16 +4,14 @@
use Symfony\Component\Validator\Constraints\Isin;
use Symfony\Component\Validator\Constraints\IsinValidator;
+use Symfony\Component\Validator\Constraints\Luhn;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
-use Symfony\Component\Validator\ValidatorBuilder;
class IsinValidatorTest extends ConstraintValidatorTestCase
{
protected function createValidator()
{
- $validatorBuilder = new ValidatorBuilder();
-
- return new IsinValidator($validatorBuilder->getValidator());
+ return new IsinValidator();
}
public function testNullIsValid()
@@ -36,6 +34,7 @@ public function testEmptyStringIsValid()
public function testValidIsin($isin)
{
$this->validator->validate($isin, new Isin());
+ $this->expectViolationsAt(0, $isin, new Luhn());
$this->assertNoViolation();
}
@@ -103,6 +102,7 @@ public function getIsinWithInvalidPattern()
*/
public function testIsinWithValidFormatButIncorrectChecksum($isin)
{
+ $this->expectViolationsAt(0, $isin, new Luhn());
$this->assertViolationRaised($isin, Isin::INVALID_CHECKSUM_ERROR);
}
diff --git a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
index f543ddf7d6c86..6866a88084d90 100644
--- a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
@@ -13,12 +13,15 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\IdentityTranslator;
+use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\All;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\Cascade;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\Expression;
use Symfony\Component\Validator\Constraints\GroupSequence;
+use Symfony\Component\Validator\Constraints\IsFalse;
+use Symfony\Component\Validator\Constraints\IsNull;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
@@ -28,6 +31,7 @@
use Symfony\Component\Validator\Constraints\Traverse;
use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Constraints\Valid;
+use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\Context\ExecutionContextFactory;
@@ -2330,4 +2334,66 @@ public function testValidateWithExplicitCascade()
CascadingEntity::$staticChild = null;
}
+
+ public function testValidatedConstraintsHashesDoNotCollide()
+ {
+ $metadata = new ClassMetadata(Entity::class);
+ $metadata->addPropertyConstraint('initialized', new NotNull(['groups' => 'should_pass']));
+ $metadata->addPropertyConstraint('initialized', new IsNull(['groups' => 'should_fail']));
+
+ $this->metadataFactory->addMetadata($metadata);
+
+ $entity = new Entity();
+ $entity->data = new \stdClass();
+
+ $this->assertCount(2, $this->validator->validate($entity, new TestConstraintHashesDoNotCollide()));
+ }
+
+ public function testValidatedConstraintsHashesDoNotCollideWithSameConstraintValidatingDifferentProperties()
+ {
+ $value = new \stdClass();
+
+ $entity = new Entity();
+ $entity->firstName = $value;
+ $entity->setLastName($value);
+
+ $validator = $this->validator->startContext($entity);
+
+ $constraint = new IsNull();
+ $validator->atPath('firstName')
+ ->validate($entity->firstName, $constraint);
+ $validator->atPath('lastName')
+ ->validate($entity->getLastName(), $constraint);
+
+ $this->assertCount(2, $validator->getViolations());
+ }
+}
+
+final class TestConstraintHashesDoNotCollide extends Constraint
+{
+}
+
+final class TestConstraintHashesDoNotCollideValidator extends ConstraintValidator
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($value, Constraint $constraint)
+ {
+ if (!$value instanceof Entity) {
+ throw new \LogicException();
+ }
+
+ $this->context->getValidator()
+ ->inContext($this->context)
+ ->atPath('data')
+ ->validate($value, new NotNull())
+ ->validate($value, new NotNull())
+ ->validate($value, new IsFalse());
+
+ $this->context->getValidator()
+ ->inContext($this->context)
+ ->validate($value, null, new GroupSequence(['should_pass']))
+ ->validate($value, null, new GroupSequence(['should_fail']));
+ }
}
diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
index eaa1309fd0ff3..b1f1384ffbd16 100644
--- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
+++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
@@ -108,7 +108,7 @@ public function validate($value, $constraints = null, $groups = null)
$this->validateGenericNode(
$value,
$previousObject,
- \is_object($value) ? spl_object_hash($value) : null,
+ \is_object($value) ? $this->generateCacheKey($value) : null,
$metadata,
$this->defaultPropertyPath,
$groups,
@@ -176,7 +176,7 @@ public function validateProperty($object, string $propertyName, $groups = null)
$propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName);
$groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
- $cacheKey = spl_object_hash($object);
+ $cacheKey = $this->generateCacheKey($object);
$propertyPath = PropertyPath::append($this->defaultPropertyPath, $propertyName);
$previousValue = $this->context->getValue();
@@ -224,7 +224,7 @@ public function validatePropertyValue($objectOrClass, string $propertyName, $val
if (\is_object($objectOrClass)) {
$object = $objectOrClass;
$class = \get_class($object);
- $cacheKey = spl_object_hash($objectOrClass);
+ $cacheKey = $this->generateCacheKey($objectOrClass);
$propertyPath = PropertyPath::append($this->defaultPropertyPath, $propertyName);
} else {
// $objectOrClass contains a class name
@@ -311,7 +311,7 @@ private function validateObject($object, string $propertyPath, array $groups, in
$this->validateClassNode(
$object,
- spl_object_hash($object),
+ $this->generateCacheKey($object),
$classMetadata,
$propertyPath,
$groups,
@@ -425,7 +425,7 @@ private function validateClassNode(object $object, ?string $cacheKey, ClassMetad
$defaultOverridden = false;
// Use the object hash for group sequences
- $groupHash = \is_object($group) ? spl_object_hash($group) : $group;
+ $groupHash = \is_object($group) ? $this->generateCacheKey($group, true) : $group;
if ($context->isGroupValidated($cacheKey, $groupHash)) {
// Skip this group when validating the properties and when
@@ -730,7 +730,7 @@ private function validateInGroup($value, ?string $cacheKey, MetadataInterface $m
// Prevent duplicate validation of constraints, in the case
// that constraints belong to multiple validated groups
if (null !== $cacheKey) {
- $constraintHash = spl_object_hash($constraint);
+ $constraintHash = $this->generateCacheKey($constraint, true);
// instanceof Valid: In case of using a Valid constraint with many groups
// it makes a reference object get validated by each group
if ($constraint instanceof Composite || $constraint instanceof Valid) {
@@ -762,4 +762,22 @@ private function validateInGroup($value, ?string $cacheKey, MetadataInterface $m
}
}
}
+
+ /**
+ * @param object $object
+ */
+ private function generateCacheKey($object, bool $dependsOnPropertyPath = false): string
+ {
+ if ($this->context instanceof ExecutionContext) {
+ $cacheKey = $this->context->generateCacheKey($object);
+ } else {
+ $cacheKey = spl_object_hash($object);
+ }
+
+ if ($dependsOnPropertyPath) {
+ $cacheKey .= $this->context->getPropertyPath();
+ }
+
+ return $cacheKey;
+ }
}
diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php
index 690c20298365d..f98fcd69795c8 100644
--- a/src/Symfony/Component/Yaml/Parser.php
+++ b/src/Symfony/Component/Yaml/Parser.php
@@ -1190,27 +1190,6 @@ private function parseQuotedString(string $yaml): ?string
}
return $value;
-
- for ($i = 1; isset($yaml[$i]) && $quotation !== $yaml[$i]; ++$i) {
- }
-
- // quoted single line string
- if (isset($yaml[$i]) && $quotation === $yaml[$i]) {
- return $yaml;
- }
-
- $lines = [$yaml];
-
- while ($this->moveToNextLine()) {
- for ($i = 1; isset($this->currentLine[$i]) && $quotation !== $this->currentLine[$i]; ++$i) {
- }
-
- $lines[] = trim($this->currentLine);
-
- if (isset($this->currentLine[$i]) && $quotation === $this->currentLine[$i]) {
- break;
- }
- }
}
private function lexInlineMapping(string $yaml): string