Skip to content

Commit

Permalink
feat: add FileFinder for recursive and file-based finders (fixes #20)
Browse files Browse the repository at this point in the history
  • Loading branch information
alekitto committed Jun 13, 2024
1 parent 6a125b3 commit a285a09
Show file tree
Hide file tree
Showing 19 changed files with 884 additions and 246 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"roave/better-reflection": "^6.0",
"roave/security-advisories": "dev-master",
"solido/php-coding-standards": "dev-master",
"symfony/cache": "^5.0 || ^6.0 || ^7.0",
"symfony/error-handler": "^5.0 || ^6.0 || ^7.0"
},
"autoload": {
Expand Down Expand Up @@ -58,7 +59,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
"dev-master": "0.5.x-dev"
}
},
"archive": {
Expand Down
719 changes: 578 additions & 141 deletions composer.lock

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions lib/FileFinder/CachedFileFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Kcs\ClassFinder\FileFinder;

use Psr\Cache\CacheItemPoolInterface;
use SplFileInfo;

use function hash;

final class CachedFileFinder implements FileFinderInterface
{
public function __construct(
private readonly FileFinderInterface $innerFinder,
private readonly CacheItemPoolInterface $cacheItemPool,
) {
}

/** @inheritDoc */
public function search(string $pattern): iterable
{
$cacheKey = hash('sha256', $pattern);
$item = $this->cacheItemPool->getItem($cacheKey);
if ($item->isHit()) {
$files = $item->get();
} else {
$files = [];
foreach ($this->innerFinder->search($pattern) as $path => $_) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps
$files[] = $path;
}

$item->set($files);
$this->cacheItemPool->save($item);
}

foreach ($files as $file) {
yield $file => new SplFileInfo($file);
}
}
}
45 changes: 45 additions & 0 deletions lib/FileFinder/DefaultFileFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Kcs\ClassFinder\FileFinder;

use FilesystemIterator;
use Kcs\ClassFinder\PathNormalizer;
use RecursiveCallbackFilterIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;

use function is_dir;
use function is_file;
use function Safe\glob;

final class DefaultFileFinder implements FileFinderInterface
{
/** @inheritDoc */
public function search(string $pattern): iterable
{
foreach (glob($pattern) as $path) {
if (is_dir($path)) {
$files = new RecursiveIteratorIterator(
new RecursiveCallbackFilterIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS),
static fn (SplFileInfo $file): bool => $file->getBasename()[0] !== '.',
),
RecursiveIteratorIterator::LEAVES_ONLY,
);

foreach ($files as $filepath => $info) {
if (! $info->isFile()) {
continue;
}

yield PathNormalizer::resolvePath($filepath) => $info;
}
} elseif (is_file($path)) {
yield PathNormalizer::resolvePath($path) => new SplFileInfo($path);
}
}
}
}
20 changes: 20 additions & 0 deletions lib/FileFinder/FileFinderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Kcs\ClassFinder\FileFinder;

use SplFileInfo;

/**
* The FileFinderInterface is responsible to find files matching a given pattern.
*/
interface FileFinderInterface
{
/**
* Searches for the given pattern and return the list of the matching files/folders.
*
* @return iterable<string, SplFileInfo>
*/
public function search(string $pattern): iterable;
}
5 changes: 5 additions & 0 deletions lib/Finder/ComposerFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
final class ComposerFinder implements FinderInterface
{
use ReflectionFilterTrait;
use RecursiveFinderTrait;

private ClassLoader $loader;
private ReflectorFactoryInterface|null $reflectorFactory = null;
Expand Down Expand Up @@ -117,6 +118,10 @@ public function getIterator(): Iterator
);
}

if (isset($this->fileFinder)) {
$iterator->setFileFinder($this->fileFinder);
}

return $this->applyFilters($iterator);
}

Expand Down
4 changes: 4 additions & 0 deletions lib/Finder/PhpDocumentorFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
final class PhpDocumentorFinder implements FinderInterface
{
use PhpDocumentorFilterTrait;
use RecursiveFinderTrait;

public function __construct(private string $dir)
{
Expand All @@ -31,6 +32,9 @@ public function getIterator(): Iterator
{
$pathCallback = $this->pathFilterCallback !== null ? ($this->pathFilterCallback)(...) : null;
$iterator = new PhpDocumentorIterator($this->dir, pathCallback: $pathCallback);
if (isset($this->fileFinder)) {
$iterator->setFileFinder($this->fileFinder);
}

if ($this->dirs !== null) {
$iterator->in($this->dirs);
Expand Down
11 changes: 9 additions & 2 deletions lib/Finder/Psr0Finder.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
final class Psr0Finder implements FinderInterface
{
use ReflectionFilterTrait;
use RecursiveFinderTrait;

private string $namespace;
private string $path;
Expand Down Expand Up @@ -56,11 +57,17 @@ public function getIterator(): Iterator
$pathFilterCallback = BogonFilesFilter::getFileFilterFn($pathFilterCallback);
}

return $this->applyFilters(new Psr0Iterator(
$iterator = new Psr0Iterator(
$this->namespace,
$this->path,
reflectorFactory: $this->reflectorFactory,
pathCallback: $pathFilterCallback,
));
);

if (isset($this->fileFinder)) {
$iterator->setFileFinder($this->fileFinder);
}

return $this->applyFilters($iterator);
}
}
11 changes: 9 additions & 2 deletions lib/Finder/Psr4Finder.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
final class Psr4Finder implements FinderInterface
{
use ReflectionFilterTrait;
use RecursiveFinderTrait;

private string $namespace;
private string $path;
Expand Down Expand Up @@ -56,11 +57,17 @@ public function getIterator(): Iterator
$pathFilterCallback = BogonFilesFilter::getFileFilterFn($pathFilterCallback);
}

return $this->applyFilters(new Psr4Iterator(
$iterator = new Psr4Iterator(
$this->namespace,
$this->path,
$this->reflectorFactory,
pathCallback: $pathFilterCallback,
));
);

if (isset($this->fileFinder)) {
$iterator->setFileFinder($this->fileFinder);
}

return $this->applyFilters($iterator);
}
}
11 changes: 9 additions & 2 deletions lib/Finder/RecursiveFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
final class RecursiveFinder implements FinderInterface
{
use ReflectionFilterTrait;
use RecursiveFinderTrait;

private string $path;

Expand All @@ -30,9 +31,15 @@ public function getIterator(): Traversable
$pathFilterCallback = BogonFilesFilter::getFileFilterFn($pathFilterCallback);
}

