<?php
declare(strict_types=1);
namespace Csoft\AutoInvoker\ClassFinder;
use Csoft\AutoInvoker\AutoInvokeRule\AutoInvokeRuleInterface;
use Generator;
use ReflectionClass;
use ReflectionException;
use Symfony\Component\Finder\Finder;
class ClassFinder implements ClassFinderInterface
{
/** @var array */
protected $classes;
/**
* @inheritDoc
*/
public function getMatchingClasses(AutoInvokeRuleInterface $rule): array
{
// Preloads the available classes for the rule.
$this->fetchClasses($rule);
$matchingClasses = [];
foreach ($rule->getSourcePaths() as $path) {
foreach ($this->classes[$path] as $fqn => $interfaces) {
// If the rule presents all class or we matched the class.
if ($rule->getInvokableInterface() === '' || in_array(
$rule->getInvokableInterface(),
$interfaces,
true
)) {
$matchingClasses[] = $fqn;
}
}
}
return $matchingClasses;
}
/**
* Fetches all available and autoloadable classes from the given paths.
*
* @param AutoInvokeRuleInterface $rule
*/
protected function fetchClasses(AutoInvokeRuleInterface $rule)
{
foreach ($rule->getSourcePaths() as $path) {
if (empty($this->classes[$path])) {
$this->classes[$path] = [];
foreach ($this->fetchClassesFromPath($path) as $fqn => $interfaces) {
$this->classes[$path][$fqn] = $interfaces;
}
}
}
}
/**
* Fetches all available and autoloadable classes from the given path.
*
* @param string $path
*
* @return Generator
*/
protected function fetchClassesFromPath(string $path): Generator
{
foreach ($this->getFinder()->files()->name('*.php')->in($path) as $file) {
foreach ($this->fetchClassesFromContent($file->getContents()) as $fqn => $interfaces) {
yield $fqn => $interfaces;
}
}
}
/**
* Returns a Finder instance.
*
* @return Finder
*/
protected function getFinder(): Finder
{
return new Finder();
}
/**
* Fetches all available and autoloadable classes from the given content.
*
* @param string $content
*
* @return Generator
*/
protected function fetchClassesFromContent(string $content): Generator
{
$namespace = ClassParser::getNameSpaceFromContent($content);
$className = ClassParser::getClassNameFromContent($content);
if (empty($className) === false) {
foreach ($className as $currentClassName) {
$fqn = $namespace . '\\' . $currentClassName;
try {
$reflectionClass = new ReflectionClass($fqn);
if ($reflectionClass->isAbstract() === false) {
yield $fqn => $reflectionClass->getInterfaceNames();
}
} catch (ReflectionException $e) {
//echo $e->getMessage() . PHP_EOL;
}
}
}
}
}