Skip to content

Commit

Permalink
Merge pull request #8017 from kenjis/feat-FileLocator-Caching
Browse files Browse the repository at this point in the history
feat: FileLocator caching
  • Loading branch information
kenjis authored Oct 16, 2023
2 parents d5ad48e + dc87b0d commit a4bc7c0
Show file tree
Hide file tree
Showing 15 changed files with 455 additions and 24 deletions.
2 changes: 1 addition & 1 deletion system/Autoloader/FileLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
* @see \CodeIgniter\Autoloader\FileLocatorTest
*/
class FileLocator
class FileLocator implements FileLocatorInterface
{
/**
* The Autoloader to use.
Expand Down
172 changes: 172 additions & 0 deletions system/Autoloader/FileLocatorCached.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\Autoloader;

use CodeIgniter\Cache\CacheInterface;
use CodeIgniter\Cache\FactoriesCache\FileVarExportHandler;

/**
* FileLocator with Cache
*
* @see \CodeIgniter\Autoloader\FileLocatorCachedTest
*/
final class FileLocatorCached implements FileLocatorInterface
{
private FileLocator $locator;

/**
* @var CacheInterface|FileVarExportHandler
*/
private $cacheHandler;

/**
* Cache data
*
* [method => data]
* E.g.,
* [
* 'search' => [$path => $foundPaths],
* ]
*/
private array $cache = [];

/**
* Is the cache updated?
*/
private bool $cacheUpdated = false;

private string $cacheKey = 'FileLocatorCache';

/**
* @param CacheInterface|FileVarExportHandler|null $cache
*/
public function __construct(FileLocator $locator, $cache = null)
{
$this->cacheHandler = $cache ?? new FileVarExportHandler();
$this->locator = $locator;

$this->loadCache();
}

private function loadCache(): void
{
$data = $this->cacheHandler->get($this->cacheKey);

if (is_array($data)) {
$this->cache = $data;
}
}

public function __destruct()
{
$this->saveCache();
}

private function saveCache(): void
{
if ($this->cacheUpdated) {
$this->cacheHandler->save($this->cacheKey, $this->cache, 3600 * 24);
}
}

/**
* Delete cache data
*/
public function deleteCache(): void
{
$this->cacheHandler->delete($this->cacheKey);
}

public function findQualifiedNameFromPath(string $path): false|string
{
if (isset($this->cache['findQualifiedNameFromPath'][$path])) {
return $this->cache['findQualifiedNameFromPath'][$path];
}

$classname = $this->locator->findQualifiedNameFromPath($path);

$this->cache['findQualifiedNameFromPath'][$path] = $classname;
$this->cacheUpdated = true;

return $classname;
}

public function getClassname(string $file): string
{
if (isset($this->cache['getClassname'][$file])) {
return $this->cache['getClassname'][$file];
}

$classname = $this->locator->getClassname($file);

$this->cache['getClassname'][$file] = $classname;
$this->cacheUpdated = true;

return $classname;
}

public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array
{
if (isset($this->cache['search'][$path][$ext][$prioritizeApp])) {
return $this->cache['search'][$path][$ext][$prioritizeApp];
}

$foundPaths = $this->locator->search($path, $ext, $prioritizeApp);

$this->cache['search'][$path][$ext][$prioritizeApp] = $foundPaths;
$this->cacheUpdated = true;

return $foundPaths;
}

public function listFiles(string $path): array
{
if (isset($this->cache['listFiles'][$path])) {
return $this->cache['listFiles'][$path];
}

$files = $this->locator->listFiles($path);

$this->cache['listFiles'][$path] = $files;
$this->cacheUpdated = true;

return $files;
}

public function listNamespaceFiles(string $prefix, string $path): array
{
if (isset($this->cache['listNamespaceFiles'][$prefix][$path])) {
return $this->cache['listNamespaceFiles'][$prefix][$path];
}

$files = $this->locator->listNamespaceFiles($prefix, $path);

$this->cache['listNamespaceFiles'][$prefix][$path] = $files;
$this->cacheUpdated = true;

return $files;
}

public function locateFile(string $file, ?string $folder = null, string $ext = 'php'): false|string
{
if (isset($this->cache['locateFile'][$file][$folder][$ext])) {
return $this->cache['locateFile'][$file][$folder][$ext];
}

$files = $this->locator->locateFile($file, $folder, $ext);

$this->cache['locateFile'][$file][$folder][$ext] = $files;
$this->cacheUpdated = true;

return $files;
}
}
80 changes: 80 additions & 0 deletions system/Autoloader/FileLocatorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\Autoloader;

/**
* Allows loading non-class files in a namespaced manner.
* Works with Helpers, Views, etc.
*/
interface FileLocatorInterface
{
/**
* Attempts to locate a file by examining the name for a namespace
* and looking through the PSR-4 namespaced files that we know about.
*
* @param string $file The relative file path or namespaced file to
* locate. If not namespaced, search in the app
* folder.
* @param string|null $folder The folder within the namespace that we should
* look for the file. If $file does not contain
* this value, it will be appended to the namespace
* folder.
* @param string $ext The file extension the file should have.
*
* @return false|string The path to the file, or false if not found.
*/
public function locateFile(string $file, ?string $folder = null, string $ext = 'php');

/**
* Examines a file and returns the fully qualified class name.
*/
public function getClassname(string $file): string;

/**
* Searches through all of the defined namespaces looking for a file.
* Returns an array of all found locations for the defined file.
*
* Example:
*
* $locator->search('Config/Routes.php');
* // Assuming PSR4 namespaces include foo and bar, might return:
* [
* 'app/Modules/foo/Config/Routes.php',
* 'app/Modules/bar/Config/Routes.php',
* ]
*/
public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array;

/**
* Find the qualified name of a file according to
* the namespace of the first matched namespace path.
*
* @return false|string The qualified name or false if the path is not found
*/
public function findQualifiedNameFromPath(string $path);

/**
* Scans the defined namespaces, returning a list of all files
* that are contained within the subpath specified by $path.
*
* @return string[] List of file paths
*/
public function listFiles(string $path): array;

/**
* Scans the provided namespace, returning a list of all files
* that are contained within the sub path specified by $path.
*
* @return string[] List of file paths
*/
public function listNamespaceFiles(string $prefix, string $path): array;
}
4 changes: 2 additions & 2 deletions system/CLI/Commands.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace CodeIgniter\CLI;

use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Autoloader\FileLocatorInterface;
use CodeIgniter\Log\Logger;
use ReflectionClass;
use ReflectionException;
Expand Down Expand Up @@ -87,7 +87,7 @@ public function discoverCommands()
return;
}

