Skip to content

Commit

Permalink
perf(documentator): Processor Lazy tasks & (weak) FileSystem cache (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
LastDragon-ru authored Aug 17, 2024
2 parents 5864fdc + 50f8cfe commit b2d206c
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 148 deletions.
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

0 comments on commit b2d206c

Please sign in to comment.