Skip to content

Commit ec77126

Browse files
Merge branch '6.4' into 7.2
* 6.4: Silence E_DEPRECATED and E_USER_DEPRECATED [HttpCache] Hit the backend only once after waiting for the cache lock fix compatibility with Symfony 7.4
2 parents ac794a7 + 9a7f7a3 commit ec77126

File tree

10 files changed

+114
-18
lines changed

10 files changed

+114
-18
lines changed

src/Symfony/Component/ErrorHandler/Debug.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Debug
2020
{
2121
public static function enable(): ErrorHandler
2222
{
23-
error_reporting(-1);
23+
error_reporting(\E_ALL & ~\E_DEPRECATED & ~\E_USER_DEPRECATED);
2424

2525
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
2626
ini_set('display_errors', 0);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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\HttpKernel\HttpCache;
13+
14+
/**
15+
* @internal
16+
*/
17+
class CacheWasLockedException extends \Exception
18+
{
19+
}

src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,13 @@ public function handle(Request $request, int $type = HttpKernelInterface::MAIN_R
210210
$this->record($request, 'reload');
211211
$response = $this->fetch($request, $catch);
212212
} else {
213-
$response = $this->lookup($request, $catch);
213+
$response = null;
214+
do {
215+
try {
216+
$response = $this->lookup($request, $catch);
217+
} catch (CacheWasLockedException) {
218+
}
219+
} while (null === $response);
214220
}
215221

216222
$this->restoreResponseBody($request, $response);
@@ -560,15 +566,7 @@ protected function lock(Request $request, Response $entry): bool
560566

561567
// wait for the lock to be released
562568
if ($this->waitForLock($request)) {
563-
// replace the current entry with the fresh one
564-
$new = $this->lookup($request);
565-
$entry->headers = $new->headers;
566-
$entry->setContent($new->getContent());
567-
$entry->setStatusCode($new->getStatusCode());
568-
$entry->setProtocolVersion($new->getProtocolVersion());
569-
foreach ($new->headers->getCookies() as $cookie) {
570-
$entry->headers->setCookie($cookie);
571-
}
569+
throw new CacheWasLockedException(); // unwind back to handle(), try again
572570
} else {
573571
// backend is slow as hell, send a 503 response (to avoid the dog pile effect)
574572
$entry->setStatusCode(503);

src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\HttpKernel\Event\TerminateEvent;
1818
use Symfony\Component\HttpKernel\HttpCache\Esi;
1919
use Symfony\Component\HttpKernel\HttpCache\HttpCache;
20+
use Symfony\Component\HttpKernel\HttpCache\Store;
2021
use Symfony\Component\HttpKernel\HttpCache\StoreInterface;
2122
use Symfony\Component\HttpKernel\HttpKernelInterface;
2223
use Symfony\Component\HttpKernel\Kernel;
@@ -662,6 +663,7 @@ public function testDegradationWhenCacheLocked()
662663
*/
663664
sleep(10);
664665

666+
$this->store = $this->createStore(); // create another store instance that does not hold the current lock
665667
$this->request('GET', '/');
666668
$this->assertHttpKernelIsNotCalled();
667669
$this->assertEquals(200, $this->response->getStatusCode());
@@ -680,6 +682,64 @@ public function testDegradationWhenCacheLocked()
680682
$this->assertEquals('Old response', $this->response->getContent());
681683
}
682684

685+
public function testHitBackendOnlyOnceWhenCacheWasLocked()
686+
{
687+
// Disable stale-while-revalidate, it circumvents waiting for the lock
688+
$this->cacheConfig['stale_while_revalidate'] = 0;
689+
690+
$this->setNextResponses([
691+
[
692+
'status' => 200,
693+
'body' => 'initial response',
694+
'headers' => [
695+
'Cache-Control' => 'public, no-cache',
696+
'Last-Modified' => 'some while ago',
697+
],
698+
],
699+
[
700+
'status' => 304,
701+
'body' => '',
702+
'headers' => [
703+
'Cache-Control' => 'public, no-cache',
704+
'Last-Modified' => 'some while ago',
705+
],
706+
],
707+
[
708+
'status' => 500,
709+
'body' => 'The backend should not be called twice during revalidation',
710+
'headers' => [],
711+
],
712+
]);
713+
714+
$this->request('GET', '/'); // warm the cache
715+
716+
// Use a store that simulates a cache entry being locked upon first attempt
717+
$this->store = new class(sys_get_temp_dir() . '/http_cache') extends Store {
718+
private bool $hasLock = false;
719+
720+
public function lock(Request $request): bool
721+
{
722+
$hasLock = $this->hasLock;
723+
$this->hasLock = true;
724+
725+
return $hasLock;
726+
}
727+
728+
public function isLocked(Request $request): bool
729+
{
730+
return false;
731+
}
732+
};
733+
734+
$this->request('GET', '/'); // hit the cache with simulated lock/concurrency block
735+
736+
$this->assertEquals(200, $this->response->getStatusCode());
737+
$this->assertEquals('initial response', $this->response->getContent());
738+
739+
$traces = $this->cache->getTraces();
740+
$this->assertSame(['stale', 'valid', 'store'], current($traces));
741+
}
742+
683743
public function testHitsCachedResponseWithSMaxAgeDirective()
684744
{
685745
$time = \DateTimeImmutable::createFromFormat('U', time() - 5);

src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ abstract class HttpCacheTestCase extends TestCase
3030
protected $responses;
3131
protected $catch;
3232
protected $esi;
33-
protected Store $store;
33+
protected ?Store $store = null;
3434

3535
protected function setUp(): void
3636
{
@@ -115,7 +115,9 @@ public function request($method, $uri = '/', $server = [], $cookies = [], $esi =
115115

116116
$this->kernel->reset();
117117

118-
$this->store = new Store(sys_get_temp_dir().'/http_cache');
118+
if (! $this->store) {
119+
$this->store = $this->createStore();
120+
}
119121

120122
if (!isset($this->cacheConfig['debug'])) {
121123
$this->cacheConfig['debug'] = true;
@@ -183,4 +185,9 @@ public static function clearDirectory($directory)
183185

184186
closedir($fp);
185187
}
188+
189+
protected function createStore(): Store
190+
{
191+
return new Store(sys_get_temp_dir() . '/http_cache');
192+
}
186193
}

src/Symfony/Component/Runtime/Internal/BasicErrorHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class BasicErrorHandler
2020
{
2121
public static function register(bool $debug): void
2222
{
23-
error_reporting(-1);
23+
error_reporting(\E_ALL & ~\E_DEPRECATED & ~\E_USER_DEPRECATED);
2424

2525
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
2626
ini_set('display_errors', $debug);

src/Symfony/Component/Runtime/Internal/SymfonyErrorHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static function register(bool $debug): void
3030
return;
3131
}
3232

33-
error_reporting(-1);
33+
error_reporting(\E_ALL & ~\E_DEPRECATED & ~\E_USER_DEPRECATED);
3434

3535
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
3636
ini_set('display_errors', $debug);

src/Symfony/Component/Runtime/SymfonyRuntime.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ public function getRunner(?object $application): RunnerInterface
150150

151151
if (!$application->getName() || !$console->has($application->getName())) {
152152
$application->setName($_SERVER['argv'][0]);
153-
$console->add($application);
153+
if (method_exists($console, 'addCommand')) {
154+
$console->addCommand($application);
155+
} else {
156+
$console->add($application);
157+
}
154158
}
155159

156160
$console->setDefaultCommand($application->getName(), true);

src/Symfony/Component/Runtime/Tests/phpt/application.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
});
2626

2727
$app = new Application();
28-
$app->add($command);
28+
if (method_exists($app, 'addCommand')) {
29+
$app->addCommand($command);
30+
} else {
31+
$app->add($command);
32+
}
2933
$app->setDefaultCommand('go', true);
3034

3135
return $app;

src/Symfony/Component/Runtime/Tests/phpt/command_list.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
$command->setName('my_command');
2424

2525
[$cmd, $args] = $runtime->getResolver(require __DIR__.'/command.php')->resolve();
26-
$app->add($cmd(...$args));
26+
if (method_exists($app, 'addCommand')) {
27+
$app->addCommand($cmd(...$args));
28+
} else {
29+
$app->add($cmd(...$args));
30+
}
2731

2832
return $app;
2933
};

0 commit comments

Comments
 (0)
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