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

Processor Lazy tasks & (weak) FileSystem cache. #185

Merged
merged 3 commits into from
Aug 17, 2024
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
5 changes: 3 additions & 2 deletions packages/documentator/src/Commands/Preprocess.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace LastDragon_ru\LaraASP\Documentator\Commands;

use Illuminate\Console\Command;
use LastDragon_ru\LaraASP\Core\Application\ContainerResolver;
use LastDragon_ru\LaraASP\Core\Utils\Cast;
use LastDragon_ru\LaraASP\Core\Utils\Path;
use LastDragon_ru\LaraASP\Documentator\Package;
Expand Down Expand Up @@ -75,7 +76,7 @@ public function __construct(
parent::__construct();
}

public function __invoke(Formatter $formatter): void {
public function __invoke(ContainerResolver $container, Formatter $formatter): void {
$cwd = getcwd();
$path = Cast::toString($this->argument('path') ?? $cwd);
$path = Path::normalize($path);
Expand All @@ -99,7 +100,7 @@ public function __invoke(Formatter $formatter): void {
$this->output->writeln($line, OutputInterface::OUTPUT_NORMAL | $resultVerbosity);
};

$duration = (new Processor())
$duration = (new Processor($container))
->task($this->preprocess)
->run($path, $exclude, $listener);

Expand Down
2 changes: 1 addition & 1 deletion packages/documentator/src/Processor/Contracts/Task.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface Task {
*
* @return non-empty-list<string>
*/
public function getExtensions(): array;
public static function getExtensions(): array;

/**
* Performs action on the `$file`.
Expand Down
15 changes: 5 additions & 10 deletions packages/documentator/src/Processor/Executor.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use Throwable;
use Traversable;

use function array_merge;
use function array_values;
use function microtime;
use function preg_match;
Expand All @@ -48,9 +47,9 @@ public function __construct(
*/
private readonly array $exclude,
/**
* @var array<string, list<Task>>
* @var InstanceList<Task>
*/
private readonly array $tasks,
private readonly InstanceList $tasks,
/**
* @var Iterator<array-key, File>
*/
Expand Down Expand Up @@ -100,7 +99,7 @@ private function runFile(File $file): float {
}

// Process
$tasks = array_merge($this->tasks[$file->getExtension()] ?? [], $this->tasks['*'] ?? []);
$tasks = $this->tasks->get($file->getExtension(), '*');
$start = microtime(true);
$paused = 0;
$this->stack[$path] = $file;
Expand Down Expand Up @@ -219,12 +218,8 @@ private function dispatch(Dependency|File $file, Result $result, float $duration

private function isSkipped(File $file): bool {
// Tasks?
if (!isset($this->tasks['*'])) {
$tasks = $this->tasks[$file->getExtension()] ?? [];

if (!$tasks) {
return true;
}
if (!$this->tasks->has($file->getExtension(), '*')) {
return true;
}

// Outside?
Expand Down
30 changes: 23 additions & 7 deletions packages/documentator/src/Processor/FileSystem/FileSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
use Iterator;
use SplFileInfo;
use Symfony\Component\Finder\Finder;
use WeakReference;

use function dirname;
use function file_put_contents;
use function is_dir;
use function is_file;

class FileSystem {
/**
* @var array<string, WeakReference<Directory|File>>
*/
private array $cache = [];

public function __construct() {
// empty
}
Expand All @@ -35,8 +41,13 @@ public function getFile(Directory $root, SplFileInfo|File|string $path): ?File {
}

// Create
$writable = $root->isWritable() && $root->isInside($path);
$file = new File($path, $writable);
$file = ($this->cache[$path] ?? null)?->get();

if (!($file instanceof File)) {
$writable = $root->isWritable() && $root->isInside($path);
$file = new File($path, $writable);
$this->cache[$path] = WeakReference::create($file);
}

return $file;
}
Expand Down Expand Up @@ -66,12 +77,17 @@ public function getDirectory(Directory $root, SplFileInfo|Directory|File|string
}

// Create
$writable = $root->isWritable() && $root->isInside($path);
$dir = $root->getPath() !== $path
? new Directory($path, $writable)
: $root;
$directory = ($this->cache[$path] ?? null)?->get();

if (!($directory instanceof Directory)) {
$writable = $root->isWritable() && $root->isInside($path);
$directory = $root->getPath() !== $path
? new Directory($path, $writable)
: $root;
$this->cache[$path] = WeakReference::create($directory);
}

return $dir;
return $directory;
}

/**
Expand Down
50 changes: 38 additions & 12 deletions packages/documentator/src/Processor/FileSystem/FileSystemTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public function testGetFile(): void {
$relative = $fs->getFile($directory, basename(__FILE__));
$notfound = $fs->getFile($directory, 'not found');
$writable = new Directory(Path::normalize(__DIR__), true);
$internal = $fs->getFile($writable, basename(__FILE__));
$internal = $fs->getFile($writable, self::getTestData()->path('c.html'));
$external = $fs->getFile($writable, '../Processor.php');
$file = new File(Path::normalize(__FILE__), false);
$file = new File(Path::normalize(self::getTestData()->path('c.txt')), false);
$fromFile = $fs->getFile($writable, $file);
$splFile = new SplFileInfo($file->getPath());
$fromSplFile = $fs->getFile($writable, $splFile);
Expand All @@ -43,7 +43,10 @@ public function testGetFile(): void {

self::assertNotNull($internal);
self::assertTrue($internal->isWritable());
self::assertEquals(Path::normalize(__FILE__), $internal->getPath());
self::assertEquals(
Path::normalize(self::getTestData()->path('c.html')),
$internal->getPath(),
);

self::assertNotNull($external);
self::assertFalse($external->isWritable());
Expand All @@ -53,13 +56,19 @@ public function testGetFile(): void {
self::assertFalse($file->isWritable());
self::assertTrue($fromFile->isWritable());
self::assertEquals($file->getPath(), $fromFile->getPath());
self::assertEquals(Path::normalize(__FILE__), $fromFile->getPath());
self::assertEquals(
Path::normalize(self::getTestData()->path('c.txt')),
$fromFile->getPath(),
);

self::assertNotNull($fromSplFile);
self::assertFalse($file->isWritable());
self::assertTrue($fromSplFile->isWritable());
self::assertEquals($file->getPath(), $fromSplFile->getPath());
self::assertEquals(Path::normalize(__FILE__), $fromSplFile->getPath());
self::assertEquals(
Path::normalize(self::getTestData()->path('c.txt')),
$fromSplFile->getPath(),
);
}

public function testGetDirectory(): void {
Expand Down Expand Up @@ -93,11 +102,12 @@ public function testGetDirectory(): void {
self::assertNull($notDirectory);

// Internal
$internal = $fs->getDirectory($writable, basename(__DIR__));
$internalPath = self::getTestData()->path('a');
$internal = $fs->getDirectory($writable, $internalPath);

self::assertNotNull($internal);
self::assertTrue($internal->isWritable());
self::assertEquals(Path::normalize(__DIR__), $internal->getPath());
self::assertEquals($internalPath, $internal->getPath());

// External
$external = $fs->getDirectory($writable, '../Testing');
Expand All @@ -107,22 +117,26 @@ public function testGetDirectory(): void {
self::assertEquals(Path::getPath(__DIR__, '../../Testing'), $external->getPath());

// From file
$fromFile = $fs->getDirectory($writable, new File(Path::normalize(__FILE__), true));
$fromFilePath = Path::normalize(self::getTestData()->path('c.html'));
$fromFile = $fs->getDirectory($writable, new File($fromFilePath, true));

self::assertNotNull($fromFile);
self::assertTrue($fromFile->isWritable());
self::assertEquals(Path::normalize(__DIR__), $fromFile->getPath());
self::assertEquals(
Path::normalize(self::getTestData()->path('')),
$fromFile->getPath(),
);

// From SplFileInfo
$spl = new SplFileInfo(__DIR__);
$spl = new SplFileInfo(self::getTestData()->path('b'));
$fromSpl = $fs->getDirectory($writable, $spl);

self::assertNotNull($fromSpl);
self::assertTrue($fromSpl->isWritable());
self::assertEquals(Path::normalize($spl->getPathname()), $fromSpl->getPath());

// From Directory
$directory = new Directory(Path::normalize(__DIR__), false);
$directory = new Directory(Path::normalize(self::getTestData()->path('a/a')), false);
$fromDirectory = $fs->getDirectory($writable, $directory);

self::assertNotNull($fromDirectory);
Expand Down Expand Up @@ -238,7 +252,6 @@ public function testGetDirectoriesIterator(): void {
);
}


public function testSave(): void {
$fs = new FileSystem();
$temp = Path::normalize(self::getTempFile(__FILE__)->getPathname());
Expand Down Expand Up @@ -266,4 +279,17 @@ public function testSaveReadonly(): void {

self::assertEquals(__FILE__, file_get_contents($temp));
}

public function testCache(): void {
$fs = new FileSystem();
$dir = new Directory(Path::normalize(__DIR__), false);
$file = $fs->getFile($dir, __FILE__);
$directory = $fs->getDirectory($dir, __DIR__);

self::assertNotNull($file);
self::assertSame($file, $fs->getFile($dir, __FILE__));

self::assertNotNull($directory);
self::assertSame($directory, $fs->getDirectory($dir, __DIR__));
}
}
120 changes: 120 additions & 0 deletions packages/documentator/src/Processor/InstanceList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\Documentator\Processor;

use Closure;
use LastDragon_ru\LaraASP\Core\Application\ContainerResolver;

use function array_keys;
use function array_merge;
use function array_unique;
use function array_values;
use function is_object;
use function is_string;

/**
* @template TInstance of object
*
* @internal
*/
class InstanceList {
/**
* @var array<class-string<TInstance>, TInstance>
*/
private array $instances = [];

/**
* @var array<string, array<class-string<TInstance>, class-string<TInstance>>>
*/
private array $map = [];

public function __construct(
protected readonly ContainerResolver $container,
/**
* @var Closure(TInstance|class-string<TInstance>):(array<array-key, string>|string|null)
*/
protected readonly Closure $keyResolver,
) {
// empty
}

public function isEmpty(): bool {
return !$this->map;
}

/**
* @return list<string>
*/
public function keys(): array {
return array_keys($this->map);
}

/**
* @return list<class-string<TInstance>>
*/
public function classes(): array {
$classes = [];

foreach ($this->map as $list) {
$classes = array_merge($classes, $list);
}

return array_values(array_unique($classes));
}

public function has(string ...$key): bool {
$exists = false;

foreach ($key as $k) {
if (isset($this->map[$k])) {
$exists = true;
break;
}
}

return $exists;
}

/**
* @return list<TInstance>
*/
public function get(string ...$key): array {
$instances = [];

foreach ($key as $k) {
foreach ($this->map[$k] ?? [] as $class) {
$instances[$class] ??= $this->resolve($class);
}
}

return array_values($instances);
}

/**
* @param TInstance|class-string<TInstance> $instance
*/
public function add(object|string $instance): static {
$keys = (array) ($this->keyResolver)($instance);

foreach ($keys as $key) {
$class = is_string($instance) ? $instance : $instance::class;
$resolved = is_object($instance) ? $instance : null;
$this->map[$key][$class] = $class;

if ($resolved) {
$this->instances[$class] = $resolved;
}
}

return $this;
}

/**
* @param class-string<TInstance> $class
*
* @return TInstance
*/
protected function resolve(string $class): object {
return $this->instances[$class] ??= $this->container->getInstance()->make($class);
}
}
Loading
Loading