Skip to content

Commit f6435c3

Browse files
committed
refactor: extract PageCache class
1 parent bbc64f3 commit f6435c3

File tree

5 files changed

+415
-20
lines changed

5 files changed

+415
-20
lines changed

system/Cache/PageCache.php

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CodeIgniter 4 framework.
5+
*
6+
* (c) CodeIgniter Foundation <[email protected]>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace CodeIgniter\Cache;
13+
14+
use CodeIgniter\HTTP\CLIRequest;
15+
use CodeIgniter\HTTP\IncomingRequest;
16+
use CodeIgniter\HTTP\ResponseInterface;
17+
use Config\Cache as CacheConfig;
18+
use Exception;
19+
20+
/**
21+
* Web Page Caching
22+
*/
23+
class PageCache
24+
{
25+
/**
26+
* Whether to take the URL query string into consideration when generating
27+
* output cache files. Valid options are:
28+
*
29+
* false = Disabled
30+
* true = Enabled, take all query parameters into account.
31+
* Please be aware that this may result in numerous cache
32+
* files generated for the same page over and over again.
33+
* array('q') = Enabled, but only take into account the specified list
34+
* of query parameters.
35+
*
36+
* @var bool|string[]
37+
*/
38+
protected $cacheQueryString = false;
39+
40+
protected CacheInterface $cache;
41+
42+
public function __construct(CacheConfig $config, CacheInterface $cache)
43+
{
44+
$this->cacheQueryString = $config->cacheQueryString;
45+
$this->cache = $cache;
46+
}
47+
48+
/**
49+
* Generates the cache key to use from the current request.
50+
*
51+
* @param CLIRequest|IncomingRequest $request
52+
*
53+
* @internal for testing purposes only
54+
*/
55+
public function generateCacheKey($request): string
56+
{
57+
if ($request instanceof CLIRequest) {
58+
return md5($request->getPath());
59+
}
60+
61+
$uri = clone $request->getUri();
62+
63+
$query = $this->cacheQueryString
64+
? $uri->getQuery(is_array($this->cacheQueryString) ? ['only' => $this->cacheQueryString] : [])
65+
: '';
66+
67+
return md5($uri->setFragment('')->setQuery($query));
68+
}
69+
70+
/**
71+
* Caches the full response from the current request.
72+
*
73+
* @param CLIRequest|IncomingRequest $request
74+
*
75+
* @params int $ttl time to live in seconds.
76+
*/
77+
public function cachePage($request, ResponseInterface $response, int $ttl): bool
78+
{
79+
$headers = [];
80+
81+
foreach ($response->headers() as $header) {
82+
$headers[$header->getName()] = $header->getValueLine();
83+
}
84+
85+
return $this->cache->save(
86+
$this->generateCacheKey($request),
87+
serialize(['headers' => $headers, 'output' => $response->getBody()]),
88+
$ttl
89+
);
90+
}
91+
92+
/**
93+
* Gets the cached response for the request.
94+
*
95+
* @param CLIRequest|IncomingRequest $request
96+
*/
97+
public function getCachedResponse($request, ResponseInterface $response): ?ResponseInterface
98+
{
99+
if ($cachedResponse = $this->cache->get($this->generateCacheKey($request))) {
100+
$cachedResponse = unserialize($cachedResponse);
101+
102+
if (
103+
! is_array($cachedResponse)
104+
|| ! isset($cachedResponse['output'])
105+
|| ! isset($cachedResponse['headers'])
106+
) {
107+
throw new Exception('Error unserializing page cache');
108+
}
109+
110+
$headers = $cachedResponse['headers'];
111+
$output = $cachedResponse['output'];
112+
113+
// Clear all default headers
114+
foreach (array_keys($response->headers()) as $key) {
115+
$response->removeHeader($key);
116+
}
117+
118+
// Set cached headers
119+
foreach ($headers as $name => $value) {
120+
$response->setHeader($name, $value);
121+
}
122+
123+
$response->setBody($output);
124+
125+
return $response;
126+
}
127+
128+
return null;
129+
}
130+
}

system/CodeIgniter.php

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace CodeIgniter;
1313

1414
use Closure;
15+
use CodeIgniter\Cache\PageCache;
1516
use CodeIgniter\Debug\Timer;
1617
use CodeIgniter\Events\Events;
1718
use CodeIgniter\Exceptions\FrameworkException;
@@ -175,13 +176,20 @@ class CodeIgniter
175176
*/
176177
protected int $bufferLevel;
177178

179+
/**
180+
* Web Page Caching
181+
*/
182+
protected PageCache $pageCache;
183+
178184
/**
179185
* Constructor.
180186
*/
181187
public function __construct(App $config)
182188
{
183189
$this->startTime = microtime(true);
184190
$this->config = $config;
191+
192+
$this->pageCache = Services::pagecache();
185193
}
186194

