Skip to content

Commit 7256823

Browse files
Merge branch '6.4' into 7.3
* 6.4: Fix negative delays with AMQP messenger transport [TwigBundle] Align TemplateIterator handling of @! original bundle templates with TwigExtension [AssetMapper] Batch concurrent requests to prevent flooding jsdelivr [HttpFoundation][Cache] Fix VARBINARY columns on sqlsrv [Cache] Fix calling the callback wrapper for ChainAdapter [Process] Fix dealing with broken stdin pipes
2 parents 62a8a88 + 146a71a commit 7256823

File tree

15 files changed

+240
-14
lines changed

15 files changed

+240
-14
lines changed

src/Symfony/Bundle/TwigBundle/TemplateIterator.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ public function getIterator(): \Traversable
6060
if (null !== $this->defaultPath) {
6161
$templates[] = $this->findTemplatesInDirectory($this->defaultPath.'/bundles/'.$bundle->getName(), $name);
6262
}
63+
64+
/*
65+
* The bundle's own templates are also registered with the "!" prefix namespace - this matches
66+
* @see \Symfony\Bundle\TwigBundle\DependencyInjection\TwigExtension::load()
67+
*/
68+
$templates[] = $this->findTemplatesInDirectory($bundleTemplatesDir, '!'.$name);
6369
}
6470

