diff --git a/src/Searchable/SearchableListFactory.php b/src/Searchable/SearchableListFactory.php index c03bf3a9..b06ea86a 100644 --- a/src/Searchable/SearchableListFactory.php +++ b/src/Searchable/SearchableListFactory.php @@ -1,175 +1,99 @@ namespace = $namespace; - $this->appPath = $appPath; - } - - /** - * @return array - */ - public function getErrors(): array - { - return $this->errors; - } - - /** - * @return Collection - */ - public function make(): Collection - { - return new Collection($this->find()); - } +use Illuminate\Support\Facades\File; +use RuntimeException; +final class SearchableListFactory +{ /** - * Get a list of searchable models. - * - * @return string[] + * @var array|null */ - private function find(): array - { - $appNamespace = $this->namespace; - - return array_values(array_filter($this->getSearchableClasses(), static function (string $class) use ($appNamespace) { - return Str::startsWith($class, $appNamespace); - })); - } + private static ?array $searchableClasses = null; /** - * @return string[] + * @var array */ - private function getSearchableClasses(): array - { - if (self::$searchableClasses === null) { - self::$searchableClasses = $this->getProjectClasses()->filter(function ($class) { - return $this->findSearchableTraitRecursively($class); - })->toArray(); - } - - return self::$searchableClasses; - } + private array $errors = []; /** - * @return Collection + * @var Reflector|null */ - private function getProjectClasses(): Collection - { - /** @var Class_[] $nodes */ - $nodes = (new NodeFinder())->find($this->getStmts(), function (Node $node) { - return $node instanceof Class_; - }); - - return Collection::make($nodes)->map(function ($node) { - return $node->namespacedName->toCodeString(); - }); - } + private ?Reflector $reflector = null; /** - * @return array + * @return array */ - private function getStmts(): array - { - $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); - $nameResolverVisitor = new NameResolver(); - $nodeTraverser = new NodeTraverser(); - $nodeTraverser->addVisitor($nameResolverVisitor); - $stmts = []; - foreach (Finder::create()->files()->name('*.php')->in($this->appPath) as $file) { - try { - $stmts[] = $parser->parse($file->getContents()); - } catch (Error $e) { - $this->errors[] = $e->getMessage(); - } - } - - $stmts = Collection::make($stmts)->flatten(1)->toArray(); - - return $nodeTraverser->traverse($stmts); - } + public function getErrors(): array + { + return $this->errors; + } /** - * @param string $class - * @return bool + * @return Collection */ - private function findSearchableTraitRecursively(string $class): bool - { - try { - $reflection = $this->reflector()->reflectClass($class); - - if (in_array(Searchable::class, $traits = $reflection->getTraitNames())) { - return true; - } + public function make(): Collection + { + return new Collection($this->find()); + } - foreach ($traits as $trait) { - if ($this->findSearchableTraitRecursively($trait)) { - return true; - } - } - - return ($parent = $reflection->getParentClass()) && $this->findSearchableTraitRecursively($parent->getName()); - } catch (IdentifierNotFound $e) { - $this->errors[] = $e->getMessage(); - - return false; - } - } - - /** - * @return Reflector - */ - private function reflector(): Reflector + private function find(): array { - if (null === $this->reflector) { - $this->reflector = (new BetterReflection())->reflector(); + list($sources, $namespaces) = $this->inferProjectSourcePaths(); + + $classes = []; + foreach($sources as $index => $source) { + $appPath = $source; + $namespace = $namespaces[$index]; + $classes = array_merge($classes, $this->getSearchableClasses($namespace, $appPath)); } - return $this->reflector; - } + return array_values($classes); + } + + private function getSearchableClasses(string $namespace, string $appPath): array + { + if (self::$searchableClasses === null) { + self::$searchableClasses = $this->getProjectClasses($namespace, $appPath)->filter(function ($class) { + return $this->findSearchableTraitRecursively($class); + })->toArray(); + } + + return self::$searchableClasses; + } + + private function getProjectClasses(string $namespace, string $appPath): Collection + { + /** @var Class_[] $nodes */ + $nodes = (new NodeFinder())->find($this->getStmts($namespace, $appPath), function (Node $node) { + return $node instanceof Class_; + }); + + return Collection::make($nodes)->map(function ($node) { + return $node->namespacedName->toCodeString(); + }); + } + + private function inferProjectSourcePaths(): array + { + if (! ($composer = file_get_contents(base_path('composer.json')))) { + throw new RuntimeException('Error reading composer.json'); + } + $autoload = json_decode($composer, true)['autoload'] ?? []; + + if (! isset($autoload['psr-4'])) { + throw new RuntimeException('psr-4 autoload mappings are not present in composer.json'); + } + + $psr4 = collect($autoload['psr-4']); + + $sources = $psr4->values()->map(function ($path) { + return base_path($path); + })->toArray(); + $namespaces = $psr4->keys()->toArray(); + + return [$sources, $namespaces]; + } }