This commit is contained in:
2026-06-12 23:57:19 +08:00
parent 0ff2cd7a06
commit 7827b8d5b1
6 changed files with 147 additions and 116 deletions
+8 -103
View File
@@ -8,6 +8,7 @@ use DirectoryIterator;
use Kiri\Abstracts\Component;
use Kiri\Di\Inject\Container;
use Kiri\Di\Inject\Skip;
use Kiri\Di\Interface\InjectMethodInterface;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use ReflectionMethod;
@@ -41,7 +42,6 @@ class Scanner extends Component
'cache_enabled' => false,
'cache_ttl' => 3600,
'debug' => false,
'class_name_strategy' => 'auto',
];
public function __construct()
@@ -239,9 +239,12 @@ class Scanner extends Component
private function loadAndParseFile(string $path): array
{
$this->optimizeWithOpcache($path);
require_once $path;
$classes = $this->getClassNamesForFile($path);
$before = get_declared_classes();
require_once $path;
$after = get_declared_classes();
$classes = array_values(array_diff($after, $before));
foreach ($classes as $class) {
if (class_exists($class)) {
$this->analyzeClass($class);
@@ -251,104 +254,6 @@ class Scanner extends Component
return $classes;
}
private function getClassNamesForFile(string $path): array
{
$strategy = $this->config['class_name_strategy'];
$classes = [];
if (in_array($strategy, ['auto', 'extract', 'both'], true)) {
$class = $this->extractClassNameFromFile($path);
if ($class !== null) {
$classes[] = $class;
}
}
if ($classes === [] || in_array($strategy, ['rename', 'both'], true)) {
$classes[] = $this->renamePathToClassName($path);
}
return array_values(array_unique(array_filter($classes)));
}
private function canExtractClassName(): bool
{
return function_exists('token_get_all');
}
private function extractClassNameFromFile(string $path): ?string
{
if (!$this->canExtractClassName()) {
return null;
}
$content = @file_get_contents($path);
if ($content === false || $content === '') {
return null;
}
$tokens = @token_get_all($content);
if (!$tokens) {
return null;
}
$namespace = '';
$class = '';
$collectingNamespace = false;
$collectingClass = false;
$previousToken = null;
foreach ($tokens as $token) {
if (is_array($token)) {
$text = $token[1];
if ($token[0] === T_NAMESPACE) {
$namespace = '';
$collectingNamespace = true;
} elseif ($collectingNamespace && in_array($token[0], [T_STRING, T_NAME_QUALIFIED, T_NS_SEPARATOR], true)) {
$namespace .= $text;
} elseif ($collectingNamespace && $token[0] === T_WHITESPACE) {
} elseif (in_array($token[0], [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true) && $previousToken !== T_DOUBLE_COLON && $previousToken !== T_NEW) {
$collectingClass = true;
} elseif ($collectingClass && $token[0] === T_STRING) {
$class = $text;
break;
} elseif ($collectingNamespace) {
$collectingNamespace = false;
}
if ($token[0] !== T_WHITESPACE) {
$previousToken = $token[0];
}
continue;
}
if ($collectingNamespace && ($token === ';' || $token === '{')) {
$collectingNamespace = false;
}
if ($token === '{') {
$collectingClass = false;
}
$previousToken = $token;
}
if ($class === '') {
return null;
}
return $namespace !== '' ? $namespace . '\\' . $class : $class;
}
private function renamePathToClassName(string $path): string
{
$relativePath = str_replace($this->basePath, '', $this->normalizePath($path));
$relativePath = str_replace('.php', '', $relativePath);
$parts = explode('/', trim($relativePath, '/\\'));
$parts = array_values(array_filter($parts, fn(string $part) => $part !== ''));
$parts = array_map('ucfirst', $parts);
return implode('\\', $parts);
}
private function optimizeWithOpcache(string $path): void
{
if ($this->hasOpcache === null) {
@@ -403,7 +308,7 @@ class Scanner extends Component
private function analyzeClassMethods(ReflectionClass $reflect, string $class): void
{
foreach ($reflect->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
foreach ($reflect->getMethods() as $method) {
if ($method->isStatic() || $method->getDeclaringClass()->getName() !== $class) {
continue;
}
@@ -422,7 +327,7 @@ class Scanner extends Component
try {
$instance = $attribute->newInstance();
if (method_exists($instance, 'dispatch')) {
if ($instance instanceof InjectMethodInterface) {
$instance->dispatch($class, $method->getName());
}
} catch (Throwable $e) {