diff --git a/src/AutoloadsBuilder.php b/src/AutoloadsBuilder.php deleted file mode 100644 index 5c72fdc..0000000 --- a/src/AutoloadsBuilder.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace olvlvl\ComposerAttributeCollector; - -use Composer\Composer; - -/** - * @internal - */ -class AutoloadsBuilder -{ - /** - * @return array{ - * 'psr-0': array>, - * 'psr-4': array>, - * 'classmap': array, - * 'files': array, - * 'exclude-from-classmap': array, - * } - */ - public function buildAutoloads(Composer $composer): array - { - $package = $composer->getPackage(); - $packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); - $generator = $composer->getAutoloadGenerator(); - $packageMap = $generator->buildPackageMap($composer->getInstallationManager(), $package, $packages); - - return $generator->parseAutoloads($packageMap, $package); - } -} diff --git a/src/ClassMapBuilder.php b/src/ClassMapBuilder.php deleted file mode 100644 index 0b8c90e..0000000 --- a/src/ClassMapBuilder.php +++ /dev/null @@ -1,130 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace olvlvl\ComposerAttributeCollector; - -use Composer\Pcre\Preg; -use Composer\Util\Filesystem; -use Composer\Util\Platform; - -use function count; -use function file_exists; -use function implode; -use function is_dir; -use function krsort; -use function preg_quote; -use function realpath; - -/** - * @internal - */ -class ClassMapBuilder -{ - public function __construct( - private MemoizeClassMapGenerator $classMapGenerator - ) { - } - - /** - * @param array{ - * 'psr-0': array>, - * 'psr-4': array>, - * 'classmap': array, - * 'exclude-from-classmap': array, - * } $autoloads - * - * @return array - * Where _key_ is a class and _value_ the absolute path of its file. - */ - public function buildClassMap(array $autoloads): array - { - $classMapGenerator = $this->classMapGenerator; - - $excluded = $autoloads['exclude-from-classmap']; - - foreach ($autoloads['classmap'] as $dir) { - // @phpstan-ignore-next-line - $classMapGenerator->scanPaths($dir, self::buildExclusionRegex($dir, $excluded)); - } - - $namespacesToScan = []; - - // Scan the PSR-0/4 directories for class files, and add them to the class map - foreach ([ 'psr-4', 'psr-0' ] as $psrType) { - foreach ($autoloads[$psrType] as $namespace => $paths) { - $namespacesToScan[$namespace][] = [ 'paths' => $paths, 'type' => $psrType ]; - } - } - - krsort($namespacesToScan); - - $filesystem = new Filesystem(); - // @phpstan-ignore-next-line - $basePath = $filesystem->normalizePath(realpath(realpath(Platform::getCwd()))); - - foreach ($namespacesToScan as $namespace => $groups) { - foreach ($groups as $group) { - foreach ($group['paths'] as $dir) { - $dir = $filesystem->normalizePath( - $filesystem->isAbsolutePath($dir) ? $dir : $basePath . '/' . $dir - ); - if ($dir === '' || !is_dir($dir)) { - continue; // @codeCoverageIgnore - } - - $classMapGenerator->scanPaths( - $dir, - // @phpstan-ignore-next-line - self::buildExclusionRegex($dir, $excluded), - $group['type'], - $namespace - ); - } - } - } - - return $classMapGenerator->getMap(); - } - - /** - * {@link \Composer\Autoload\AutoloadGenerator::buildExclusionRegex} - */ - // @phpstan-ignore-next-line - private static function buildExclusionRegex(string $dir, ?array $excluded): ?string - { - if (null === $excluded) { - return null; //@codeCoverageIgnore - } - - // filter excluded patterns here to only use those matching $dir - // exclude-from-classmap patterns are all realpath'd, so we can only filter them if $dir exists so that - //realpath($dir) will work if $dir does not exist, it should anyway not find anything there so no trouble - if (file_exists($dir)) { - // transform $dir in the same way that exclude-from-classmap patterns are transformed so we can match them - // against each other - // @phpstan-ignore-next-line - $dirMatch = preg_quote(strtr(realpath($dir), '\\', '/')); - foreach ($excluded as $index => $pattern) { - // extract the constant string prefix of the pattern here, - // until we reach a non-escaped regex special character - $pattern = Preg::replace( - '{^(([^.+*?\[^\]$(){}=!<>|:\\\\#-]+|\\\\[.+*?\[^\]$(){}=!<>|:#-])*).*}', - '$1', - $pattern - ); - // if the pattern is not a subset or superset of $dir, it is unrelated and we skip it - if (!str_starts_with($pattern, $dirMatch) && !str_starts_with($dirMatch, $pattern)) { - unset($excluded[$index]); - } - } - } - - return count($excluded) > 0 ? '{(' . implode('|', $excluded) . ')}' : null; - } -} diff --git a/src/MemoizeAttributeCollector.php b/src/MemoizeAttributeCollector.php index c4631a0..6eeb643 100644 --- a/src/MemoizeAttributeCollector.php +++ b/src/MemoizeAttributeCollector.php @@ -47,11 +47,11 @@ public function __construct( * * @throws ReflectionException */ - public function collectAttributes(array $classMap): Collector + public function collectAttributes(array $classMap): TransientCollection { $filterClasses = []; $classAttributeCollector = $this->classAttributeCollector; - $collector = new Collector(); + $collector = new TransientCollection(); foreach ($classMap as $class => $filepath) { $filterClasses[$class] = true; @@ -66,7 +66,12 @@ public function collectAttributes(array $classMap): Collector $mtime = filemtime($filepath); if ($timestamp < $mtime) { - $this->io->debug("Refresh attributes of class '$class' in '$filepath' ($timestamp < $mtime)"); + if ($timestamp) { + $diff = $mtime - $timestamp; + $this->io->debug("Refresh attributes of class '$class' in '$filepath' ($diff sec ago)"); + } else { + $this->io->debug("Collect attributes of class '$class' in '$filepath'"); + } [ $classAttributes, diff --git a/src/MemoizeClassMapFilter.php b/src/MemoizeClassMapFilter.php index 933c089..dc62b94 100644 --- a/src/MemoizeClassMapFilter.php +++ b/src/MemoizeClassMapFilter.php @@ -53,7 +53,13 @@ public function filter(array $classMap, Closure $filter): array assert(is_int($mtime)); if ($timestamp < $mtime) { - $this->io->debug("Refresh filtered for '$pathname' ($timestamp < $mtime)"); + if ($timestamp) { + $diff = $mtime - $timestamp; + $this->io->debug("Refresh filtered files in '$pathname' ($diff sec ago)"); + } else { + $this->io->debug("Filter files in '$pathname'"); + } + $keep = $filter($class, $pathname); $this->state[$pathname] = [ time(), $keep ]; } diff --git a/src/MemoizeClassMapGenerator.php b/src/MemoizeClassMapGenerator.php index f237caa..96e5f31 100644 --- a/src/MemoizeClassMapGenerator.php +++ b/src/MemoizeClassMapGenerator.php @@ -63,7 +63,7 @@ public function getMap(): array $maps = []; - foreach ($this->state as [ , $map ]) { + foreach ($this->state as [, $map]) { $maps[] = $map; } @@ -77,33 +77,25 @@ public function getMap(): array * The path to search in. * @param non-empty-string|null $excluded * Regex that matches file paths to be excluded from the classmap - * @param 'classmap'|'psr-0'|'psr-4' $autoloadType - * Optional autoload standard to use mapping rules with the namespace instead of purely doing a classmap - * @param string|null $namespace - * Optional namespace prefix to filter by, only for psr-0/psr-4 autoloading * * @throws RuntimeException When the path is neither an existing file nor directory */ - public function scanPaths( - string $path, - string $excluded = null, - string $autoloadType = 'classmap', - ?string $namespace = null - ): void { + public function scanPaths(string $path, string $excluded = null): void + { $this->paths[$path] = true; [ $timestamp ] = $this->state[$path] ?? [ 0 ]; - if ($this->should_update($timestamp, $path)) { + if ($this->shouldUpdate($timestamp, $path)) { $inner = new ClassMapGenerator(); $inner->avoidDuplicateScans(); - $inner->scanPaths($path, $excluded, $autoloadType, $namespace); + $inner->scanPaths($path, $excluded); $map = $inner->getClassMap()->getMap(); $this->state[$path] = [ time(), $map ]; } } - private function should_update(int $timestamp, string $path): bool + private function shouldUpdate(int $timestamp, string $path): bool { if (!$timestamp) { return true; @@ -127,7 +119,7 @@ private function should_update(int $timestamp, string $path): bool foreach (new DirectoryIterator($path) as $di) { if ($di->isDir() && !$di->isDot()) { - if ($this->should_update($timestamp, $di->getPathname())) { + if ($this->shouldUpdate($timestamp, $di->getPathname())) { return true; } } diff --git a/src/Plugin.php b/src/Plugin.php index 2aa64cc..493ee9b 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -149,8 +149,8 @@ private static function buildFileFilter(): Filter ]); } - private static function render(Collector $collector): string + private static function render(TransientCollection $collector): string { - return CollectionRenderer::render($collector); + return TransientCollectionRenderer::render($collector); } } diff --git a/src/Collector.php b/src/TransientCollection.php similarity index 93% rename from src/Collector.php rename to src/TransientCollection.php index ce109a0..79d8ebc 100644 --- a/src/Collector.php +++ b/src/TransientCollection.php @@ -10,11 +10,11 @@ namespace olvlvl\ComposerAttributeCollector; /** - * Collects classes and methods with attributes. + * A collection of attributes used during the collection process. * * @internal */ -final class Collector +final class TransientCollection { /** * @var array> diff --git a/src/CollectionRenderer.php b/src/TransientCollectionRenderer.php similarity index 94% rename from src/CollectionRenderer.php rename to src/TransientCollectionRenderer.php index b37a2e4..8577f4e 100644 --- a/src/CollectionRenderer.php +++ b/src/TransientCollectionRenderer.php @@ -9,9 +9,9 @@ * * @internal */ -final class CollectionRenderer +final class TransientCollectionRenderer { - public static function render(Collector $collector): string + public static function render(TransientCollection $collector): string { $targetClassesCode = self::targetsToCode($collector->classes); $targetMethodsCode = self::targetsToCode($collector->methods); diff --git a/tests/ClassMapBuilderTest.php b/tests/ClassMapBuilderTest.php deleted file mode 100644 index f9b9a42..0000000 --- a/tests/ClassMapBuilderTest.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace tests\olvlvl\ComposerAttributeCollector; - -use Composer\IO\NullIO; -use olvlvl\ComposerAttributeCollector\ClassMapBuilder; -use olvlvl\ComposerAttributeCollector\Datastore\FileDatastore; -use olvlvl\ComposerAttributeCollector\MemoizeClassMapGenerator; -use PHPUnit\Framework\TestCase; - -use function getcwd; - -final class ClassMapBuilderTest extends TestCase -{ - public function testBuildClassMap(): void - { - $io = new NullIO(); - $sut = new ClassMapBuilder( - new MemoizeClassMapGenerator( - new FileDatastore(get_cache_dir(), $io), - $io, - ) - ); - - $classMap = $sut->buildClassMap([ - 'psr-0' => [ - ], - 'psr-4' => [ - "Acme\\PSR4\\" => [ - __DIR__ . '/Acme/PSR4' - ], - ], - 'classmap' => [ - __DIR__ . '/Acme/ClassMap' - ], - 'exclude-from-classmap' => [ - ] - ]); - - $cwd = getcwd(); - - $expected = [ - 'Acme\PSR4\ActiveRecord\Article' => "$cwd/tests/Acme/PSR4/ActiveRecord/Article.php", - 'Acme\PSR4\CreateMenu' => "$cwd/tests/Acme/PSR4/CreateMenu.php", - 'Acme\PSR4\CreateMenuHandler' => "$cwd/tests/Acme/PSR4/CreateMenuHandler.php", - 'Acme\PSR4\DeleteMenu' => "$cwd/tests/Acme/PSR4/DeleteMenu.php", - 'Acme\PSR4\DeleteMenuHandler' => "$cwd/tests/Acme/PSR4/DeleteMenuHandler.php", - 'Acme\PSR4\EventA' => "$cwd/tests/Acme/PSR4/EventA.php", - 'Acme\PSR4\Presentation\ArticleController' => "$cwd/tests/Acme/PSR4/Presentation/ArticleController.php", - 'Acme\PSR4\SubscriberA' => "$cwd/tests/Acme/PSR4/SubscriberA.php", - 'Acme\PSR4\SubscriberB' => "$cwd/tests/Acme/PSR4/SubscriberB.php", - 'Acme\Presentation\ImageController' => "$cwd/tests/Acme/ClassMap/controllers.php", - 'Acme\Presentation\FileController' => "$cwd/tests/Acme/ClassMap/controllers.php", - 'Acme\PSR4\IncompatibleSignature' => "$cwd/tests/Acme/PSR4/IncompatibleSignature.php", - 'Acme\PSR4\MissingInterface' => "$cwd/tests/Acme/PSR4/MissingInterface.php", - 'Acme\PSR4\MissingParent' => "$cwd/tests/Acme/PSR4/MissingParent.php", - ]; - - $this->assertEquals($expected, $classMap); - } -} diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index e72eb83..b67136f 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -29,14 +29,14 @@ public function testFrom(): void ] ]; - $package = $this->getMockBuilder(RootPackageInterface::class)->getMock(); + $package = $this->createMock(RootPackageInterface::class); $package ->method('getExtra') ->willReturn($extra); $cwd = getcwd(); assert(is_string($cwd)); - $config = $this->getMockBuilder(\Composer\Config::class)->getMock(); + $config = $this->createMock(\Composer\Config::class); $config ->method('get') ->with('vendor-dir') @@ -57,6 +57,7 @@ public function testFrom(): void "$cwd/tests/Acme/PSR4/IncompatibleSignature.php", "$cwd/vendor/vendor1/package1/file.php", ], + useCache: false, ); $actual = Config::from($composer); @@ -66,7 +67,7 @@ public function testFrom(): void public function testFromFailsOnMissingVendorDir(): void { - $config = $this->getMockBuilder(\Composer\Config::class)->getMock(); + $config = $this->createMock(\Composer\Config::class); $config ->method('get') ->with('vendor-dir') diff --git a/tests/MemoizeClassMapGeneratorTest.php b/tests/MemoizeClassMapGeneratorTest.php index 0f36f3e..29169f7 100644 --- a/tests/MemoizeClassMapGeneratorTest.php +++ b/tests/MemoizeClassMapGeneratorTest.php @@ -94,7 +94,9 @@ private static function write(string $name, string $data): void } /** - * @return array + * @param non-empty-string $path + * + * @return array */ private static function map(string $path): array { diff --git a/tests/PluginTest.php b/tests/PluginTest.php index 82d47a5..511e7a4 100644 --- a/tests/PluginTest.php +++ b/tests/PluginTest.php @@ -66,7 +66,8 @@ protected function setUp(): void ], exclude: [ "$cwd/tests/Acme/PSR4/IncompatibleSignature.php" - ] + ], + useCache: false, ); Plugin::dump(