Merge pull request #7644 from kenjis/refactor-PageCache

refactor: extract ResponseCache class for Web Page Caching
This commit is contained in:
kenjis 2023-07-05 10:52:11 +09:00 committed by GitHub
commit 0d154876b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 503 additions and 51 deletions

View File

@ -222,6 +222,10 @@ parameters:
- Cache
skip_violations:
# Individual class exemptions
CodeIgniter\Cache\ResponseCache:
- CodeIgniter\HTTP\CLIRequest
- CodeIgniter\HTTP\IncomingRequest
- CodeIgniter\HTTP\ResponseInterface
CodeIgniter\Entity\Cast\URICast:
- CodeIgniter\HTTP\URI
CodeIgniter\Log\Handlers\ChromeLoggerHandler:

View File

@ -25,16 +25,6 @@ parameters:
count: 1
path: system/Cache/Handlers/RedisHandler.php
-
message: "#^Call to an undefined method CodeIgniter\\\\HTTP\\\\Request\\:\\:getPost\\(\\)\\.$#"
count: 1
path: system/CodeIgniter.php
-
message: "#^Call to an undefined method CodeIgniter\\\\HTTP\\\\Request\\:\\:setLocale\\(\\)\\.$#"
count: 1
path: system/CodeIgniter.php
-
message: "#^Property CodeIgniter\\\\Database\\\\BaseBuilder\\:\\:\\$db \\(CodeIgniter\\\\Database\\\\BaseConnection\\) in empty\\(\\) is not falsy\\.$#"
count: 1

View File

@ -0,0 +1,149 @@
<?php
/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace CodeIgniter\Cache;
use CodeIgniter\HTTP\CLIRequest;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Cache as CacheConfig;
use Exception;
/**
* Web Page Caching
*/
final class ResponseCache
{
/**
* Whether to take the URL query string into consideration when generating
* output cache files. Valid options are:
*
* false = Disabled
* true = Enabled, take all query parameters into account.
* Please be aware that this may result in numerous cache
* files generated for the same page over and over again.
* array('q') = Enabled, but only take into account the specified list
* of query parameters.
*
* @var bool|string[]
*/
private $cacheQueryString = false;
/**
* Cache time to live.
*
* @var int seconds
*/
private int $ttl = 0;
private CacheInterface $cache;
public function __construct(CacheConfig $config, CacheInterface $cache)
{
$this->cacheQueryString = $config->cacheQueryString;
$this->cache = $cache;
}
/**
* @return $this
*/
public function setTtl(int $ttl)
{
$this->ttl = $ttl;
return $this;
}
/**
* Generates the cache key to use from the current request.
*
* @param CLIRequest|IncomingRequest $request
*
* @internal for testing purposes only
*/
public function generateCacheKey($request): string
{
if ($request instanceof CLIRequest) {
return md5($request->getPath());
}
$uri = clone $request->getUri();
$query = $this->cacheQueryString
? $uri->getQuery(is_array($this->cacheQueryString) ? ['only' => $this->cacheQueryString] : [])
: '';
return md5($uri->setFragment('')->setQuery($query));
}
/**
* Caches the response.
*
* @param CLIRequest|IncomingRequest $request
*/
public function make($request, ResponseInterface $response): bool
{
if ($this->ttl === 0) {
return true;
}
$headers = [];
foreach ($response->headers() as $header) {
$headers[$header->getName()] = $header->getValueLine();
}
return $this->cache->save(
$this->generateCacheKey($request),
serialize(['headers' => $headers, 'output' => $response->getBody()]),
$this->ttl
);
}
/**
* Gets the cached response for the request.
*
* @param CLIRequest|IncomingRequest $request
*/
public function get($request, ResponseInterface $response): ?ResponseInterface
{
if ($cachedResponse = $this->cache->get($this->generateCacheKey($request))) {
$cachedResponse = unserialize($cachedResponse);
if (
! is_array($cachedResponse)
|| ! isset($cachedResponse['output'])
|| ! isset($cachedResponse['headers'])
) {
throw new Exception('Error unserializing page cache');
}
$headers = $cachedResponse['headers'];
$output = $cachedResponse['output'];
// Clear all default headers
foreach (array_keys($response->headers()) as $key) {
$response->removeHeader($key);
}
// Set cached headers
foreach ($headers as $name => $value) {
$response->setHeader($name, $value);
}
$response->setBody($output);
return $response;
}
return null;
}
}