return $this->applyFilters(new RecursiveIterator(
$iterator = new RecursiveIterator(
$this->path,
pathCallback: $pathFilterCallback,
));
);

if (isset($this->fileFinder)) {
$iterator->setFileFinder($this->fileFinder);
}

return $this->applyFilters($iterator);
}
}
19 changes: 19 additions & 0 deletions lib/Finder/RecursiveFinderTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Kcs\ClassFinder\Finder;

use Kcs\ClassFinder\FileFinder\FileFinderInterface;

trait RecursiveFinderTrait
{
private FileFinderInterface $fileFinder;

public function withFileFinder(FileFinderInterface $fileFinder): static
{
$this->fileFinder = $fileFinder;

return $this;
}
}
10 changes: 9 additions & 1 deletion lib/Iterator/ComposerIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
final class ComposerIterator extends ClassIterator
{
use RecursiveIteratorTrait;

private ReflectorFactoryInterface $reflectorFactory;

public function __construct(
Expand Down Expand Up @@ -78,15 +80,21 @@ private function searchInPsrMap(): Generator
foreach ($dirs as $dir) {
$itr = new Psr4Iterator($ns, $dir, $this->reflectorFactory, $this->flags, $this->classLoader->getClassMap());
$itr->pathCallback = $this->pathCallback;
if (isset($this->fileFinder)) {
$itr->setFileFinder($this->fileFinder);
}

yield from $itr;
}
}

foreach ($this->classLoader->getPrefixes() as $ns => $dirs) {
foreach ((array) $dirs as $dir) {
foreach ($dirs as $dir) {
$itr = new Psr0Iterator($ns, $dir, $this->reflectorFactory, $this->flags, $this->classLoader->getClassMap());
$itr->pathCallback = $this->pathCallback;
if (isset($this->fileFinder)) {
$itr->setFileFinder($this->fileFinder);
}

yield from $itr;
}
Expand Down
25 changes: 19 additions & 6 deletions lib/Iterator/FilteredComposerIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
use function array_map;
use function array_unique;
use function explode;
use function strpos;
use function str_contains;
use function str_starts_with;

/**
* Iterates over classes defined in a composer-generated ClassLoader
Expand All @@ -30,6 +31,8 @@
*/
final class FilteredComposerIterator extends ClassIterator
{
use RecursiveIteratorTrait;

private ReflectorFactoryInterface $reflectorFactory;

/** @var string[]|null */
Expand Down Expand Up @@ -126,11 +129,21 @@ private function searchInPsrMap(): Generator
}

foreach ($this->traversePrefixes($this->classLoader->getPrefixesPsr4()) as $ns => $dir) {
yield from new Psr4Iterator($ns, $dir, $this->reflectorFactory, $this->flags, $this->classLoader->getClassMap(), $this->notNamespaces, $this->pathCallback);
$itr = new Psr4Iterator($ns, $dir, $this->reflectorFactory, $this->flags, $this->classLoader->getClassMap(), $this->notNamespaces, $this->pathCallback);
if (isset($this->fileFinder)) {
$itr->setFileFinder($this->fileFinder);
}

yield from $itr;
}

foreach ($this->traversePrefixes($this->classLoader->getPrefixes()) as $ns => $dir) {
yield from new Psr0Iterator($ns, $dir, $this->reflectorFactory, $this->flags, $this->classLoader->getClassMap(), $this->notNamespaces, $this->pathCallback);
$itr = new Psr0Iterator($ns, $dir, $this->reflectorFactory, $this->flags, $this->classLoader->getClassMap(), $this->notNamespaces, $this->pathCallback);
if (isset($this->fileFinder)) {
$itr->setFileFinder($this->fileFinder);
}

yield from $itr;
}
}

Expand Down Expand Up @@ -158,7 +171,7 @@ private function validNamespace(string $class): bool
{
if ($this->notNamespaces !== null) {
foreach ($this->notNamespaces as $namespace) {
if (strpos($class, $namespace) === 0) {
if (str_starts_with($class, $namespace)) {
return false;
}
}
Expand All @@ -169,7 +182,7 @@ private function validNamespace(string $class): bool
}

foreach ($this->namespaces as $namespace) {
if (strpos($class, $namespace) === 0) {
if (str_starts_with($class, $namespace)) {
return true;
}
}
Expand All @@ -185,7 +198,7 @@ private function validDir(string $path): bool

foreach ($this->dirs as $dir) {
// Check for intersection of required path and evaluated dir
if (strpos($path, $dir) !== false || strpos($dir, $path) !== false) {
if (str_contains($path, $dir) || str_contains($dir, $path)) {
return true;
}
}
Expand Down
Loading

0 comments on commit a285a09

Please sign in to comment.