From e109c6c6e54d89260039098e6528aac75ad69e1e Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 7 Oct 2023 08:10:20 +0900 Subject: [PATCH] feat!: FileLocator caching --- system/Autoloader/FileLocator.php | 103 ++++++++++++++++++++++++++++-- system/Cache/FileLocatorCache.php | 72 +++++++++++++++++++++ 2 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 system/Cache/FileLocatorCache.php diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index bc18bd31b980..68262e37a2a3 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -11,6 +11,8 @@ namespace CodeIgniter\Autoloader; +use CodeIgniter\Cache\FileLocatorCache; + /** * Allows loading non-class files in a namespaced manner. * Works with Helpers, Views, etc. @@ -26,9 +28,40 @@ class FileLocator */ protected $autoloader; - public function __construct(Autoloader $autoloader) + /** + * Cache + * + * [method => data] + * E.g., + * [ + * 'search' => [$path => $foundPaths], + * ] + */ + protected array $cache = []; + + /** + * Is the cache updated? + */ + protected bool $cacheUpdated = false; + + private ?FileLocatorCache $locatorCache; + + public function __construct(Autoloader $autoloader, ?FileLocatorCache $cache = null) + { + $this->autoloader = $autoloader; + $this->locatorCache = $cache; + + if ($cache !== null) { + $this->locatorCache->setLocator($this); + $this->locatorCache->load(); + } + } + + public function __destruct() { - $this->autoloader = $autoloader; + if ($this->locatorCache !== null) { + $this->locatorCache->save(); + } } /** @@ -119,6 +152,10 @@ public function locateFile(string $file, ?string $folder = null, string $ext = ' */ public function getClassname(string $file): string { + if (isset($this->cache['getClassname'][$file])) { + return $this->cache['getClassname'][$file]; + } + $php = file_get_contents($file); $tokens = token_get_all($php); $dlm = false; @@ -154,7 +191,12 @@ public function getClassname(string $file): string return ''; } - return $namespace . '\\' . $className; + $fullClassname = $namespace . '\\' . $className; + + $this->cache['getClassname'][$file] = $fullClassname; + $this->cacheUpdated = true; + + return $fullClassname; } /** @@ -172,6 +214,10 @@ public function getClassname(string $file): string */ public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array { + if (isset($this->cache['search'][$path])) { + return $this->cache['search'][$path]; + } + $path = $this->ensureExt($path, $ext); $foundPaths = []; @@ -197,7 +243,42 @@ public function search(string $path, string $ext = 'php', bool $prioritizeApp = } // Remove any duplicates - return array_unique($foundPaths); + $foundPaths = array_unique($foundPaths); + + $this->cache['search'][$path] = $foundPaths; + $this->cacheUpdated = true; + + return $foundPaths; + } + + /** + * Gets cache + * + * @internal For caching only + */ + public function getCache(): array + { + return $this->cache; + } + + /** + * Sets cache + * + * @internal For caching only + */ + public function setCache(array $data): void + { + $this->cache = $data; + } + + /** + * Is the cache updated? + * + * @internal For caching only + */ + public function isCacheUpdated(): bool + { + return $this->cacheUpdated; } /** @@ -223,6 +304,10 @@ protected function ensureExt(string $path, string $ext): string */ protected function getNamespaces() { + if (isset($this->cache['getNamespaces'])) { + return $this->cache['getNamespaces']; + } + $namespaces = []; // Save system for last @@ -248,6 +333,9 @@ protected function getNamespaces() $namespaces[] = $system; + $this->cache['getNamespaces'] = $namespaces; + $this->cacheUpdated = true; + return $namespaces; } @@ -259,6 +347,10 @@ protected function getNamespaces() */ public function findQualifiedNameFromPath(string $path) { + if (isset($this->cache['findQualifiedNameFromPath'][$path])) { + return $this->cache['findQualifiedNameFromPath'][$path]; + } + $path = realpath($path) ?: $path; if (! is_file($path)) { @@ -285,6 +377,9 @@ public function findQualifiedNameFromPath(string $path) // Check if this exists if (class_exists($className)) { + $this->cache['findQualifiedNameFromPath'][$path] = $className; + $this->cacheUpdated = true; + return $className; } } diff --git a/system/Cache/FileLocatorCache.php b/system/Cache/FileLocatorCache.php new file mode 100644 index 000000000000..2bf3165591cb --- /dev/null +++ b/system/Cache/FileLocatorCache.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Cache; + +use CodeIgniter\Autoloader\FileLocator; +use CodeIgniter\Cache\FactoriesCache\FileVarExportHandler; + +final class FileLocatorCache +{ + /** + * @var CacheInterface|FileVarExportHandler + */ + private $cache; + + private FileLocator $locator; + + /** + * @param CacheInterface|FileVarExportHandler|null $cache + */ + public function __construct($cache = null) + { + $this->cache = $cache ?? new FileVarExportHandler(); + } + + public function setLocator(FileLocator $fileLocator): void + { + $this->locator = $fileLocator; + } + + public function save(): void + { + if (! $this->locator->isCacheUpdated()) { + return; + } + + $data = $this->locator->getCache(); + + $this->cache->save($this->getCacheKey(), $data, 3600 * 24); + } + + private function getCacheKey(): string + { + return 'FileLocatorCache'; + } + + public function load(): bool + { + $key = $this->getCacheKey(); + + if (! $data = $this->cache->get($key)) { + return false; + } + + $this->locator->setCache($data); + + return true; + } + + public function delete(): void + { + $this->cache->delete($this->getCacheKey()); + } +}