187195
/**
@@ -518,7 +526,7 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache
518526
// so that we can have live speed updates along the way.
519527
// Must be run after filters to preserve the Response headers.
520528
if (static::$cacheTTL > 0) {
521-
$this->cachePage($cacheConfig);
529+
$this->pageCache->cachePage($this->request, $this->response, static::$cacheTTL);
522530
}
523531

524532
// Update the performance metrics
@@ -674,27 +682,11 @@ protected function forceSecureAccess($duration = 31_536_000)
674682
*/
675683
public function displayCache(Cache $config)
676684
{
677-
if ($cachedResponse = cache()->get($this->generateCacheName($config))) {
678-
$cachedResponse = unserialize($cachedResponse);
679-
if (! is_array($cachedResponse) || ! isset($cachedResponse['output']) || ! isset($cachedResponse['headers'])) {
680-
throw new Exception('Error unserializing page cache');
681-
}
682-
683-
$headers = $cachedResponse['headers'];
684-
$output = $cachedResponse['output'];
685-
686-
// Clear all default headers
687-
foreach (array_keys($this->response->headers()) as $key) {
688-
$this->response->removeHeader($key);
689-
}
690-
691-
// Set cached headers
692-
foreach ($headers as $name => $value) {
693-
$this->response->setHeader($name, $value);
694-
}
685+
if ($cachedResponse = $this->pageCache->getCachedResponse($this->request, $this->response)) {
686+
$this->response = $cachedResponse;
695687

696688
$this->totalTime = $this->benchmark->getElapsedTime('total_execution');
697-
$output = $this->displayPerformanceMetrics($output);
689+
$output = $this->displayPerformanceMetrics($cachedResponse->getBody());
698690
$this->response->setBody($output);
699691

700692
return $this->response;
@@ -716,6 +708,8 @@ public static function cache(int $time)
716708
* full-page caching for very high performance.
717709
*
718710
* @return bool
711+
*
712+
* @deprecated 4.4.0 No longer used.
719713
*/
720714
public function cachePage(Cache $config)
721715
{
@@ -741,6 +735,8 @@ public function getPerformanceStats(): array
741735

742736
/**
743737
* Generates the cache name to use for our full-page caching.
738+
*
739+
* @deprecated 4.4.0 No longer used.
744740
*/
745741
protected function generateCacheName(Cache $config): string
746742
{

system/Config/BaseService.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use CodeIgniter\Autoloader\Autoloader;
1515
use CodeIgniter\Autoloader\FileLocator;
1616
use CodeIgniter\Cache\CacheInterface;
17+
use CodeIgniter\Cache\PageCache;
1718
use CodeIgniter\CLI\Commands;
1819
use CodeIgniter\CodeIgniter;
1920
use CodeIgniter\Database\ConnectionInterface;
@@ -111,6 +112,7 @@
111112
* @method static Logger logger($getShared = true)
112113
* @method static MigrationRunner migrations(Migrations $config = null, ConnectionInterface $db = null, $getShared = true)
113114
* @method static Negotiate negotiator(RequestInterface $request = null, $getShared = true)
115+
* @method static PageCache pagecache(?Cache $config = null, ?CacheInterface $cache = null, bool $getShared = true)
114116
* @method static Pager pager(ConfigPager $config = null, RendererInterface $view = null, $getShared = true)
115117
* @method static Parser parser($viewPath = null, ConfigView $config = null, $getShared = true)
116118
* @method static RedirectResponse redirectresponse(App $config = null, $getShared = true)

system/Config/Services.php

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

1414
use CodeIgniter\Cache\CacheFactory;
1515
use CodeIgniter\Cache\CacheInterface;
16+
use CodeIgniter\Cache\PageCache;
1617
use CodeIgniter\CLI\Commands;
1718
use CodeIgniter\CodeIgniter;
1819
use CodeIgniter\Database\ConnectionInterface;
@@ -438,6 +439,23 @@ public static function negotiator(?RequestInterface $request = null, bool $getSh
438439
return new Negotiate($request);
439440
}
440441

442+
/**
443+
* Return the PageCache.
444+
*
445+
* @return PageCache
446+
*/
447+
public static function pagecache(?Cache $config = null, ?CacheInterface $cache = null, bool $getShared = true)
448+
{
449+
if ($getShared) {
450+
return static::getSharedInstance('pagecache', $config, $cache);
451+
}
452+
453+
$config ??= config(Cache::class);
454+
$cache ??= AppServices::cache();
455+
456+
return new PageCache($config, $cache);
457+
}
458+
441459
/**
442460
* Return the appropriate pagination handler.
443461
*

0 commit comments

Comments
 (0)