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 fa37e6d7baa3a..cf7e9b39f2bec 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_class($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/PdoStore.php b/src/Symfony/Component/Lock/Store/PdoStore.php
index 7af7f3870fad2..a3273c0cbc75d 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;
@@ -168,10 +169,10 @@ public function putOffExpiration(Key $key, $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();
}
@@ -201,9 +202,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 7295fdf79a5a9..0bd5627ac9bd1 100644
--- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
@@ -113,6 +113,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 a8f159dc7c491..c0cd0c5fb9dad 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/Tests/Transport/Doctrine/ConnectionTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php
index d34e45d2ce518..b4348d4958a48 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php
@@ -11,14 +11,14 @@
namespace Symfony\Component\Messenger\Tests\Transport\Doctrine;
+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\SchemaConfig;
use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer;
+use Doctrine\DBAL\Statement;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
use Symfony\Component\Messenger\Transport\Doctrine\Connection;
@@ -30,7 +30,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]),
@@ -64,7 +64,7 @@ public function testGetWithNoPendingMessageWillReturnNull()
$queryBuilder = $this->getQueryBuilderMock();
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
- $stmt = $this->getStatementMock(false);
+ $stmt = $this->getResultMock(false);
$queryBuilder
->method('getParameters')
@@ -143,16 +143,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;
@@ -267,7 +263,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]),
@@ -312,12 +308,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/Tests/Transport/Doctrine/DoctrineIntegrationTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/DoctrineIntegrationTest.php
index 45ca64b77106a..37a38b95d485e 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/DoctrineIntegrationTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/DoctrineIntegrationTest.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Messenger\Tests\Transport\Doctrine;
+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/Transport/Doctrine/Connection.php b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php
index 4f5b43c4ac8d9..19141bd8d94c3 100644
--- a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php
+++ b/src/Symfony/Component/Messenger/Transport/Doctrine/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;
@@ -164,7 +164,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();
@@ -252,7 +252,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
@@ -263,7 +263,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);
@@ -276,7 +276,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);
}
@@ -310,7 +310,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);
diff --git a/src/Symfony/Component/Mime/Address.php b/src/Symfony/Component/Mime/Address.php
index b0dcbd0880f66..53f682c9c2bb7 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).', \is_object($address) ? \get_class($address) : \gettype($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/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php
index 8e92abe29cdcf..470e9ed0010af 100644
--- a/src/Symfony/Component/Serializer/Serializer.php
+++ b/src/Symfony/Component/Serializer/Serializer.php
@@ -20,6 +20,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;
@@ -157,7 +158,7 @@ public function normalize($data, $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 fe8f8de929692..dce864abfcf47 100644
--- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php
@@ -491,6 +491,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/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
index 2dfb09d2ffc1f..8dbcc06e8ddc6 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 8ef46a9a90eeb..c1ad3ffec5014 100644
--- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
+++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
@@ -786,6 +786,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 cfc100d80ce6c..06a11444e35e4 100644
--- a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
+++ b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
@@ -19,23 +19,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