diff --git a/composer.json b/composer.json index ce66c61..667ea5c 100644 --- a/composer.json +++ b/composer.json @@ -16,8 +16,7 @@ ], "require": { "php": "8.0 - 8.3", - "ext-tokenizer": "*", - "nette/utils": "^4.0" + "ext-tokenizer": "*" }, "require-dev": { "nette/tester": "^2.4", diff --git a/src/RobotLoader/RobotLoader.php b/src/RobotLoader/RobotLoader.php index d214e07..e651dca 100644 --- a/src/RobotLoader/RobotLoader.php +++ b/src/RobotLoader/RobotLoader.php @@ -9,10 +9,6 @@ namespace Nette\Loaders; -use Nette; -use Nette\Utils\FileSystem; -use SplFileInfo; - /** * Nette auto loader is responsible for loading classes and interfaces. @@ -60,7 +56,7 @@ class RobotLoader public function __construct() { if (!extension_loaded('tokenizer')) { - throw new Nette\NotSupportedException('PHP extension Tokenizer is not loaded.'); + throw new \LogicException('PHP extension Tokenizer is not loaded.'); } } @@ -211,12 +207,11 @@ private function refreshClasses(): void foreach ($this->scanPaths as $path) { $iterator = is_file($path) - ? [new SplFileInfo($path)] + ? [$path] : $this->createFileIterator($path); - foreach ($iterator as $fileInfo) { - $mtime = $fileInfo->getMTime(); - $file = $fileInfo->getPathname(); + foreach ($iterator as $file) { + $mtime = filemtime($file); $foundClasses = isset($files[$file]) && $files[$file] === $mtime ? ($classes[$file] ?? []) : $this->scanPhp($file); @@ -230,7 +225,7 @@ private function refreshClasses(): void foreach ($foundClasses as $class) { if (isset($this->classes[$class])) { - throw new Nette\InvalidStateException(sprintf( + throw new \RuntimeException(sprintf( 'Ambiguous class %s resolution; defined in %s and in %s.', $class, $this->classes[$class][0], @@ -248,12 +243,12 @@ private function refreshClasses(): void /** * Creates an iterator scanning directory for PHP files and subdirectories. - * @throws Nette\IOException if path is not found + * @throws \RuntimeException if path is not found */ - private function createFileIterator(string $dir): Nette\Utils\Finder + private function createFileIterator(string $dir): \Generator { if (!is_dir($dir)) { - throw new Nette\IOException(sprintf("Directory '%s' not found.", $dir)); + throw new \RuntimeException(sprintf("Directory '%s' not found.", $dir)); } $dir = realpath($dir) ?: $dir; // realpath does not work in phar @@ -264,11 +259,40 @@ private function createFileIterator(string $dir): Nette\Utils\Finder } } - return Nette\Utils\Finder::findFiles($this->acceptFiles) - ->filter($filter = fn(SplFileInfo $file) => $file->getRealPath() === false || !isset($disallow[$file->getRealPath()])) - ->descentFilter($filter) - ->from($dir) - ->exclude($this->ignoreDirs); + yield from $this->traverseDir($dir, $disallow); + } + + + private function traverseDir(string $dir, array $disallow): \Generator + { + try { + $files = new \FilesystemIterator($dir, \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::UNIX_PATHS); + } catch (\RuntimeException) { + return; + } + + foreach ($files as $file) { + $realPath = realpath($file); + $file = $realPath ?: $file; + if ($realPath && isset($disallow[$realPath])) { + continue; + } elseif (is_dir($file) && !self::matches(basename($file), $this->ignoreDirs)) { + yield from $this->traverseDir($file, $disallow); + } elseif (is_file($file) && self::matches(basename($file), $this->acceptFiles)) { + yield $file; + } + } + } + + + private static function matches(string $file, array $masks): bool + { + foreach ($masks as $mask) { + if (fnmatch($mask, $file)) { + return true; + } + } + return false; } @@ -291,7 +315,7 @@ private function updateFile(string $file): void } if (isset($prevFile)) { - throw new Nette\InvalidStateException(sprintf( + throw new \RuntimeException(sprintf( 'Ambiguous class %s resolution; defined in %s and in %s.', $class, $prevFile, @@ -397,7 +421,9 @@ public function setAutoRefresh(bool $on = true): static */ public function setTempDirectory(string $dir): static { - FileSystem::createDir($dir); + if (!is_dir($dir) && !@mkdir($dir, recursive: true) && !is_dir($dir)) { // @ - dir may already exist + throw new \RuntimeException("Unable to create directory '$dir'"); + } $this->tempDirectory = $dir; return $this; } diff --git a/tests/Loaders/RobotLoader.phar.phpt b/tests/Loaders/RobotLoader.phar.phpt index bfba175..33af354 100644 --- a/tests/Loaders/RobotLoader.phar.phpt +++ b/tests/Loaders/RobotLoader.phar.phpt @@ -47,7 +47,7 @@ $loader->setTempDirectory(getTempDir()); $loader->addDirectory("phar://$pharFile/non-dir"); Assert::exception( fn() => $loader->rebuild(), - Nette\IOException::class, + RuntimeException::class, "Directory 'phar://$pharFile/non-dir' not found.", ); @@ -57,6 +57,6 @@ $loader->setTempDirectory(getTempDir()); $loader->addDirectory("phar://$pharFile/non-file.php"); Assert::exception( fn() => $loader->rebuild(), - Nette\IOException::class, + RuntimeException::class, "Directory 'phar://$pharFile/non-file.php' not found.", );