From 92ecc14c4c158540a9acf6fc268c7b27d98debc7 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 11 Jul 2023 11:05:11 +0900 Subject: [PATCH 01/17] feat: add methods for caching --- system/Config/Factories.php | 58 ++++++++++++++++++++++++++- tests/system/Config/FactoriesTest.php | 55 +++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 5bf7afebd431..943c9055c982 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -80,6 +80,15 @@ class Factories */ protected static $instances = []; + /** + * Whether the component instances are updated? + * + * @var array [component => true] + * + * @internal For caching only + */ + protected static $updated = []; + /** * Define the class to load. You can *override* the concrete class. * @@ -153,6 +162,7 @@ public static function __callStatic(string $component, array $arguments) self::$instances[$options['component']][$class] = new $class(...$arguments); self::$aliases[$options['component']][$alias] = $class; + self::$updated[$options['component']] = true; // If a short classname is specified, also register FQCN to share the instance. if (! isset(self::$aliases[$options['component']][$class])) { @@ -383,7 +393,8 @@ public static function reset(?string $component = null) unset( static::$options[$component], static::$aliases[$component], - static::$instances[$component] + static::$instances[$component], + static::$updated[$component] ); return; @@ -392,6 +403,7 @@ public static function reset(?string $component = null) static::$options = []; static::$aliases = []; static::$instances = []; + static::$updated = []; } /** @@ -440,4 +452,48 @@ public static function getBasename(string $alias): string return $alias; } + + /** + * Gets component data for caching. + * + * @internal For caching only + */ + public static function getComponentInstances(string $component): array + { + if (! isset(static::$aliases[$component])) { + return [ + 'aliases' => [], + 'instances' => [], + ]; + } + + $data = [ + 'aliases' => static::$aliases[$component], + 'instances' => self::$instances[$component], + ]; + + return $data; + } + + /** + * Sets component data + * + * @internal For caching only + */ + public static function setComponentInstances(string $component, array $data) + { + static::$aliases[$component] = $data['aliases']; + self::$instances[$component] = $data['instances']; + unset(self::$updated[$component]); + } + + /** + * Whether the component instances are updated? + * + * @internal For caching only + */ + public static function isUpdated(string $component): bool + { + return isset(self::$updated[$component]) ? true : false; + } } diff --git a/tests/system/Config/FactoriesTest.php b/tests/system/Config/FactoriesTest.php index 3750113bbf73..d48a6e50141d 100644 --- a/tests/system/Config/FactoriesTest.php +++ b/tests/system/Config/FactoriesTest.php @@ -398,4 +398,59 @@ public function testDefineAndLoad() $this->assertInstanceOf(EntityModel::class, $model); } + + public function testGetComponentInstances() + { + Factories::config('App'); + Factories::config(\Config\Database::class); + + $data = Factories::getComponentInstances('config'); + + $this->assertIsArray($data); + $this->assertArrayHasKey('aliases', $data); + $this->assertArrayHasKey('instances', $data); + + return $data; + } + + /** + * @depends testGetComponentInstances + */ + public function testSetComponentInstances(array $data) + { + $before = Factories::getComponentInstances('config'); + $this->assertSame(['aliases' => [], 'instances' => []], $before); + + Factories::setComponentInstances('config', $data); + + $data = Factories::getComponentInstances('config'); + + $this->assertIsArray($data); + $this->assertArrayHasKey('aliases', $data); + $this->assertArrayHasKey('instances', $data); + + return $data; + } + + /** + * @depends testSetComponentInstances + */ + public function testIsUpdated(array $data) + { + Factories::reset(); + + $updated = $this->getFactoriesStaticProperty('updated'); + + $this->assertSame([], $updated); + $this->assertFalse(Factories::isUpdated('config')); + + Factories::config('App'); + + $this->assertTrue(Factories::isUpdated('config')); + $this->assertFalse(Factories::isUpdated('models')); + + Factories::setComponentInstances('config', $data); + + $this->assertFalse(Factories::isUpdated('config')); + } } From ccc532d871aee045947f8d85d3b0b73d9e7d14a6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 11 Jul 2023 11:37:11 +0900 Subject: [PATCH 02/17] feat: add FactoriesCache for Config caching --- system/Cache/FactoriesCache.php | 65 ++++++++++++++ .../FactoriesCache/FileVarExportHandler.php | 46 ++++++++++ .../Cache/FactoriesCacheFileHandlerTest.php | 33 +++++++ ...FactoriesCacheFileVarExportHandlerTest.php | 90 +++++++++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 system/Cache/FactoriesCache.php create mode 100644 system/Cache/FactoriesCache/FileVarExportHandler.php create mode 100644 tests/system/Cache/FactoriesCacheFileHandlerTest.php create mode 100644 tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php diff --git a/system/Cache/FactoriesCache.php b/system/Cache/FactoriesCache.php new file mode 100644 index 000000000000..7644ee5afbde --- /dev/null +++ b/system/Cache/FactoriesCache.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Cache; + +use CodeIgniter\Cache\FactoriesCache\FileVarExportHandler; +use CodeIgniter\Config\Factories; + +final class FactoriesCache +{ + /** + * @var CacheInterface|FileVarExportHandler + */ + private $cache; + + /** + * @param CacheInterface|FileVarExportHandler $cache + */ + public function __construct($cache = null) + { + $this->cache = $cache ?? new FileVarExportHandler(); + } + + public function save(string $component): void + { + if (! Factories::isUpdated($component)) { + return; + } + + $data = Factories::getComponentInstances($component); + + $this->cache->save($this->getCacheKey($component), $data, 3600 * 24); + } + + private function getCacheKey(string $component) + { + return 'FactoriesCache_' . $component; + } + + public function load(string $component): bool + { + $key = $this->getCacheKey($component); + + if (! $data = $this->cache->get($key)) { + return false; + } + + Factories::setComponentInstances($component, $data); + + return true; + } + + public function delete(string $component): void + { + $this->cache->delete($this->getCacheKey($component)); + } +} diff --git a/system/Cache/FactoriesCache/FileVarExportHandler.php b/system/Cache/FactoriesCache/FileVarExportHandler.php new file mode 100644 index 000000000000..99a71d8bd23f --- /dev/null +++ b/system/Cache/FactoriesCache/FileVarExportHandler.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Cache\FactoriesCache; + +final class FileVarExportHandler +{ + private string $path = WRITEPATH . 'cache'; + + /** + * @param array|bool|float|int|object|string|null $val + */ + public function save(string $key, $val): void + { + $val = var_export($val, true); + + // Write to temp file first to ensure atomicity + $tmp = $this->path . "/{$key}." . uniqid('', true) . '.tmp'; + file_put_contents($tmp, 'path . "/{$key}"); + } + + public function delete(string $key): void + { + @unlink($this->path . "/{$key}"); + } + + /** + * @return array|bool|float|int|object|string|null + */ + public function get(string $key) + { + @include $this->path . "/{$key}"; + + return $val ?? false; // @phpstan-ignore-line + } +} diff --git a/tests/system/Cache/FactoriesCacheFileHandlerTest.php b/tests/system/Cache/FactoriesCacheFileHandlerTest.php new file mode 100644 index 000000000000..b5434bbb1b1b --- /dev/null +++ b/tests/system/Cache/FactoriesCacheFileHandlerTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Cache; + +use Config\Cache as CacheConfig; + +/** + * @internal + * + * @group Others + */ +final class FactoriesCacheFileHandlerTest extends FactoriesCacheFileVarExportHandlerTest +{ + /** + * @var @var FileVarExportHandler|CacheInterface + */ + protected $handler; + + protected function createFactoriesCache(): void + { + $this->handler = CacheFactory::getHandler(new CacheConfig(), 'file'); + $this->cache = new FactoriesCache($this->handler); + } +} diff --git a/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php b/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php new file mode 100644 index 000000000000..3fcf751b6176 --- /dev/null +++ b/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Cache; + +use CodeIgniter\Cache\FactoriesCache\FileVarExportHandler; +use CodeIgniter\Config\Factories; +use CodeIgniter\Test\CIUnitTestCase; +use Config\App; + +/** + * @internal + * @no-final + * + * @group Others + */ +class FactoriesCacheFileVarExportHandlerTest extends CIUnitTestCase +{ + protected FactoriesCache $cache; + + /** + * @var CacheInterface|FileVarExportHandler + */ + protected $handler; + + protected function createFactoriesCache(): void + { + $this->handler = new FileVarExportHandler(); + $this->cache = new FactoriesCache($this->handler); + } + + public function testInstantiate() + { + $this->createFactoriesCache(); + + $this->assertInstanceOf(FactoriesCache::class, $this->cache); + } + + public function testSave() + { + Factories::reset(); + Factories::config('App'); + + $this->createFactoriesCache(); + + $this->cache->save('config'); + + $cachedData = $this->handler->get('FactoriesCache_config'); + + $this->assertArrayHasKey('basenames', $cachedData); + $this->assertArrayHasKey('instances', $cachedData); + $this->assertArrayHasKey('Modules', $cachedData['basenames']); + $this->assertArrayHasKey('App', $cachedData['basenames']); + } + + public function testLoad() + { + Factories::reset(); + /** @var App $appConfig */ + $appConfig = Factories::config('App'); + $appConfig->baseURL = 'http://test.example.jp/this-is-test/'; + + $this->createFactoriesCache(); + $this->cache->save('config'); + + Factories::reset(); + + $this->cache->load('config'); + + $appConfig = Factories::config('App'); + $this->assertSame('http://test.example.jp/this-is-test/', $appConfig->baseURL); + } + + public function testDelete() + { + $this->createFactoriesCache(); + + $this->cache->delete('config'); + + $this->assertFalse($this->cache->load('config')); + } +} From f9b95e3fa92dd982a84c0ee8bb8d5e41d1825a1d Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 11 Jul 2023 17:56:55 +0900 Subject: [PATCH 03/17] feat: add __set_state() for Config caching --- app/Config/Paths.php | 13 +++++++++++++ system/Config/BaseConfig.php | 17 +++++++++++++++++ system/Modules/Modules.php | 20 ++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/app/Config/Paths.php b/app/Config/Paths.php index 262c745ffa95..35a941940b5e 100644 --- a/app/Config/Paths.php +++ b/app/Config/Paths.php @@ -77,4 +77,17 @@ class Paths * is used when no value is provided to `Services::renderer()`. */ public string $viewDirectory = __DIR__ . '/../Views'; + + public static function __set_state(array $array) + { + $obj = new self(); + + $properties = array_keys(get_object_vars($obj)); + + foreach ($properties as $property) { + $obj->{$property} = $array[$property]; + } + + return $obj; + } } diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index 0958d7ae7f75..fad688ab6214 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -26,6 +26,8 @@ * from the environment. * * These can be set within the .env file. + * + * @phpstan-consistent-constructor */ class BaseConfig { @@ -51,6 +53,21 @@ class BaseConfig */ protected static $moduleConfig; + public static function __set_state(array $array) + { + static::$override = false; + $obj = new static(); + static::$override = true; + + $properties = array_keys(get_object_vars($obj)); + + foreach ($properties as $property) { + $obj->{$property} = $array[$property]; + } + + return $obj; + } + /** * Will attempt to get environment variables with names * that match the properties of the child class. diff --git a/system/Modules/Modules.php b/system/Modules/Modules.php index ade3e693ceea..f99e7215e733 100644 --- a/system/Modules/Modules.php +++ b/system/Modules/Modules.php @@ -15,6 +15,8 @@ * Modules Class * * @see https://codeigniter.com/user_guide/general/modules.html + * + * @phpstan-consistent-constructor */ class Modules { @@ -39,6 +41,11 @@ class Modules */ public $aliases = []; + public function __construct() + { + // For @phpstan-consistent-constructor + } + /** * Should the application auto-discover the requested resource. */ @@ -50,4 +57,17 @@ public function shouldDiscover(string $alias): bool return in_array(strtolower($alias), $this->aliases, true); } + + public static function __set_state(array $array) + { + $obj = new static(); + + $properties = array_keys(get_object_vars($obj)); + + foreach ($properties as $property) { + $obj->{$property} = $array[$property]; + } + + return $obj; + } } From db612fab7c62cb0db7305d92f5ac6d0c22f6cb45 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 11 Jul 2023 17:59:58 +0900 Subject: [PATCH 04/17] refactor: move definition of ENVIRONMENT to index.php/spark For Config caching. --- public/index.php | 5 +++++ spark | 5 +++++ system/CodeIgniter.php | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/public/index.php b/public/index.php index d031ea10944a..0cdc61b05ddd 100644 --- a/public/index.php +++ b/public/index.php @@ -43,6 +43,11 @@ require_once SYSTEMPATH . 'Config/DotEnv.php'; (new CodeIgniter\Config\DotEnv(ROOTPATH))->load(); +// Define ENVIRONMENT +if (! defined('ENVIRONMENT')) { + define('ENVIRONMENT', env('CI_ENVIRONMENT', 'production')); +} + /* * --------------------------------------------------------------- * GRAB OUR CODEIGNITER INSTANCE diff --git a/spark b/spark index f2ba3f305ceb..2ea79d5ccdaf 100755 --- a/spark +++ b/spark @@ -78,6 +78,11 @@ require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstra require_once SYSTEMPATH . 'Config/DotEnv.php'; (new CodeIgniter\Config\DotEnv(ROOTPATH))->load(); +// Define ENVIRONMENT +if (! defined('ENVIRONMENT')) { + define('ENVIRONMENT', env('CI_ENVIRONMENT', 'production')); +} + // Grab our CodeIgniter $app = Config\Services::codeigniter(); $app->initialize(); diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 8bdcc15bc16e..3664c0321fa2 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -200,7 +200,6 @@ public function __construct(App $config) public function initialize() { // Define environment variables - $this->detectEnvironment(); $this->bootstrapEnvironment(); // Setup Exception Handling @@ -560,6 +559,8 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache * production * * @codeCoverageIgnore + * + * @deprecated 4.4.0 No longer used. Moved to index.php and spark. */ protected function detectEnvironment() { From 9e4225b8e7e3381824b73be6a844436f897902db Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 11 Jul 2023 18:02:40 +0900 Subject: [PATCH 05/17] feat: add property to stop Config property override --- system/Config/BaseConfig.php | 41 +++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index fad688ab6214..127f70f13667 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -39,6 +39,11 @@ class BaseConfig */ public static $registrars = []; + /** + * Whether to override properties by Env vars and Registrars. + */ + public static bool $override = true; + /** * Has module discovery happened yet? * @@ -78,23 +83,25 @@ public function __construct() { static::$moduleConfig = config(Modules::class); - $this->registerProperties(); - - $properties = array_keys(get_object_vars($this)); - $prefix = static::class; - $slashAt = strrpos($prefix, '\\'); - $shortPrefix = strtolower(substr($prefix, $slashAt === false ? 0 : $slashAt + 1)); - - foreach ($properties as $property) { - $this->initEnvValue($this->{$property}, $property, $prefix, $shortPrefix); - - if ($this instanceof Encryption && $property === 'key') { - if (strpos($this->{$property}, 'hex2bin:') === 0) { - // Handle hex2bin prefix - $this->{$property} = hex2bin(substr($this->{$property}, 8)); - } elseif (strpos($this->{$property}, 'base64:') === 0) { - // Handle base64 prefix - $this->{$property} = base64_decode(substr($this->{$property}, 7), true); + if (static::$override) { + $this->registerProperties(); + + $properties = array_keys(get_object_vars($this)); + $prefix = static::class; + $slashAt = strrpos($prefix, '\\'); + $shortPrefix = strtolower(substr($prefix, $slashAt === false ? 0 : $slashAt + 1)); + + foreach ($properties as $property) { + $this->initEnvValue($this->{$property}, $property, $prefix, $shortPrefix); + + if ($this instanceof Encryption && $property === 'key') { + if (strpos($this->{$property}, 'hex2bin:') === 0) { + // Handle hex2bin prefix + $this->{$property} = hex2bin(substr($this->{$property}, 8)); + } elseif (strpos($this->{$property}, 'base64:') === 0) { + // Handle base64 prefix + $this->{$property} = base64_decode(substr($this->{$property}, 7), true); + } } } } From b01bb38d8609930ec208cda04ecd94f02d440d4e Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 11 Jul 2023 18:04:05 +0900 Subject: [PATCH 06/17] refactor: use config() instead of new keyword --- system/Config/Services.php | 2 +- system/HTTP/UserAgent.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Config/Services.php b/system/Config/Services.php index ffca4fa804ff..6bd16cc30213 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -117,7 +117,7 @@ public static function cache(?Cache $config = null, bool $getShared = true) return static::getSharedInstance('cache', $config); } - $config ??= new Cache(); + $config ??= config(Cache::class); return CacheFactory::getHandler($config); } diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 4b579229d88f..1ccc2109c119 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -102,7 +102,7 @@ class UserAgent */ public function __construct(?UserAgents $config = null) { - $this->config = $config ?? new UserAgents(); + $this->config = $config ?? config(UserAgents::class); if (isset($_SERVER['HTTP_USER_AGENT'])) { $this->agent = trim($_SERVER['HTTP_USER_AGENT']); From 47af9c0cbd91010bbcf760016a8ff8f9fd7ac4d9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 11 Jul 2023 19:45:37 +0900 Subject: [PATCH 07/17] docs: add Config caching code as comments --- public/index.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/public/index.php b/public/index.php index 0cdc61b05ddd..1cc4710549d5 100644 --- a/public/index.php +++ b/public/index.php @@ -48,6 +48,11 @@ define('ENVIRONMENT', env('CI_ENVIRONMENT', 'production')); } +// Load Config Cache +// $factoriesCache = new \CodeIgniter\Cache\FactoriesCache(); +// $factoriesCache->load('config'); +// ^^^ Uncomment these lines if you want to use Config Caching. + /* * --------------------------------------------------------------- * GRAB OUR CODEIGNITER INSTANCE @@ -73,6 +78,10 @@ $app->run(); +// Save Config Cache +// $factoriesCache->save('config'); +// ^^^ Uncomment this line if you want to use Config Caching. + // Exits the application, setting the exit code for CLI-based applications // that might be watching. exit(EXIT_SUCCESS); From f49a5575339ff59d41c5a9c34198923ada80935f Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 12 Jul 2023 13:04:16 +0900 Subject: [PATCH 08/17] chore: update psalm-baseline.xml --- psalm-baseline.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index dcef743f607f..3ede420d002c 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,10 @@ + + + $val + + Memcache From eb59d2bf7e814be9a5179c944b7d1fc846fac539 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 12 Jul 2023 13:00:14 +0900 Subject: [PATCH 09/17] refactor: by rector --- system/Config/Factories.php | 6 ++---- tests/system/Config/FactoriesTest.php | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 943c9055c982..ae3003c10893 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -467,12 +467,10 @@ public static function getComponentInstances(string $component): array ]; } - $data = [ + return [ 'aliases' => static::$aliases[$component], 'instances' => self::$instances[$component], ]; - - return $data; } /** @@ -494,6 +492,6 @@ public static function setComponentInstances(string $component, array $data) */ public static function isUpdated(string $component): bool { - return isset(self::$updated[$component]) ? true : false; + return isset(self::$updated[$component]); } } diff --git a/tests/system/Config/FactoriesTest.php b/tests/system/Config/FactoriesTest.php index d48a6e50141d..c5936a7c341f 100644 --- a/tests/system/Config/FactoriesTest.php +++ b/tests/system/Config/FactoriesTest.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Config; use CodeIgniter\Test\CIUnitTestCase; +use Config\Database; use InvalidArgumentException; use ReflectionClass; use stdClass; @@ -402,7 +403,7 @@ public function testDefineAndLoad() public function testGetComponentInstances() { Factories::config('App'); - Factories::config(\Config\Database::class); + Factories::config(Database::class); $data = Factories::getComponentInstances('config'); From 1e819804ce7d489e4d63cb3bd491d37f196af4eb Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 14 Jul 2023 08:33:19 +0900 Subject: [PATCH 10/17] refactor: remove $val --- system/Cache/FactoriesCache/FileVarExportHandler.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/system/Cache/FactoriesCache/FileVarExportHandler.php b/system/Cache/FactoriesCache/FileVarExportHandler.php index 99a71d8bd23f..f7cee5ef6248 100644 --- a/system/Cache/FactoriesCache/FileVarExportHandler.php +++ b/system/Cache/FactoriesCache/FileVarExportHandler.php @@ -24,7 +24,7 @@ public function save(string $key, $val): void // Write to temp file first to ensure atomicity $tmp = $this->path . "/{$key}." . uniqid('', true) . '.tmp'; - file_put_contents($tmp, 'path . "/{$key}"); } @@ -39,8 +39,6 @@ public function delete(string $key): void */ public function get(string $key) { - @include $this->path . "/{$key}"; - - return $val ?? false; // @phpstan-ignore-line + return @include $this->path . "/{$key}"; } } From 0404ae9886ddd1d5c313c52b41bd198300c31355 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 14 Jul 2023 16:20:11 +0900 Subject: [PATCH 11/17] docs: fix @param Co-authored-by: Andrey Pyzhikov <5071@mail.ru> --- system/Cache/FactoriesCache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Cache/FactoriesCache.php b/system/Cache/FactoriesCache.php index 7644ee5afbde..93114eca10b9 100644 --- a/system/Cache/FactoriesCache.php +++ b/system/Cache/FactoriesCache.php @@ -22,7 +22,7 @@ final class FactoriesCache private $cache; /** - * @param CacheInterface|FileVarExportHandler $cache + * @param CacheInterface|FileVarExportHandler|null $cache */ public function __construct($cache = null) { From b8a42865748950682d60a4a7e76940b9bf5e9e0b Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 18 Jul 2023 18:12:49 +0900 Subject: [PATCH 12/17] refactor: remove __set_state() in Config\Paths No longer use config(Config\Paths::class). --- app/Config/Paths.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/app/Config/Paths.php b/app/Config/Paths.php index 35a941940b5e..262c745ffa95 100644 --- a/app/Config/Paths.php +++ b/app/Config/Paths.php @@ -77,17 +77,4 @@ class Paths * is used when no value is provided to `Services::renderer()`. */ public string $viewDirectory = __DIR__ . '/../Views'; - - public static function __set_state(array $array) - { - $obj = new self(); - - $properties = array_keys(get_object_vars($obj)); - - foreach ($properties as $property) { - $obj->{$property} = $array[$property]; - } - - return $obj; - } } From 8c2fdc65d4eded5f62713b0096ba805ee98961f6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 22 Jul 2023 17:12:37 +0900 Subject: [PATCH 13/17] refactor: add return types --- system/Cache/FactoriesCache.php | 2 +- system/Config/Factories.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Cache/FactoriesCache.php b/system/Cache/FactoriesCache.php index 93114eca10b9..d78d0b1b02a4 100644 --- a/system/Cache/FactoriesCache.php +++ b/system/Cache/FactoriesCache.php @@ -40,7 +40,7 @@ public function save(string $component): void $this->cache->save($this->getCacheKey($component), $data, 3600 * 24); } - private function getCacheKey(string $component) + private function getCacheKey(string $component): string { return 'FactoriesCache_' . $component; } diff --git a/system/Config/Factories.php b/system/Config/Factories.php index ae3003c10893..0eb6d2443e44 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -478,7 +478,7 @@ public static function getComponentInstances(string $component): array * * @internal For caching only */ - public static function setComponentInstances(string $component, array $data) + public static function setComponentInstances(string $component, array $data): void { static::$aliases[$component] = $data['aliases']; self::$instances[$component] = $data['instances']; From c78f8dd543d6e1c003c650679ffaafe1263216a9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 29 Jul 2023 15:44:15 +0900 Subject: [PATCH 14/17] test: fix out-of-dated test code --- .../Cache/FactoriesCacheFileVarExportHandlerTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php b/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php index 3fcf751b6176..a3aff35b000e 100644 --- a/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php +++ b/tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php @@ -15,6 +15,7 @@ use CodeIgniter\Config\Factories; use CodeIgniter\Test\CIUnitTestCase; use Config\App; +use Config\Modules; /** * @internal @@ -55,10 +56,10 @@ public function testSave() $cachedData = $this->handler->get('FactoriesCache_config'); - $this->assertArrayHasKey('basenames', $cachedData); + $this->assertArrayHasKey('aliases', $cachedData); $this->assertArrayHasKey('instances', $cachedData); - $this->assertArrayHasKey('Modules', $cachedData['basenames']); - $this->assertArrayHasKey('App', $cachedData['basenames']); + $this->assertArrayHasKey(Modules::class, $cachedData['aliases']); + $this->assertArrayHasKey('App', $cachedData['aliases']); } public function testLoad() From 8de664f831db9989b5263c7a3d1d6feee9fae39a Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 15 Aug 2023 12:00:50 +0900 Subject: [PATCH 15/17] docs: add docs --- user_guide_src/source/changelogs/v4.4.0.rst | 6 +- user_guide_src/source/concepts/factories.rst | 78 +++++++++++++++++++ .../source/installation/upgrade_440.rst | 10 ++- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.4.0.rst b/user_guide_src/source/changelogs/v4.4.0.rst index 4b7b065cd6e2..c510f884606d 100644 --- a/user_guide_src/source/changelogs/v4.4.0.rst +++ b/user_guide_src/source/changelogs/v4.4.0.rst @@ -222,8 +222,10 @@ Others - It can also take an object that implements ``ResponseInterface`` as its first argument. - It implements ``ResponsableInterface``. - **DebugBar:** Now :ref:`view-routes` are displayed in *DEFINED ROUTES* on the *Routes* tab. -- **Factories:** You can now define the classname that will actually be loaded. - See :ref:`factories-defining-classname-to-be-loaded`. +- **Factories:** + - You can now define the classname that will actually be loaded. + See :ref:`factories-defining-classname-to-be-loaded`. + - The Config Caching implemented. See :ref:`factories-config-caching` for details. Message Changes *************** diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index 881f0fa1b1f1..aa9c49c16220 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -262,3 +262,81 @@ that single call will return a new or shared instance: .. literalinclude:: factories/007.php :lines: 2- + +.. _factories-config-caching: + +Config Caching +************** + +.. versionadded:: 4.4.0 + +To improve performance, the Config Caching has been implemented. + +Prerequisite +============ + +.. important:: Using this feature when the prerequisites are not met will prevent + CodeIgniter from operating properly. Do not use this feature in such cases. + +- To use this feature, the properties of all Config objects instantiated in + Factories must not be modified after instantiation. Put another way, the Config + classes must be an immutable or readonly classes. +- By default, every Config class that is cached must implement ``__set_state()`` + method. + +How It Works +============ + +- Save the all Config instances in Factories into a cache file before shutdown, + if the state of the Config instances in Factories changes. +- Restore cached Config instances before CodeIgniter initialization if a cache + is available. + +Simply put, all Config instances held by Factories are cached immediately prior +to shutdown, and the cached instances are used permanently. + +How to Update Config Values +=========================== + +Once cached, the cache is never expired. Changing a existing Config file +(or changing Environment Variables for it) will not update the cache nor the Config +values. + +So if you want to update Config values, update Config files or Environment Variables +for them, and you must manually delete the cache file. + +You can use the ``spark cache:clear`` command: + +.. code-block:: console + + php spark cache:clear + +Or simply delete the **writable/cache/FactoriesCache_config** file. + +How to Enable Config Caching +============================ + +Uncomment the following code in **public/index.php**:: + + --- a/public/index.php + +++ b/public/index.php + @@ -49,8 +49,8 @@ if (! defined('ENVIRONMENT')) { + } + + // Load Config Cache + -// $factoriesCache = new \CodeIgniter\Cache\FactoriesCache(); + -// $factoriesCache->load('config'); + +$factoriesCache = new \CodeIgniter\Cache\FactoriesCache(); + +$factoriesCache->load('config'); + // ^^^ Uncomment these lines if you want to use Config Caching. + + /* + @@ -79,7 +79,7 @@ $app->setContext($context); + $app->run(); + + // Save Config Cache + -// $factoriesCache->save('config'); + +$factoriesCache->save('config'); + // ^^^ Uncomment this line if you want to use Config Caching. + + // Exits the application, setting the exit code for CLI-based applications diff --git a/user_guide_src/source/installation/upgrade_440.rst b/user_guide_src/source/installation/upgrade_440.rst index ffb8751c5206..1d7eeca2d9f8 100644 --- a/user_guide_src/source/installation/upgrade_440.rst +++ b/user_guide_src/source/installation/upgrade_440.rst @@ -117,15 +117,16 @@ match the new array structure. Mandatory File Changes ********************** -index.php -========= +index.php and spark +=================== -The following file received significant changes and +The following files received significant changes and **you must merge the updated versions** with your application: - ``public/index.php`` (see also :ref:`v440-codeigniter-and-exit`) +- ``spark`` -.. important:: If you don't update the above file, CodeIgniter will not work +.. important:: If you don't update the above files, CodeIgniter will not work properly after running ``composer update``. The upgrade procedure, for example, is as follows: @@ -134,6 +135,7 @@ The following file received significant changes and composer update cp vendor/codeigniter4/framework/public/index.php public/index.php + cp vendor/codeigniter4/framework/spark spark Config Files ============ From 82328c4d07080797903c0698d413f4650080765d Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 16 Aug 2023 14:23:36 +0900 Subject: [PATCH 16/17] docs: fix by proofreading Co-authored-by: MGatner --- user_guide_src/source/changelogs/v4.4.0.rst | 2 +- user_guide_src/source/concepts/factories.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.4.0.rst b/user_guide_src/source/changelogs/v4.4.0.rst index c510f884606d..2be5278fe786 100644 --- a/user_guide_src/source/changelogs/v4.4.0.rst +++ b/user_guide_src/source/changelogs/v4.4.0.rst @@ -225,7 +225,7 @@ Others - **Factories:** - You can now define the classname that will actually be loaded. See :ref:`factories-defining-classname-to-be-loaded`. - - The Config Caching implemented. See :ref:`factories-config-caching` for details. + - Config Caching implemented. See :ref:`factories-config-caching` for details. Message Changes *************** diff --git a/user_guide_src/source/concepts/factories.rst b/user_guide_src/source/concepts/factories.rst index aa9c49c16220..f4872327f082 100644 --- a/user_guide_src/source/concepts/factories.rst +++ b/user_guide_src/source/concepts/factories.rst @@ -270,7 +270,7 @@ Config Caching .. versionadded:: 4.4.0 -To improve performance, the Config Caching has been implemented. +To improve performance, Config Caching has been implemented. Prerequisite ============ @@ -298,7 +298,7 @@ to shutdown, and the cached instances are used permanently. How to Update Config Values =========================== -Once cached, the cache is never expired. Changing a existing Config file +Once stored, the cached versions never expire. Changing a existing Config file (or changing Environment Variables for it) will not update the cache nor the Config values. From ef045ce010fa9a2916849df0fc215f5372224e8c Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 17 Aug 2023 10:27:54 +0900 Subject: [PATCH 17/17] refactor: early return --- system/Config/BaseConfig.php | 40 +++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index 127f70f13667..d2c396dc36ba 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -83,25 +83,27 @@ public function __construct() { static::$moduleConfig = config(Modules::class); - if (static::$override) { - $this->registerProperties(); - - $properties = array_keys(get_object_vars($this)); - $prefix = static::class; - $slashAt = strrpos($prefix, '\\'); - $shortPrefix = strtolower(substr($prefix, $slashAt === false ? 0 : $slashAt + 1)); - - foreach ($properties as $property) { - $this->initEnvValue($this->{$property}, $property, $prefix, $shortPrefix); - - if ($this instanceof Encryption && $property === 'key') { - if (strpos($this->{$property}, 'hex2bin:') === 0) { - // Handle hex2bin prefix - $this->{$property} = hex2bin(substr($this->{$property}, 8)); - } elseif (strpos($this->{$property}, 'base64:') === 0) { - // Handle base64 prefix - $this->{$property} = base64_decode(substr($this->{$property}, 7), true); - } + if (! static::$override) { + return; + } + + $this->registerProperties(); + + $properties = array_keys(get_object_vars($this)); + $prefix = static::class; + $slashAt = strrpos($prefix, '\\'); + $shortPrefix = strtolower(substr($prefix, $slashAt === false ? 0 : $slashAt + 1)); + + foreach ($properties as $property) { + $this->initEnvValue($this->{$property}, $property, $prefix, $shortPrefix); + + if ($this instanceof Encryption && $property === 'key') { + if (strpos($this->{$property}, 'hex2bin:') === 0) { + // Handle hex2bin prefix + $this->{$property} = hex2bin(substr($this->{$property}, 8)); + } elseif (strpos($this->{$property}, 'base64:') === 0) { + // Handle base64 prefix + $this->{$property} = base64_decode(substr($this->{$property}, 7), true); } } }