/** @var FileLocator $locator */
/** @var FileLocatorInterface $locator */
$locator = service('locator');
$files = $locator->listFiles('Commands/');

Expand Down
4 changes: 2 additions & 2 deletions system/Commands/Utilities/Routes/ControllerFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace CodeIgniter\Commands\Utilities\Routes;

use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Autoloader\FileLocatorInterface;
use CodeIgniter\Config\Services;

/**
Expand All @@ -26,7 +26,7 @@ final class ControllerFinder
*/
private string $namespace;

private FileLocator $locator;
private FileLocatorInterface $locator;

/**
* @param string $namespace namespace to search
Expand Down
3 changes: 2 additions & 1 deletion system/Config/BaseService.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use CodeIgniter\Autoloader\Autoloader;
use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Autoloader\FileLocatorInterface;
use CodeIgniter\Cache\CacheInterface;
use CodeIgniter\Cache\ResponseCache;
use CodeIgniter\CLI\Commands;
Expand Down Expand Up @@ -226,7 +227,7 @@ public static function autoloader(bool $getShared = true)
* within namespaced folders, as well as convenience methods for
* loading 'helpers', and 'libraries'.
*
* @return FileLocator
* @return FileLocatorInterface
*/
public static function locator(bool $getShared = true)
{
Expand Down
4 changes: 2 additions & 2 deletions system/Publisher/Publisher.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace CodeIgniter\Publisher;

use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Autoloader\FileLocatorInterface;
use CodeIgniter\Files\FileCollection;
use CodeIgniter\HTTP\URI;
use CodeIgniter\Publisher\Exceptions\PublisherException;
Expand Down Expand Up @@ -105,7 +105,7 @@ final public static function discover(string $directory = 'Publishers'): array

self::$discovered[$directory] = [];

/** @var FileLocator $locator */
/** @var FileLocatorInterface $locator */
$locator = service('locator');

if ([] === $files = $locator->listFiles($directory)) {
Expand Down
6 changes: 3 additions & 3 deletions system/Router/RouteCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace CodeIgniter\Router;

use Closure;
use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Autoloader\FileLocatorInterface;
use CodeIgniter\Router\Exceptions\RouterException;
use Config\App;
use Config\Modules;
Expand Down Expand Up @@ -245,7 +245,7 @@ class RouteCollection implements RouteCollectionInterface
/**
* Handle to the file locator to use.
*
* @var FileLocator
* @var FileLocatorInterface
*/
protected $fileLocator;

Expand Down Expand Up @@ -283,7 +283,7 @@ class RouteCollection implements RouteCollectionInterface
/**
* Constructor
*/
public function __construct(FileLocator $locator, Modules $moduleConfig, Routing $routing)
public function __construct(FileLocatorInterface $locator, Modules $moduleConfig, Routing $routing)
{
$this->fileLocator = $locator;
$this->moduleConfig = $moduleConfig;
Expand Down
13 changes: 9 additions & 4 deletions system/View/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace CodeIgniter\View;

use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Autoloader\FileLocatorInterface;
use CodeIgniter\View\Exceptions\ViewException;
use Config\View as ViewConfig;
use ParseError;
Expand Down Expand Up @@ -79,10 +79,15 @@ class Parser extends View
/**
* Constructor
*
* @param FileLocator|null $loader
* @param FileLocatorInterface|null $loader
*/
public function __construct(ViewConfig $config, ?string $viewPath = null, $loader = null, ?bool $debug = null, ?LoggerInterface $logger = null)
{
public function __construct(
ViewConfig $config,
?string $viewPath = null,
$loader = null,
?bool $debug = null,
?LoggerInterface $logger = null
) {
// Ensure user plugins override core plugins.
$this->plugins = $config->plugins;

Expand Down
Loading

0 comments on commit a4bc7c0

Please sign in to comment.