6571
foreach ($this->paths as $dir => $namespace) {

src/Symfony/Bundle/TwigBundle/Tests/TemplateIteratorTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public function testGetIterator()
2525
sort($sorted);
2626
$this->assertEquals(
2727
[
28+
'@!Bar/index.html.twig',
2829
'@Bar/index.html.twig',
2930
'@Bar/layout.html.twig',
3031
'@Foo/index.html.twig',
@@ -43,6 +44,7 @@ public function testGetIteratorWithFileNameFilter()
4344
sort($sorted);
4445
$this->assertEquals(
4546
[
47+
'@!Bar/index.html.twig',
4648
'@Bar/index.html.twig',
4749
'@Bar/layout.html.twig',
4850
'@Foo/index.html.twig',
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\AssetMapper\ImportMap;
13+
14+
use Symfony\Component\HttpClient\DecoratorTrait;
15+
use Symfony\Contracts\HttpClient\HttpClientInterface;
16+
use Symfony\Contracts\HttpClient\ResponseInterface;
17+
18+
/**
19+
* @internal
20+
*/
21+
class BatchHttpClient implements HttpClientInterface
22+
{
23+
use DecoratorTrait;
24+
25+
private const BATCH_SIZE = 250;
26+
27+
private \WeakMap $pendingRequests;
28+
29+
public function request(string $method, string $url, array $options = []): ResponseInterface
30+
{
31+
$this->pendingRequests ??= new \WeakMap();
32+
$pendingRequests = [];
33+
34+
foreach ($this->pendingRequests as $response => $_) {
35+
if ($response->getInfo('http_code')) {
36+
$this->pendingRequests->offsetUnset($response);
37+
} else {
38+
$pendingRequests[] = $response;
39+
}
40+
}
41+
42+
if (\count($pendingRequests) >= self::BATCH_SIZE) {
43+
foreach ($this->client->stream($pendingRequests) as $response => $chunk) {
44+
if (!$chunk->isTimeout() && $chunk->isFirst()) {
45+
$response->getStatusCode(); // ignore 3/4/5xx
46+
$this->pendingRequests->offsetUnset($response);
47+
break;
48+
}
49+
}
50+
}
51+
52+
$response = $this->client->request($method, $url, $options);
53+
$this->pendingRequests[$response] = true;
54+
55+
return $response;
56+
}
57+
}

src/Symfony/Component/AssetMapper/ImportMap/ImportMapUpdateChecker.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function __construct(
2424
private readonly ImportMapConfigReader $importMapConfigReader,
2525
?HttpClientInterface $httpClient = null,
2626
) {
27-
$this->httpClient = $httpClient ?? HttpClient::create();
27+
$this->httpClient = new BatchHttpClient($httpClient ?? HttpClient::create());
2828
}
2929

3030
/**

src/Symfony/Component/AssetMapper/ImportMap/ImportMapVersionChecker.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ class ImportMapVersionChecker
2121
{
2222
private const PACKAGE_METADATA_PATTERN = 'https://registry.npmjs.org/%package%/%version%';
2323

24-
private HttpClientInterface $httpClient;
24+
private readonly HttpClientInterface $httpClient;
2525

2626
public function __construct(
2727
private ImportMapConfigReader $importMapConfigReader,
2828
private RemotePackageDownloader $packageDownloader,
2929
?HttpClientInterface $httpClient = null,
3030
) {
31-
$this->httpClient = $httpClient ?? HttpClient::create();
31+
$this->httpClient = new BatchHttpClient($httpClient ?? HttpClient::create());
3232
}
3333

3434
/**

src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\AssetMapper\Compiler\CssAssetUrlCompiler;
1515
use Symfony\Component\AssetMapper\Exception\RuntimeException;
16+
use Symfony\Component\AssetMapper\ImportMap\BatchHttpClient;
1617
use Symfony\Component\AssetMapper\ImportMap\ImportMapEntry;
1718
use Symfony\Component\AssetMapper\ImportMap\ImportMapType;
1819
use Symfony\Component\AssetMapper\ImportMap\PackageRequireOptions;
@@ -32,12 +33,12 @@ final class JsDelivrEsmResolver implements PackageResolverInterface
3233

3334
private const ES_MODULE_SHIMS = 'es-module-shims';
3435

35-
private HttpClientInterface $httpClient;
36+
private readonly HttpClientInterface $httpClient;
3637

3738
public function __construct(
3839
?HttpClientInterface $httpClient = null,
3940
) {
40-
$this->httpClient = $httpClient ?? HttpClient::create();
41+
$this->httpClient = new BatchHttpClient($httpClient ?? HttpClient::create());
4142
}
4243

4344
public function resolvePackages(array $packagesToRequire): array

src/Symfony/Component/Cache/Adapter/ChainAdapter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public function get(string $key, callable $callback, ?float $beta = null, ?array
108108
$callback = $wrap;
109109
$beta = \INF === $beta ? \INF : 0;
110110
}
111-
if ($adapter instanceof CacheInterface) {
111+
if ($adapter instanceof CacheInterface && $i !== $this->adapterCount) {
112112
$value = $adapter->get($key, $callback, $beta, $metadata);
113113
} else {
114114
$value = $this->doGet($adapter, $key, $callback, $beta, $metadata);

src/Symfony/Component/Cache/Adapter/PdoAdapter.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,17 @@ protected function doSave(array $values, int $lifetime): array|bool
324324
$insertStmt->bindValue(':time', $now, \PDO::PARAM_INT);
325325
}
326326

327+
if ('sqlsrv' === $driver) {
328+
$dataStream = fopen('php://memory', 'r+');
329+
}
327330
foreach ($values as $id => $data) {
331+
if ('sqlsrv' === $driver) {
332+
rewind($dataStream);
333+
fwrite($dataStream, $data);
334+
ftruncate($dataStream, \strlen($data));
335+
rewind($dataStream);
336+
$data = $dataStream;
337+
}
328338
try {
329339
$stmt->execute();
330340
} catch (\PDOException $e) {

src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,4 +266,28 @@ private function getNonPruneableMock(): AdapterInterface
266266
{
267267
return $this->createMock(AdapterInterface::class);
268268
}
269+
270+
public function testSetCallbackWrapperPropagation()
271+
{
272+
$adapter1 = new ArrayAdapter();
273+
$adapter2 = new FilesystemAdapter();
274+
275+
$chain = new ChainAdapter([$adapter1, $adapter2]);
276+
277+
$callbackWrapperCalled = false;
278+
$customWrapper = static function (callable $callback, CacheItem $item, bool &$save) use (&$callbackWrapperCalled) {
279+
$callbackWrapperCalled = true;
280+
281+
return $callback($item, $save);
282+
};
283+
284+
$chain->setCallbackWrapper($customWrapper);
285+
286+
$chain->delete('test-callback-wrapper');
287+
288+
$result = $chain->get('test-callback-wrapper', static fn () => 'computed-value');
289+
290+
$this->assertTrue($callbackWrapperCalled);
291+
$this->assertSame('computed-value', $result);
292+
}
269293
}

src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,12 @@ private function getInsertStatement(#[\SensitiveParameter] string $sessionId, st
802802
rewind($data);
803803
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :expiry, :time) RETURNING $this->dataCol into :data";
804804
break;
805+
case 'sqlsrv':
806+
$data = fopen('php://memory', 'r+');
807+
fwrite($data, $sessionData);
808+
rewind($data);
809+
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)";
810+
break;
805811
default:
806812
$data = $sessionData;
807813
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)";
@@ -829,6 +835,12 @@ private function getUpdateStatement(#[\SensitiveParameter] string $sessionId, st
829835
rewind($data);
830836
$sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
831837
break;
838+
case 'sqlsrv':
839+
$data = fopen('php://memory', 'r+');
840+
fwrite($data, $sessionData);
841+
rewind($data);
842+
$sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id";
843+
break;
832844
default:
833845
$data = $sessionData;
834846
$sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id";
@@ -876,12 +888,16 @@ private function getMergeStatement(#[\SensitiveParameter] string $sessionId, str
876888
$mergeStmt = $this->pdo->prepare($mergeSql);
877889

878890
if ('sqlsrv' === $this->driver) {
891+
$dataStream = fopen('php://memory', 'r+');
892+
fwrite($dataStream, $data);
893+
rewind($dataStream);
894+
879895
$mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
880896
$mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
881-
$mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
897+
$mergeStmt->bindParam(3, $dataStream, \PDO::PARAM_LOB);
882898
$mergeStmt->bindValue(4, time() + $maxlifetime, \PDO::PARAM_INT);
883899
$mergeStmt->bindValue(5, time(), \PDO::PARAM_INT);
884-
$mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB);
900+
$mergeStmt->bindParam(6, $dataStream, \PDO::PARAM_LOB);
885901
$mergeStmt->bindValue(7, time() + $maxlifetime, \PDO::PARAM_INT);
886902
$mergeStmt->bindValue(8, time(), \PDO::PARAM_INT);
887903
} else {

0 commit comments

Comments
 (0)