View File

@ -12,6 +12,7 @@
namespace CodeIgniter;
use Closure;
use CodeIgniter\Cache\ResponseCache;
use CodeIgniter\Debug\Timer;
use CodeIgniter\Events\Events;
use CodeIgniter\Exceptions\FrameworkException;
@ -84,7 +85,7 @@ class CodeIgniter
/**
* Current request.
*
* @var CLIRequest|IncomingRequest|Request|null
* @var CLIRequest|IncomingRequest|null
*/
protected $request;
@ -127,6 +128,8 @@ class CodeIgniter
* Cache expiration time
*
* @var int seconds
*
* @deprecated 4.4.0 Moved to ResponseCache::$ttl. No longer used.
*/
protected static $cacheTTL = 0;
@ -175,6 +178,11 @@ class CodeIgniter
*/
protected int $bufferLevel;
/**
* Web Page Caching
*/
protected ResponseCache $pageCache;
/**
* Constructor.
*/
@ -182,6 +190,8 @@ class CodeIgniter
{
$this->startTime = microtime(true);
$this->config = $config;
$this->pageCache = Services::responsecache();
}
/**
@ -330,7 +340,7 @@ class CodeIgniter
);
}
static::$cacheTTL = 0;
$this->pageCache->setTtl(0);
$this->bufferLevel = ob_get_level();
$this->startBenchmark();
@ -463,7 +473,7 @@ class CodeIgniter
return $possibleResponse;
}
if ($possibleResponse instanceof Request) {
if ($possibleResponse instanceof IncomingRequest || $possibleResponse instanceof CLIRequest) {
$this->request = $possibleResponse;
}
}
@ -517,9 +527,7 @@ class CodeIgniter
// Cache it without the performance metrics replaced
// so that we can have live speed updates along the way.
// Must be run after filters to preserve the Response headers.
if (static::$cacheTTL > 0) {
$this->cachePage($cacheConfig);
}
$this->pageCache->make($this->request, $this->response);
// Update the performance metrics
$body = $this->response->getBody();
@ -603,9 +611,11 @@ class CodeIgniter
* Sets a Request object to be used for this request.
* Used when running certain tests.
*
* @param CLIRequest|IncomingRequest $request
*
* @return $this
*/
public function setRequest(Request $request)
public function setRequest($request)
{
$this->request = $request;
@ -674,27 +684,11 @@ class CodeIgniter
*/
public function displayCache(Cache $config)
{
if ($cachedResponse = cache()->get($this->generateCacheName($config))) {
$cachedResponse = unserialize($cachedResponse);
if (! is_array($cachedResponse) || ! isset($cachedResponse['output']) || ! isset($cachedResponse['headers'])) {
throw new Exception('Error unserializing page cache');
}
$headers = $cachedResponse['headers'];
$output = $cachedResponse['output'];
// Clear all default headers
foreach (array_keys($this->response->headers()) as $key) {
$this->response->removeHeader($key);
}
// Set cached headers
foreach ($headers as $name => $value) {
$this->response->setHeader($name, $value);
}
if ($cachedResponse = $this->pageCache->get($this->request, $this->response)) {
$this->response = $cachedResponse;
$this->totalTime = $this->benchmark->getElapsedTime('total_execution');
$output = $this->displayPerformanceMetrics($output);
$output = $this->displayPerformanceMetrics($cachedResponse->getBody());
$this->response->setBody($output);
return $this->response;
@ -705,6 +699,8 @@ class CodeIgniter
/**
* Tells the app that the final output should be cached.
*
* @deprecated 4.4.0 Moved to ResponseCache::setTtl(). to No longer used.
*/
public static function cache(int $time)
{
@ -716,6 +712,8 @@ class CodeIgniter
* full-page caching for very high performance.
*
* @return bool
*
* @deprecated 4.4.0 No longer used.
*/
public function cachePage(Cache $config)
{
@ -741,6 +739,8 @@ class CodeIgniter
/**
* Generates the cache name to use for our full-page caching.
*
* @deprecated 4.4.0 No longer used.
*/
protected function generateCacheName(Cache $config): string
{

View File

@ -14,6 +14,7 @@ namespace CodeIgniter\Config;
use CodeIgniter\Autoloader\Autoloader;
use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Cache\CacheInterface;
use CodeIgniter\Cache\ResponseCache;
use CodeIgniter\CLI\Commands;
use CodeIgniter\CodeIgniter;
use CodeIgniter\Database\ConnectionInterface;
@ -117,6 +118,7 @@ use Config\View as ConfigView;
* @method static View renderer($viewPath = null, ConfigView $config = null, $getShared = true)
* @method static IncomingRequest|CLIRequest request(App $config = null, $getShared = true)
* @method static ResponseInterface response(App $config = null, $getShared = true)
* @method static ResponseCache responsecache(?Cache $config = null, ?CacheInterface $cache = null, bool $getShared = true)
* @method static Router router(RouteCollectionInterface $routes = null, Request $request = null, $getShared = true)
* @method static RouteCollection routes($getShared = true)
* @method static Security security(App $config = null, $getShared = true)

View File

@ -13,6 +13,7 @@ namespace CodeIgniter\Config;
use CodeIgniter\Cache\CacheFactory;
use CodeIgniter\Cache\CacheInterface;
use CodeIgniter\Cache\ResponseCache;
use CodeIgniter\CLI\Commands;
use CodeIgniter\CodeIgniter;
use CodeIgniter\Database\ConnectionInterface;
@ -438,6 +439,23 @@ class Services extends BaseService
return new Negotiate($request);
}
/**
* Return the ResponseCache.
*
* @return ResponseCache
*/
public static function responsecache(?Cache $config = null, ?CacheInterface $cache = null, bool $getShared = true)
{
if ($getShared) {
return static::getSharedInstance('responsecache', $config, $cache);
}
$config ??= config(Cache::class);
$cache ??= AppServices::cache();
return new ResponseCache($config, $cache);
}
/**
* Return the appropriate pagination handler.
*

View File

@ -104,12 +104,13 @@ class Controller
}
/**
* Provides a simple way to tie into the main CodeIgniter class and
* tell it how long to cache the current page for.
* How long to cache the current page for.
*
* @params int $time time to live in seconds.
*/
protected function cachePage(int $time)
{
CodeIgniter::cache($time);
Services::responsecache()->setTtl($time);
}
/**

View File

@ -12,6 +12,7 @@
namespace CodeIgniter\Test;
use CodeIgniter\Events\Events;
use CodeIgniter\HTTP\CLIRequest;
use CodeIgniter\HTTP\Exceptions\RedirectException;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\Request;
@ -328,11 +329,13 @@ class FeatureTestCase extends CIUnitTestCase
*
* Always populate the GET vars based on the URI.
*
* @return Request
* @param CLIRequest|IncomingRequest $request
*
* @return CLIRequest|IncomingRequest
*
* @throws ReflectionException
*/
protected function populateGlobals(string $method, Request $request, ?array $params = null)
protected function populateGlobals(string $method, $request, ?array $params = null)
{
// $params should set the query vars if present,
// otherwise set it from the URL.
@ -357,10 +360,13 @@ class FeatureTestCase extends CIUnitTestCase
* This allows the body to be formatted in a way that the controller is going to
* expect as in the case of testing a JSON or XML API.
*
* @param array|null $params The parameters to be formatted and put in the body. If this is empty, it will get the
* what has been loaded into the request global of the request class.
* @param CLIRequest|IncomingRequest $request
* @param array|null $params The parameters to be formatted and put in the body. If this is empty, it will get the
* what has been loaded into the request global of the request class.
*
* @return CLIRequest|IncomingRequest
*/
protected function setRequestBody(Request $request, ?array $params = null): Request
protected function setRequestBody($request, ?array $params = null)
{
if (isset($this->requestBody) && $this->requestBody !== '') {
$request->setBody($this->requestBody);

View File

@ -0,0 +1,249 @@
<?php
/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace CodeIgniter\Cache;
use CodeIgniter\HTTP\CLIRequest;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\Response;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use Config\App as AppConfig;
use Config\Cache as CacheConfig;
use ErrorException;
use Exception;
/**
* @backupGlobals enabled
*
* @internal
*
* @group Others
*/
final class ResponseCacheTest extends CIUnitTestCase
{
private AppConfig $appConfig;
protected function setUp(): void
{
parent::setUp();
$this->appConfig = new AppConfig();
}
private function createIncomingRequest(
string $uri = '',
array $query = [],
?AppConfig $appConfig = null
): IncomingRequest {
$_POST = $_GET = $_SERVER = $_REQUEST = $_ENV = $_COOKIE = $_SESSION = [];
$_SERVER['REQUEST_URI'] = '/' . $uri . ($query ? '?' . http_build_query($query) : '');
$_SERVER['SCRIPT_NAME'] = '/index.php';
$appConfig ??= $this->appConfig;
$siteUri = new URI($appConfig->baseURL . $uri);
if ($query !== []) {
$_GET = $_REQUEST = $query;
$siteUri->setQueryArray($query);
}
return new IncomingRequest(
$appConfig,
$siteUri,
null,
new UserAgent()
);
}
/**
* @phpstan-param list<string> $params
*/
private function createCLIRequest(array $params = [], ?AppConfig $appConfig = null): CLIRequest
{
$_POST = $_GET = $_SERVER = $_REQUEST = $_ENV = $_COOKIE = $_SESSION = [];
$_SERVER['argv'] = ['public/index.php', ...$params];
$_SERVER['SCRIPT_NAME'] = 'public/index.php';
$appConfig ??= $this->appConfig;
return new CLIRequest($appConfig);
}
private function createResponseCache(?CacheConfig $cacheConfig = null): ResponseCache
{
$cache = mock(CacheFactory::class);
$cacheConfig ??= new CacheConfig();
return (new ResponseCache($cacheConfig, $cache))->setTtl(300);
}
public function testCachePageIncomingRequest()
{
$pageCache = $this->createResponseCache();
$request = $this->createIncomingRequest('foo/bar');
$response = new Response($this->appConfig);
$response->setHeader('ETag', 'abcd1234');
$response->setBody('The response body.');
$return = $pageCache->make($request, $response);
$this->assertTrue($return);
// Check cache with a request with the same URI path.
$request = $this->createIncomingRequest('foo/bar');
$cachedResponse = $pageCache->get($request, new Response($this->appConfig));
$this->assertInstanceOf(ResponseInterface::class, $cachedResponse);
$this->assertSame('The response body.', $cachedResponse->getBody());
$this->assertSame('abcd1234', $cachedResponse->getHeaderLine('ETag'));
// Check cache with a request with the same URI path and different query string.
$request = $this->createIncomingRequest('foo/bar', ['foo' => 'bar', 'bar' => 'baz']);
$cachedResponse = $pageCache->get($request, new Response($this->appConfig));
$this->assertInstanceOf(ResponseInterface::class, $cachedResponse);
$this->assertSame('The response body.', $cachedResponse->getBody());
$this->assertSame('abcd1234', $cachedResponse->getHeaderLine('ETag'));
// Check cache with another request with the different URI path.
$request = $this->createIncomingRequest('another');
$cachedResponse = $pageCache->get($request, new Response($this->appConfig));
$this->assertNull($cachedResponse);
}
public function testCachePageIncomingRequestWithCacheQueryString()
{
$cacheConfig = new CacheConfig();
$cacheConfig->cacheQueryString = true;
$pageCache = $this->createResponseCache($cacheConfig);
$request = $this->createIncomingRequest('foo/bar', ['foo' => 'bar', 'bar' => 'baz']);
$response = new Response($this->appConfig);
$response->setHeader('ETag', 'abcd1234');
$response->setBody('The response body.');
$return = $pageCache->make($request, $response);
$this->assertTrue($return);
// Check cache with a request with the same URI path and same query string.
$this->createIncomingRequest('foo/bar', ['foo' => 'bar', 'bar' => 'baz']);
$cachedResponse = $pageCache->get($request, new Response($this->appConfig));
$this->assertInstanceOf(ResponseInterface::class, $cachedResponse);
$this->assertSame('The response body.', $cachedResponse->getBody());
$this->assertSame('abcd1234', $cachedResponse->getHeaderLine('ETag'));
// Check cache with a request with the same URI path and different query string.
$request = $this->createIncomingRequest('foo/bar', ['xfoo' => 'bar', 'bar' => 'baz']);
$cachedResponse = $pageCache->get($request, new Response($this->appConfig));
$this->assertNull($cachedResponse);
// Check cache with another request with the different URI path.
$request = $this->createIncomingRequest('another');
$cachedResponse = $pageCache->get($request, new Response($this->appConfig));
$this->assertNull($cachedResponse);
}
public function testCachePageCLIRequest()
{
$pageCache = $this->createResponseCache();
$request = $this->createCLIRequest(['foo', 'bar']);
$response = new Response($this->appConfig);
$response->setBody('The response body.');
$return = $pageCache->make($request, $response);
$this->assertTrue($return);
// Check cache with a request with the same params.
$request = $this->createCLIRequest(['foo', 'bar']);
$cachedResponse = $pageCache->get($request, new Response($this->appConfig));
$this->assertInstanceOf(ResponseInterface::class, $cachedResponse);
$this->assertSame('The response body.', $cachedResponse->getBody());
// Check cache with another request with the different params.
$request = $this->createCLIRequest(['baz']);
$cachedResponse = $pageCache->get($request, new Response($this->appConfig));
$this->assertNull($cachedResponse);
}
public function testUnserializeError()
{
$this->expectException(ErrorException::class);
$this->expectExceptionMessage('unserialize(): Error at offset 0 of 12 bytes');
$cache = mock(CacheFactory::class);
$cacheConfig = new CacheConfig();
$pageCache = new ResponseCache($cacheConfig, $cache);
$request = $this->createIncomingRequest('foo/bar');
$response = new Response($this->appConfig);
$response->setHeader('ETag', 'abcd1234');
$response->setBody('The response body.');
$pageCache->make($request, $response);
$cacheKey = $pageCache->generateCacheKey($request);
// Save invalid data.
$cache->save($cacheKey, 'Invalid data');
// Check cache with a request with the same URI path.
$pageCache->get($request, new Response($this->appConfig));
}
public function testInvalidCacheError()
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('Error unserializing page cache');
$cache = mock(CacheFactory::class);
$cacheConfig = new CacheConfig();
$pageCache = new ResponseCache($cacheConfig, $cache);
$request = $this->createIncomingRequest('foo/bar');
$response = new Response($this->appConfig);
$response->setHeader('ETag', 'abcd1234');
$response->setBody('The response body.');
$pageCache->make($request, $response);
$cacheKey = $pageCache->generateCacheKey($request);
// Save invalid data.
$cache->save($cacheKey, serialize(['a' => '1']));
// Check cache with a request with the same URI path.
$pageCache->get($request, new Response($this->appConfig));
}
}

View File

@ -96,6 +96,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/pages/about';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -193,6 +194,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/pages/about';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -216,6 +218,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/pages/about';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -244,6 +247,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/pages/about';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -268,6 +272,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/pages/about';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -298,6 +303,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/pages/about';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$routes = Services::routes();
$routes->add(
@ -331,6 +337,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/pages/about';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -452,6 +459,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/example';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -475,6 +483,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/example';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -501,6 +510,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/example';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$_SERVER['REQUEST_METHOD'] = 'GET';
@ -527,6 +537,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/example';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$_SERVER['REQUEST_METHOD'] = 'GET';
@ -551,6 +562,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/example';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$_SERVER['REQUEST_METHOD'] = 'POST';
@ -615,6 +627,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/example';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$_SERVER['REQUEST_METHOD'] = 'GET';
@ -638,6 +651,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/image';
$_SERVER['SCRIPT_NAME'] = '/index.php';
// Inject mock router.
$routes = Services::routes();
@ -679,6 +693,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 2;
$_SERVER['REQUEST_URI'] = '/cli';
$_SERVER['SCRIPT_NAME'] = 'public/index.php';
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$_SERVER['REQUEST_METHOD'] = 'CLI';
@ -698,6 +713,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 1;
$_SERVER['REQUEST_URI'] = '/';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$_SERVER['REQUEST_METHOD'] = 'POST';
@ -722,6 +738,7 @@ final class CodeIgniterTest extends CIUnitTestCase
$_SERVER['argc'] = 1;
$_SERVER['REQUEST_URI'] = '/';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$_SERVER['REQUEST_METHOD'] = 'POST';
@ -754,6 +771,7 @@ final class CodeIgniterTest extends CIUnitTestCase
command('cache:clear');
$_SERVER['REQUEST_URI'] = '/test';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$routes = Services::routes();
$routes->add('test', static function () {
@ -832,13 +850,14 @@ final class CodeIgniterTest extends CIUnitTestCase
foreach ($testingUrls as $testingUrl) {
$this->resetServices();
$_SERVER['REQUEST_URI'] = '/' . $testingUrl;
$_SERVER['SCRIPT_NAME'] = '/index.php';
$this->codeigniter = new MockCodeIgniter(new App());
$routes = Services::routes(true);
$routePath = explode('?', $testingUrl)[0];
$string = 'This is a test page, to check cache configuration';
$routes->add($routePath, static function () use ($string) {
CodeIgniter::cache(60);
Services::responsecache()->setTtl(60);
$response = Services::response();
return $response->setBody($string);

View File

@ -52,15 +52,24 @@ Parameter Type Changes
- **Services:** The first parameter of ``Services::security()`` has been
changed from ``Config\App`` to ``Config\Security``.
- **Session:** The second parameter of ``Session::__construct()`` has been
changed from ``Config\App`` to ``Config\Session``.
- **Session:** The first parameter of ``__construct()`` in ``BaseHandler``,
``DatabaseHandler``, ``FileHandler``, ``MemcachedHandler``, and ``RedisHandler``
has been changed from ``Config\App`` to ``Config\Session``.
- **Session:**
- The second parameter of ``Session::__construct()`` has been changed from
``Config\App`` to ``Config\Session``.
- The first parameter of ``__construct()`` in the database's ``BaseHandler``,
``DatabaseHandler``, ``FileHandler``, ``MemcachedHandler``, and ``RedisHandler``
has been changed from ``Config\App`` to ``Config\Session``.
- **Security:** The first parameter of ``Security::__construct()`` has been
changed from ``Config\App`` to ``Config\Security``.
- **Validation:** The method signature of ``Validation::check()`` has been changed.
The ``string`` typehint on the ``$rule`` parameter was removed.
- **CodeIgniter:** The method signature of ``CodeIgniter::setRequest()`` has been
changed. The ``Request`` typehint on the ``$request`` parameter was removed.
- **FeatureTestCase:**
- The method signature of ``FeatureTestCase::populateGlobals()`` has been
changed. The ``Request`` typehint on the ``$request`` parameter was removed.
- The method signature of ``FeatureTestCase::setRequestBody()`` has been
changed. The ``Request`` typehint on the ``$request`` parameter and the
return type ``Request`` were removed.
Added Parameters
----------------
@ -179,7 +188,12 @@ Deprecations
are deprecated. Because these methods have been moved to ``BaseExceptionHandler`` or
``ExceptionHandler``.
- **Autoloader:** ``Autoloader::sanitizeFilename()`` is deprecated.
- **CodeIgniter:** ``CodeIgniter::$returnResponse`` property is deprecated. No longer used.
- **CodeIgniter:**
- ``CodeIgniter::$returnResponse`` property is deprecated. No longer used.
- ``CodeIgniter::$cacheTTL`` property is deprecated. No longer used. Use ``ResponseCache`` instead.
- ``CodeIgniter::cache()`` method is deprecated. No longer used. Use ``ResponseCache`` instead.
- ``CodeIgniter::cachePage()`` method is deprecated. No longer used. Use ``ResponseCache`` instead.
- ``CodeIgniter::generateCacheName()`` method is deprecated. No longer used. Use ``ResponseCache`` instead.
- **RedirectException:** ``\CodeIgniter\Router\Exceptions\RedirectException`` is deprecated. Use ``\CodeIgniter\HTTP\Exceptions\RedirectException`` instead.
- **Session:** The property ``$sessionDriverName``, ``$sessionCookieName``,
``$sessionExpiration``, ``$sessionSavePath``, ``$sessionMatchIP``,