diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php
index b17cc0a44f9e8..1f5f472802e7a 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php
@@ -118,6 +118,39 @@ public function testWritesResponseEvenIfXContentDigestIsPresent()
$this->assertNotNull($response);
}
+ public function testWritingARestoredResponseDoesNotCorruptCache()
+ {
+ /*
+ * This covers the regression reported in https://github.com/symfony/symfony/issues/37174.
+ *
+ * A restored response does *not* load the body, but only keep the file path in a special X-Body-File
+ * header. For reasons (?), the file path was also used as the restored response body.
+ * It would be up to others (HttpCache...?) to honor this header and actually load the response content
+ * from there.
+ *
+ * When a restored response was stored again, the Store itself would ignore the header. In the first
+ * step, this would compute a new Content Digest based on the file path in the restored response body;
+ * this is covered by "Checkpoint 1" below. But, since the X-Body-File header was left untouched (Checkpoint 2), downstream
+ * code (HttpCache...) would not immediately notice.
+ *
+ * Only upon performing the lookup for a second time, we'd get a Response where the (wrong) Content Digest
+ * is also reflected in the X-Body-File header, this time also producing wrong content when the downstream
+ * evaluates it.
+ */
+ $this->store->write($this->request, $this->response);
+ $digest = $this->response->headers->get('X-Content-Digest');
+ $path = $this->getStorePath($digest);
+
+ $response = $this->store->lookup($this->request);
+ $this->store->write($this->request, $response);
+ $this->assertEquals($digest, $response->headers->get('X-Content-Digest')); // Checkpoint 1
+ $this->assertEquals($path, $response->headers->get('X-Body-File')); // Checkpoint 2
+
+ $response = $this->store->lookup($this->request);
+ $this->assertEquals($digest, $response->headers->get('X-Content-Digest'));
+ $this->assertEquals($path, $response->headers->get('X-Body-File'));
+ }
+
public function testFindsAStoredEntryWithLookup()
{
$this->storeSimpleEntry();
diff --git a/src/Symfony/Component/Ldap/Security/LdapUserProvider.php b/src/Symfony/Component/Ldap/Security/LdapUserProvider.php
index 28bd1d4c68926..c593a1376e5da 100644
--- a/src/Symfony/Component/Ldap/Security/LdapUserProvider.php
+++ b/src/Symfony/Component/Ldap/Security/LdapUserProvider.php
@@ -108,7 +108,7 @@ public function refreshUser(UserInterface $user)
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user)));
}
- return new LdapUser($user->getEntry(), $user->getUsername(), $user->getPassword(), $user->getRoles());
+ return new LdapUser($user->getEntry(), $user->getUsername(), $user->getPassword(), $user->getRoles(), $user->getExtraFields());
}
/**
diff --git a/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php b/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php
index 8d0a7a3517584..a2e888077cde8 100644
--- a/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php
+++ b/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php
@@ -330,4 +330,14 @@ public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute()
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']);
$this->assertInstanceOf(LdapUser::class, $provider->loadUserByUsername('foo'));
}
+
+ public function testRefreshUserShouldReturnUserWithSameProperties()
+ {
+ $ldap = $this->createMock(LdapInterface::class);
+ $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']);
+
+ $user = new LdapUser(new Entry('foo'), 'foo', 'bar', ['ROLE_DUMMY'], ['email' => 'foo@symfony.com']);
+
+ $this->assertEquals($user, $provider->refreshUser($user));
+ }
}
diff --git a/src/Symfony/Component/Lock/Store/MongoDbStore.php b/src/Symfony/Component/Lock/Store/MongoDbStore.php
index 296c68be1072a..34c740a6685c9 100644
--- a/src/Symfony/Component/Lock/Store/MongoDbStore.php
+++ b/src/Symfony/Component/Lock/Store/MongoDbStore.php
@@ -14,8 +14,8 @@
use MongoDB\BSON\UTCDateTime;
use MongoDB\Client;
use MongoDB\Collection;
-use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\WriteException;
+use MongoDB\Driver\ReadPreference;
use MongoDB\Exception\DriverRuntimeException;
use MongoDB\Exception\InvalidArgumentException as MongoInvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
@@ -54,8 +54,6 @@ class MongoDbStore implements BlockingStoreInterface
private $options;
private $initialTtl;
- private $databaseVersion;
-
use ExpiringStoreTrait;
/**
@@ -87,8 +85,8 @@ class MongoDbStore implements BlockingStoreInterface
* to 0.0 and optionally leverage
* self::createTtlIndex(int $expireAfterSeconds = 0).
*
- * writeConcern, readConcern and readPreference are not specified by
- * MongoDbStore meaning the collection's settings will take effect.
+ * writeConcern and readConcern are not specified by MongoDbStore meaning the connection's settings will take effect.
+ * readPreference is primary for all queries.
* @see https://docs.mongodb.com/manual/applications/replication/
*/
public function __construct($mongo, array $options = [], float $initialTtl = 300.0)
@@ -262,6 +260,8 @@ public function exists(Key $key): bool
'expires_at' => [
'$gt' => $this->createMongoDateTime(microtime(true)),
],
+ ], [
+ 'readPreference' => new ReadPreference(\defined(ReadPreference::PRIMARY) ? ReadPreference::PRIMARY : ReadPreference::RP_PRIMARY),
]);
}
@@ -315,25 +315,6 @@ private function isDuplicateKeyException(WriteException $e): bool
return 11000 === $code;
}
- private function getDatabaseVersion(): string
- {
- if (null !== $this->databaseVersion) {
- return $this->databaseVersion;
- }
-
- $command = new Command([
- 'buildinfo' => 1,
- ]);
- $cursor = $this->getCollection()->getManager()->executeReadCommand(
- $this->getCollection()->getDatabaseName(),
- $command
- );
- $buildInfo = $cursor->toArray()[0];
- $this->databaseVersion = $buildInfo->version;
-
- return $this->databaseVersion;
- }
-
private function getCollection(): Collection
{
if (null !== $this->collection) {
diff --git a/src/Symfony/Component/Lock/Store/PdoStore.php b/src/Symfony/Component/Lock/Store/PdoStore.php
index b792d4d9ca979..fb44803681adf 100644
--- a/src/Symfony/Component/Lock/Store/PdoStore.php
+++ b/src/Symfony/Component/Lock/Store/PdoStore.php
@@ -13,6 +13,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\Lock\Exception\InvalidArgumentException;
@@ -158,10 +159,10 @@ public function putOffExpiration(Key $key, float $ttl)
$stmt->bindValue(':id', $this->getHashedKey($key));
$stmt->bindValue(':token1', $uniqueToken);
$stmt->bindValue(':token2', $uniqueToken);
- $stmt->execute();
+ $result = $stmt->execute();
// If this method is called twice in the same second, the row wouldn't be updated. We have to call exists to know if we are the owner
- if (!$stmt->rowCount() && !$this->exists($key)) {
+ if (!($result instanceof Result ? $result : $stmt)->rowCount() && !$this->exists($key)) {
throw new LockConflictedException();
}
@@ -191,9 +192,9 @@ public function exists(Key $key)
$stmt->bindValue(':id', $this->getHashedKey($key));
$stmt->bindValue(':token', $this->getUniqueToken($key));
- $stmt->execute();
+ $result = $stmt->execute();
- return (bool) (method_exists($stmt, 'fetchOne') ? $stmt->fetchOne() : $stmt->fetchColumn());
+ return (bool) ($result instanceof Result ? $result->fetchOne() : $stmt->fetchColumn());
}
/**
diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
index 0a7fde06ce6d6..45ccd65cdf13f 100644
--- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
@@ -115,6 +115,9 @@ private function getPayload(Email $email, Envelope $envelope): array
if ($email->getHtmlBody()) {
$payload['Message.Body.Html.Data'] = $email->getHtmlBody();
}
+ if ($email->getReplyTo()) {
+ $payload['ReplyToAddresses.member'] = $this->stringifyAddresses($email->getReplyTo());
+ }
return $payload;
}
diff --git a/src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php b/src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php
index 0472c36b6209a..b06ac839c64f7 100644
--- a/src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php
+++ b/src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php
@@ -22,9 +22,6 @@ class SendEmailMessage
private $message;
private $envelope;
- /**
- * @internal
- */
public function __construct(RawMessage $message, Envelope $envelope = null)
{
$this->message = $message;
diff --git a/src/Symfony/Component/Mailer/Tests/TransportTest.php b/src/Symfony/Component/Mailer/Tests/TransportTest.php
index 95eb5b6ebf03a..dfd8d1926e61a 100644
--- a/src/Symfony/Component/Mailer/Tests/TransportTest.php
+++ b/src/Symfony/Component/Mailer/Tests/TransportTest.php
@@ -60,6 +60,22 @@ public function fromStringProvider(): iterable
];
}
+ /**
+ * @dataProvider fromDsnProvider
+ */
+ public function testFromDsn(string $dsn, TransportInterface $transport): void
+ {
+ $this->assertEquals($transport, Transport::fromDsn($dsn));
+ }
+
+ public function fromDsnProvider(): iterable
+ {
+ yield 'multiple transports' => [
+ 'failover(smtp://a smtp://b)',
+ new FailoverTransport([new Transport\Smtp\EsmtpTransport('a'), new Transport\Smtp\EsmtpTransport('b')]),
+ ];
+ }
+
/**
* @dataProvider fromWrongStringProvider
*/
diff --git a/src/Symfony/Component/Mailer/Transport.php b/src/Symfony/Component/Mailer/Transport.php
index b30ba6fbdc6ed..5d5c7e0b6eb8c 100644
--- a/src/Symfony/Component/Mailer/Transport.php
+++ b/src/Symfony/Component/Mailer/Transport.php
@@ -51,7 +51,7 @@ class Transport
public static function fromDsn(string $dsn, EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null): TransportInterface
{
- $factory = new self(self::getDefaultFactories($dispatcher, $client, $logger));
+ $factory = new self(iterator_to_array(self::getDefaultFactories($dispatcher, $client, $logger)));
return $factory->fromString($dsn);
}
diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsIntegrationTest.php
index 3c47be5614751..c840822abea40 100644
--- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsIntegrationTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsIntegrationTest.php
@@ -45,7 +45,7 @@ private function execute(string $dsn): void
$connection->setup();
$this->clearSqs($dsn);
- $connection->send('{"message": "Hi"}', ['type' => DummyMessage::class]);
+ $connection->send('{"message": "Hi"}', ['type' => DummyMessage::class, DummyMessage::class => 'special']);
$this->assertSame(1, $connection->getMessageCount());
$wait = 0;
@@ -54,7 +54,7 @@ private function execute(string $dsn): void
}
$this->assertEquals('{"message": "Hi"}', $encoded['body']);
- $this->assertEquals(['type' => DummyMessage::class], $encoded['headers']);
+ $this->assertEquals(['type' => DummyMessage::class, DummyMessage::class => 'special'], $encoded['headers']);
}
private function clearSqs(string $dsn): void
diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/Connection.php
index 5d5bfb36c65c7..1ae2b463210f2 100644
--- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/Connection.php
+++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/Connection.php
@@ -30,6 +30,7 @@
class Connection
{
private const AWS_SQS_FIFO_SUFFIX = '.fifo';
+ private const MESSAGE_ATTRIBUTE_NAME = 'X-Symfony-Messenger';
private const DEFAULT_OPTIONS = [
'buffer_size' => 9,
@@ -200,7 +201,12 @@ private function fetchMessage(): bool
foreach ($this->currentResponse->getMessages() as $message) {
$headers = [];
- foreach ($message->getMessageAttributes() as $name => $attribute) {
+ $attributes = $message->getMessageAttributes();
+ if (isset($attributes[self::MESSAGE_ATTRIBUTE_NAME]) && 'String' === $attributes[self::MESSAGE_ATTRIBUTE_NAME]->getDataType()) {
+ $headers = json_decode($attributes[self::MESSAGE_ATTRIBUTE_NAME]->getStringValue(), true);
+ unset($attributes[self::MESSAGE_ATTRIBUTE_NAME]);
+ }
+ foreach ($attributes as $name => $attribute) {
if ('String' !== $attribute->getDataType()) {
continue;
}
@@ -284,13 +290,27 @@ public function send(string $body, array $headers, int $delay = 0, ?string $mess
'MessageAttributes' => [],
];
+ $specialHeaders = [];
foreach ($headers as $name => $value) {
+ if ('.' === $name[0] || self::MESSAGE_ATTRIBUTE_NAME === $name || \strlen($name) > 256 || '.' === substr($name, -1) || 'AWS.' === substr($name, 0, \strlen('AWS.')) || 'Amazon.' === substr($name, 0, \strlen('Amazon.')) || preg_match('/([^a-zA-Z0-9_\.-]+|\.\.)/', $name)) {
+ $specialHeaders[$name] = $value;
+
+ continue;
+ }
+
$parameters['MessageAttributes'][$name] = new MessageAttributeValue([
'DataType' => 'String',
'StringValue' => $value,
]);
}
+ if (!empty($specialHeaders)) {
+ $parameters['MessageAttributes'][self::MESSAGE_ATTRIBUTE_NAME] = new MessageAttributeValue([
+ 'DataType' => 'String',
+ 'StringValue' => json_encode($specialHeaders),
+ ]);
+ }
+
if (self::isFifoQueue($this->configuration['queue_name'])) {
$parameters['MessageGroupId'] = null !== $messageGroupId ? $messageGroupId : __METHOD__;
$parameters['MessageDeduplicationId'] = null !== $messageDeduplicationId ? $messageDeduplicationId : sha1(json_encode(['body' => $body, 'headers' => $headers]));
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php
index ef94e60f44312..88a68defeeece 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php
@@ -11,15 +11,15 @@
namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport;
+use Doctrine\DBAL\Abstraction\Result;
use Doctrine\DBAL\DBALException;
-use Doctrine\DBAL\Driver\ResultStatement;
-use Doctrine\DBAL\ForwardCompatibility\Driver\ResultStatement as ForwardCompatibleResultStatement;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaConfig;
use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer;
+use Doctrine\DBAL\Statement;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Bridge\Doctrine\Tests\Fixtures\DummyMessage;
use Symfony\Component\Messenger\Bridge\Doctrine\Transport\Connection;
@@ -31,7 +31,7 @@ public function testGetAMessageWillChangeItsStatus()
$queryBuilder = $this->getQueryBuilderMock();
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
- $stmt = $this->getStatementMock([
+ $stmt = $this->getResultMock([
'id' => 1,
'body' => '{"message":"Hi"}',
'headers' => json_encode(['type' => DummyMessage::class]),
@@ -65,7 +65,7 @@ public function testGetWithNoPendingMessageWillReturnNull()
$queryBuilder = $this->getQueryBuilderMock();
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
- $stmt = $this->getStatementMock(false);
+ $stmt = $this->getResultMock(false);
$queryBuilder
->method('getParameters')
@@ -144,16 +144,12 @@ private function getQueryBuilderMock()
return $queryBuilder;
}
- private function getStatementMock($expectedResult): ResultStatement
+ private function getResultMock($expectedResult)
{
- $mockedInterface = interface_exists(ForwardCompatibleResultStatement::class)
- ? ForwardCompatibleResultStatement::class
- : ResultStatement::class;
-
- $stmt = $this->createMock($mockedInterface);
+ $stmt = $this->createMock(interface_exists(Result::class) ? Result::class : Statement::class);
$stmt->expects($this->once())
- ->method(method_exists($mockedInterface, 'fetchAssociative') ? 'fetchAssociative' : 'fetch')
+ ->method(interface_exists(Result::class) ? 'fetchAssociative' : 'fetch')
->willReturn($expectedResult);
return $stmt;
@@ -270,7 +266,7 @@ public function testFind()
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
$id = 1;
- $stmt = $this->getStatementMock([
+ $stmt = $this->getResultMock([
'id' => $id,
'body' => '{"message":"Hi"}',
'headers' => json_encode(['type' => DummyMessage::class]),
@@ -315,12 +311,9 @@ public function testFindAll()
'headers' => json_encode(['type' => DummyMessage::class]),
];
- $mockedInterface = interface_exists(ForwardCompatibleResultStatement::class)
- ? ForwardCompatibleResultStatement::class
- : ResultStatement::class;
- $stmt = $this->createMock($mockedInterface);
+ $stmt = $this->createMock(interface_exists(Result::class) ? Result::class : Statement::class);
$stmt->expects($this->once())
- ->method(method_exists($mockedInterface, 'fetchAllAssociative') ? 'fetchAllAssociative' : 'fetchAll')
+ ->method(interface_exists(Result::class) ? 'fetchAllAssociative' : 'fetchAll')
->willReturn([$message1, $message2]);
$driverConnection
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php
index a16800464fc1d..f5cd170cfbb2d 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Version;
use PHPUnit\Framework\TestCase;
@@ -71,7 +72,7 @@ public function testSendWithDelay()
->setParameter(':body', '{"message": "Hi i am delayed"}')
->execute();
- $available_at = new \DateTime(method_exists($stmt, 'fetchOne') ? $stmt->fetchOne() : $stmt->fetchColumn());
+ $available_at = new \DateTime($stmt instanceof Result ? $stmt->fetchOne() : $stmt->fetchColumn());
$now = new \DateTime();
$now->modify('+60 seconds');
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php
index 90017123e168c..39398519bbee0 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php
@@ -13,7 +13,7 @@
use Doctrine\DBAL\Connection as DBALConnection;
use Doctrine\DBAL\DBALException;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\DBAL\Schema\Schema;
@@ -175,7 +175,7 @@ public function get(): ?array
$query->getParameters(),
$query->getParameterTypes()
);
- $doctrineEnvelope = method_exists($stmt, 'fetchAssociative') ? $stmt->fetchAssociative() : $stmt->fetch();
+ $doctrineEnvelope = $stmt instanceof Result ? $stmt->fetchAssociative() : $stmt->fetch();
if (false === $doctrineEnvelope) {
$this->driverConnection->commit();
@@ -267,7 +267,7 @@ public function getMessageCount(): int
$stmt = $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters(), $queryBuilder->getParameterTypes());
- return method_exists($stmt, 'fetchOne') ? $stmt->fetchOne() : $stmt->fetchColumn();
+ return $stmt instanceof Result ? $stmt->fetchOne() : $stmt->fetchColumn();
}
public function findAll(int $limit = null): array
@@ -278,7 +278,7 @@ public function findAll(int $limit = null): array
}
$stmt = $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters(), $queryBuilder->getParameterTypes());
- $data = method_exists($stmt, 'fetchAllAssociative') ? $stmt->fetchAllAssociative() : $stmt->fetchAll();
+ $data = $stmt instanceof Result ? $stmt->fetchAllAssociative() : $stmt->fetchAll();
return array_map(function ($doctrineEnvelope) {
return $this->decodeEnvelopeHeaders($doctrineEnvelope);
@@ -291,7 +291,7 @@ public function find($id): ?array
->where('m.id = ?');
$stmt = $this->executeQuery($queryBuilder->getSQL(), [$id]);
- $data = method_exists($stmt, 'fetchAssociative') ? $stmt->fetchAssociative() : $stmt->fetch();
+ $data = $stmt instanceof Result ? $stmt->fetchAssociative() : $stmt->fetch();
return false === $data ? null : $this->decodeEnvelopeHeaders($data);
}
@@ -350,7 +350,7 @@ private function createQueryBuilder(): QueryBuilder
->from($this->configuration['table_name'], 'm');
}
- private function executeQuery(string $sql, array $parameters = [], array $types = []): ResultStatement
+ private function executeQuery(string $sql, array $parameters = [], array $types = [])
{
try {
$stmt = $this->driverConnection->executeQuery($sql, $parameters, $types);
@@ -390,6 +390,7 @@ private function addTableToSchema(Schema $schema): void
$table->addColumn('headers', self::$useDeprecatedConstants ? Type::TEXT : Types::TEXT)
->setNotnull(true);
$table->addColumn('queue_name', self::$useDeprecatedConstants ? Type::STRING : Types::STRING)
+ ->setLength(190) // mysql 5.6 only supports 191 characters on an indexed column in utf8mb4 mode
->setNotnull(true);
$table->addColumn('created_at', self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE)
->setNotnull(true);
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json b/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json
index 35ddc7f09a52a..9d8d8a8fef534 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json
@@ -22,7 +22,6 @@
},
"require-dev": {
"doctrine/dbal": "^2.6|^3.0",
- "doctrine/orm": "^2.6.3",
"doctrine/persistence": "^1.3",
"symfony/property-access": "^4.4|^5.0",
"symfony/serializer": "^4.4|^5.0"
diff --git a/src/Symfony/Component/Mime/Address.php b/src/Symfony/Component/Mime/Address.php
index 6d663e93b75fa..9847712b10747 100644
--- a/src/Symfony/Component/Mime/Address.php
+++ b/src/Symfony/Component/Mime/Address.php
@@ -89,7 +89,7 @@ public static function create($address): self
return $address;
}
if (\is_string($address)) {
- return new self($address);
+ return self::fromString($address);
}
throw new InvalidArgumentException(sprintf('An address can be an instance of Address or a string ("%s") given).', get_debug_type($address)));
diff --git a/src/Symfony/Component/Mime/Crypto/SMimeSigner.php b/src/Symfony/Component/Mime/Crypto/SMimeSigner.php
index 243aaf10da060..1b555375ce908 100644
--- a/src/Symfony/Component/Mime/Crypto/SMimeSigner.php
+++ b/src/Symfony/Component/Mime/Crypto/SMimeSigner.php
@@ -24,11 +24,6 @@ final class SMimeSigner extends SMime
private $signOptions;
private $extraCerts;
- /**
- * @var string|null
- */
- private $privateKeyPassphrase;
-
/**
* @param string $certificate The path of the file containing the signing certificate (in PEM format)
* @param string $privateKey The path of the file containing the private key (in PEM format)
@@ -52,7 +47,6 @@ public function __construct(string $certificate, string $privateKey, string $pri
$this->signOptions = $signOptions ?? PKCS7_DETACHED;
$this->extraCerts = $extraCerts ? realpath($extraCerts) : null;
- $this->privateKeyPassphrase = $privateKeyPassphrase;
}
public function sign(Message $message): Message
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
index a667997a9b7e4..baf377427d384 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
@@ -69,6 +69,11 @@ public function getPublicAccessor()
return $this->publicAccessor;
}
+ public function isPublicAccessor($param)
+ {
+ throw new \LogicException('This method should never have been called.');
+ }
+
public function getPublicAccessorWithDefaultValue()
{
return $this->publicAccessorWithDefaultValue;
diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json
index 4cde7bacf9a17..dd7bfb1ac7f56 100644
--- a/src/Symfony/Component/PropertyAccess/composer.json
+++ b/src/Symfony/Component/PropertyAccess/composer.json
@@ -19,7 +19,7 @@
"php": ">=7.2.5",
"symfony/inflector": "^4.4|^5.0",
"symfony/polyfill-php80": "^1.15",
- "symfony/property-info": "^5.1"
+ "symfony/property-info": "^5.1.1"
},
"require-dev": {
"symfony/cache": "^4.4|^5.0"
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
index 8de931d9c577b..1cf15345028c7 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
@@ -40,7 +40,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
/**
* @internal
*/
- public static $defaultAccessorPrefixes = ['is', 'can', 'get', 'has'];
+ public static $defaultAccessorPrefixes = ['get', 'is', 'has', 'can'];
/**
* @internal
diff --git a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
index bee7a4a5694c3..cd137eea78be0 100644
--- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
@@ -171,7 +171,7 @@ protected function parseImport(RouteCollection $collection, array $config, strin
$schemes = isset($config['schemes']) ? $config['schemes'] : null;
$methods = isset($config['methods']) ? $config['methods'] : null;
$trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true;
- $namePrefix = $config['name_prefix'] ?? '';
+ $namePrefix = $config['name_prefix'] ?? null;
$exclude = $config['exclude'] ?? null;
if (isset($config['controller'])) {
diff --git a/src/Symfony/Component/Routing/RouteCollection.php b/src/Symfony/Component/Routing/RouteCollection.php
index a3284771bb579..b661bb13a61de 100644
--- a/src/Symfony/Component/Routing/RouteCollection.php
+++ b/src/Symfony/Component/Routing/RouteCollection.php
@@ -179,8 +179,8 @@ public function addNamePrefix(string $prefix)
foreach ($this->routes as $name => $route) {
$prefixedRoutes[$prefix.$name] = $route;
- if (null !== $name = $route->getDefault('_canonical_route')) {
- $route->setDefault('_canonical_route', $prefix.$name);
+ if (null !== $canonicalName = $route->getDefault('_canonical_route')) {
+ $route->setDefault('_canonical_route', $prefix.$canonicalName);
}
if (isset($this->priorities[$name])) {
$prefixedPriorities[$prefix.$name] = $this->priorities[$name];
diff --git a/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php b/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
index 05d3d0162ad1e..90a66dbc679eb 100644
--- a/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
+++ b/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
@@ -363,4 +363,21 @@ public function testAddWithPriority()
$this->assertSame($expected, $collection2->all());
}
+
+ public function testAddWithPriorityAndPrefix()
+ {
+ $collection3 = new RouteCollection();
+ $collection3->add('foo3', $foo3 = new Route('/foo'), 0);
+ $collection3->add('bar3', $bar3 = new Route('/bar'), 1);
+ $collection3->add('baz3', $baz3 = new Route('/baz'));
+ $collection3->addNamePrefix('prefix_');
+
+ $expected = [
+ 'prefix_bar3' => $bar3,
+ 'prefix_foo3' => $foo3,
+ 'prefix_baz3' => $baz3,
+ ];
+
+ $this->assertSame($expected, $collection3->all());
+ }
}
diff --git a/src/Symfony/Component/Security/Http/Firewall/AccessListener.php b/src/Symfony/Component/Security/Http/Firewall/AccessListener.php
index 8da2a994bf48f..b218e1086c62a 100644
--- a/src/Symfony/Component/Security/Http/Firewall/AccessListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/AccessListener.php
@@ -95,11 +95,13 @@ public function authenticate(RequestEvent $event)
return;
}
- if ([self::PUBLIC_ACCESS] === $attributes) {
- return;
+ if ([self::PUBLIC_ACCESS] !== $attributes) {
+ throw $this->createAccessDeniedException($request, $attributes);
}
+ }
- throw $this->createAccessDeniedException($request, $attributes);
+ if ([self::PUBLIC_ACCESS] === $attributes) {
+ return;
}
if (!$token->isAuthenticated()) {
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php
index 9748e6522c6ad..154addc7c4095 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php
@@ -18,8 +18,10 @@
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\Security\Http\Event\LazyResponseEvent;
use Symfony\Component\Security\Http\Firewall\AccessListener;
@@ -279,6 +281,33 @@ public function testHandleWhenPublicAccessIsAllowedAndExceptionOnTokenIsFalse()
$this->expectNotToPerformAssertions();
}
+ public function testHandleWhenPublicAccessWhileAuthenticated()
+ {
+ $token = new UsernamePasswordToken(new User('Wouter', null, ['ROLE_USER']), null, 'main', ['ROLE_USER']);
+ $tokenStorage = new TokenStorage();
+ $tokenStorage->setToken($token);
+ $request = new Request();
+
+ $accessMap = $this->createMock(AccessMapInterface::class);
+ $accessMap->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->willReturn([[AccessListener::PUBLIC_ACCESS], null])
+ ;
+
+ $listener = new AccessListener(
+ $tokenStorage,
+ $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')->getMock(),
+ $accessMap,
+ $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
+ false
+ );
+
+ $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST));
+
+ $this->expectNotToPerformAssertions();
+ }
+
public function testHandleMWithultipleAttributesShouldBeHandledAsAnd()
{
$request = new Request();
diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php
index fa4f944cd1fe1..b8c33a2fe56c5 100644
--- a/src/Symfony/Component/Serializer/Serializer.php
+++ b/src/Symfony/Component/Serializer/Serializer.php
@@ -21,6 +21,7 @@
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
+use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
@@ -158,7 +159,7 @@ public function normalize($data, string $format = null, array $context = [])
}
if (\is_array($data) || $data instanceof \Traversable) {
- if ($data instanceof \Countable && 0 === $data->count()) {
+ if (($context[AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS] ?? false) === true && $data instanceof \Countable && 0 === $data->count()) {
return $data;
}
diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php
index cbc40b361cbcc..3158f728fbfb2 100644
--- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php
@@ -492,6 +492,27 @@ public function testNotNormalizableValueExceptionMessageForAResource()
(new Serializer())->normalize(tmpfile());
}
+ public function testNormalizeTransformEmptyArrayObjectToArray()
+ {
+ $serializer = new Serializer(
+ [
+ new PropertyNormalizer(),
+ new ObjectNormalizer(),
+ new ArrayDenormalizer(),
+ ],
+ [
+ 'json' => new JsonEncoder(),
+ ]
+ );
+
+ $object = [];
+ $object['foo'] = new \ArrayObject();
+ $object['bar'] = new \ArrayObject(['notempty']);
+ $object['baz'] = new \ArrayObject(['nested' => new \ArrayObject()]);
+
+ $this->assertSame('{"foo":[],"bar":["notempty"],"baz":{"nested":[]}}', $serializer->serialize($object, 'json'));
+ }
+
public function testNormalizePreserveEmptyArrayObject()
{
$serializer = new Serializer(
diff --git a/src/Symfony/Component/String/AbstractString.php b/src/Symfony/Component/String/AbstractString.php
index c9ba147b319cf..f2754a56d0b69 100644
--- a/src/Symfony/Component/String/AbstractString.php
+++ b/src/Symfony/Component/String/AbstractString.php
@@ -643,7 +643,11 @@ public function truncate(int $length, string $ellipsis = '', bool $cut = true):
}
if (!$cut) {
- $length = $ellipsisLength + ($this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1) ?? $stringLength);
+ if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) {
+ return clone $this;
+ }
+
+ $length += $ellipsisLength;
}
$str = $this->slice(0, $length - $ellipsisLength);
diff --git a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php
index b333c74a252ba..44f14c18af3a2 100644
--- a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php
+++ b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php
@@ -1450,6 +1450,7 @@ public static function provideTruncate()
['foobar...', 'foobar foo', 6, '...', false],
['foobar...', 'foobar foo', 7, '...', false],
['foobar foo...', 'foobar foo a', 10, '...', false],
+ ['foobar foo aar', 'foobar foo aar', 12, '...', false],
];
}
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php
index 7db835b333f52..677660f6c31af 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php
@@ -11,39 +11,21 @@
namespace Symfony\Component\Validator\Tests\Constraints;
-use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
-use Symfony\Component\ExpressionLanguage\SyntaxError;
use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntax;
use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntaxValidator;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
class ExpressionLanguageSyntaxValidatorTest extends ConstraintValidatorTestCase
{
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject|ExpressionLanguage
- */
- protected $expressionLanguage;
-
protected function createValidator()
{
- return new ExpressionLanguageSyntaxValidator($this->expressionLanguage);
- }
-
- protected function setUp(): void
- {
- $this->expressionLanguage = $this->createExpressionLanguage();
-
- parent::setUp();
+ return new ExpressionLanguageSyntaxValidator(new ExpressionLanguage());
}
public function testExpressionValid(): void
{
- $this->expressionLanguage->expects($this->once())
- ->method('lint')
- ->with($this->value, []);
-
- $this->validator->validate($this->value, new ExpressionLanguageSyntax([
+ $this->validator->validate('1 + 1', new ExpressionLanguageSyntax([
'message' => 'myMessage',
'allowedVariables' => [],
]));
@@ -53,12 +35,18 @@ public function testExpressionValid(): void
public function testExpressionWithoutNames(): void
{
- $this->expressionLanguage->expects($this->once())
- ->method('lint')
- ->with($this->value, null);
+ $this->validator->validate('1 + 1', new ExpressionLanguageSyntax([
+ 'message' => 'myMessage',
+ ]));
- $this->validator->validate($this->value, new ExpressionLanguageSyntax([
+ $this->assertNoViolation();
+ }
+
+ public function testExpressionWithAllowedVariableName(): void
+ {
+ $this->validator->validate('a + 1', new ExpressionLanguageSyntax([
'message' => 'myMessage',
+ 'allowedVariables' => ['a'],
]));
$this->assertNoViolation();
@@ -66,24 +54,15 @@ public function testExpressionWithoutNames(): void
public function testExpressionIsNotValid(): void
{
- $this->expressionLanguage->expects($this->once())
- ->method('lint')
- ->with($this->value, [])
- ->willThrowException(new SyntaxError('Test exception', 42));
-
- $this->validator->validate($this->value, new ExpressionLanguageSyntax([
+ $this->validator->validate('a + 1', new ExpressionLanguageSyntax([
'message' => 'myMessage',
'allowedVariables' => [],
]));
$this->buildViolation('myMessage')
- ->setParameter('{{ syntax_error }}', '"Test exception around position 42."')
+ ->setParameter('{{ syntax_error }}', '"Variable "a" is not valid around position 1 for expression `a + 1`."')
+ ->setInvalidValue('a + 1')
->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR)
->assertRaised();
}
-
- protected function createExpressionLanguage(): MockObject
- {
- return $this->getMockBuilder('\Symfony\Component\ExpressionLanguage\ExpressionLanguage')->getMock();
- }
}
diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
index 55dfc12763665..fcb1d146944a5 100644
--- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
+++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
@@ -92,7 +92,7 @@ public function testExport(string $testName, $value, bool $staticValueExpected =
} elseif (\PHP_VERSION_ID < 70400) {
$fixtureFile = __DIR__.'/Fixtures/'.$testName.'-legacy.php';
} else {
- $this->markAsSkipped('PHP >= 7.4.6 required.');
+ $this->markTestSkipped('PHP >= 7.4.6 required.');
}
$this->assertStringEqualsFile($fixtureFile, $dump);
diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
index a60d8057e0b7f..2c23815bad64d 100644
--- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
+++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
@@ -811,6 +811,30 @@ public function testUncheckedTimeoutThrows()
}
}
+ public function testTimeoutWithActiveConcurrentStream()
+ {
+ $p1 = TestHttpServer::start(8067);
+ $p2 = TestHttpServer::start(8077);
+
+ $client = $this->getHttpClient(__FUNCTION__);
+ $streamingResponse = $client->request('GET', 'http://localhost:8067/max-duration');
+ $blockingResponse = $client->request('GET', 'http://localhost:8077/timeout-body', [
+ 'timeout' => 0.25,
+ ]);
+
+ $this->assertSame(200, $streamingResponse->getStatusCode());
+ $this->assertSame(200, $blockingResponse->getStatusCode());
+
+ $this->expectException(TransportExceptionInterface::class);
+
+ try {
+ $blockingResponse->getContent();
+ } finally {
+ $p1->stop();
+ $p2->stop();
+ }
+ }
+
public function testDestruct()
{
$client = $this->getHttpClient(__FUNCTION__);
diff --git a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
index 5dae27c912a09..1af9130f964f6 100644
--- a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
+++ b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
@@ -16,23 +16,28 @@
class TestHttpServer
{
- private static $process;
+ private static $process = [];
- public static function start()
+ public static function start(int $port = 8057)
{
- if (self::$process) {
- self::$process->stop();
+ if (isset(self::$process[$port])) {
+ self::$process[$port]->stop();
+ } else {
+ register_shutdown_function(static function () use ($port) {
+ self::$process[$port]->stop();
+ });
}
$finder = new PhpExecutableFinder();
- $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:8057']));
+ $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:'.$port]));
$process->setWorkingDirectory(__DIR__.'/Fixtures/web');
$process->start();
+ self::$process[$port] = $process;
do {
usleep(50000);
- } while (!@fopen('http://127.0.0.1:8057/', 'r'));
+ } while (!@fopen('http://127.0.0.1:'.$port, 'r'));
- self::$process = $process;
+ return $process;
}
}
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy