Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: FileLocator caching #8017

Merged
merged 18 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <admin@codeigniter.com>
*
* 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 <admin@codeigniter.com>
*
* 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
Loading