From 7827b8d5b12d3d325189fe3740a17982b3431ce4 Mon Sep 17 00:00:00 2001 From: whwyy Date: Fri, 12 Jun 2026 23:57:19 +0800 Subject: [PATCH] eee --- ChangeSet.php | 74 +++++++++++++++++++++++++++--- Container.php | 18 +++++++- HotReloadState.php | 12 +++-- ScanManifest.php | 46 ++++++++++++++++++- Scanner.php | 111 ++++----------------------------------------- composer.json | 2 +- 6 files changed, 147 insertions(+), 116 deletions(-) diff --git a/ChangeSet.php b/ChangeSet.php index f53830f..4824772 100644 --- a/ChangeSet.php +++ b/ChangeSet.php @@ -6,34 +6,75 @@ namespace Kiri\Di; class ChangeSet { + + /** + * @var array + */ private array $changedFiles = []; + + /** + * @var array + */ private array $removedFiles = []; + + /** + * @var array + */ private array $changedClasses = []; + + /** + * @var array + */ private array $removedClasses = []; + + /** + * @param string $file + * @return void + */ public function addChangedFile(string $file): void { $this->changedFiles[$file] = true; } + + /** + * @param string $file + * @return void + */ public function addRemovedFile(string $file): void { $this->removedFiles[$file] = true; } + + /** + * @param string $class + * @return void + */ public function addChangedClass(string $class): void { $this->changedClasses[$class] = true; } + + /** + * @param string $class + * @return void + */ public function addRemovedClass(string $class): void { $this->removedClasses[$class] = true; } + + /** + * @param ChangeSet $changeSet + * @return $this + */ public function merge(ChangeSet $changeSet): self { foreach ($changeSet->getChangedFiles() as $file) { @@ -55,39 +96,60 @@ class ChangeSet return $this; } + + /** + * @return array + */ public function getChangedFiles(): array { return array_keys($this->changedFiles); } + + /** + * @return array + */ public function getRemovedFiles(): array { return array_keys($this->removedFiles); } + + /** + * @return array + */ public function getChangedClasses(): array { return array_keys($this->changedClasses); } + + /** + * @return array + */ public function getRemovedClasses(): array { return array_keys($this->removedClasses); } + + /** + * @return bool + */ public function hasChanges(): bool { - return $this->changedFiles !== [] - || $this->removedFiles !== [] - || $this->changedClasses !== [] - || $this->removedClasses !== []; + return $this->changedFiles !== [] || $this->removedFiles !== [] || $this->changedClasses !== [] || $this->removedClasses !== []; } + + /** + * @return array + */ public function toArray(): array { return [ - 'changed_files' => $this->getChangedFiles(), - 'removed_files' => $this->getRemovedFiles(), + 'changed_files' => $this->getChangedFiles(), + 'removed_files' => $this->getRemovedFiles(), 'changed_classes' => $this->getChangedClasses(), 'removed_classes' => $this->getRemovedClasses(), ]; diff --git a/Container.php b/Container.php index f336c1e..76956c1 100644 --- a/Container.php +++ b/Container.php @@ -225,7 +225,14 @@ class Container implements ContainerInterface $construct = $this->getMethodParams($handler); } - $newInstance = $reflect->newInstanceArgs($construct); + $isController = class_exists(\Kiri\Router\Base\Controller::class) && $reflect->isSubclassOf(\Kiri\Router\Base\Controller::class); + $needsProxy = !$isController && class_exists(\Kiri\Router\Annotate\DeferRegistry::class) && \Kiri\Router\Annotate\DeferRegistry::hasAny($className); + + if ($needsProxy) { + $newInstance = \Kiri\Router\Annotate\DeferProxyGenerator::create($className, $construct); + } else { + $newInstance = $reflect->newInstanceArgs($construct); + } return $this->runInit($reflect, static::configure($newInstance, $config)); } @@ -252,7 +259,14 @@ class Container implements ContainerInterface if (empty($construct) && ($handler = $reflect->getConstructor()) !== null) { $construct = $this->getMethodParams($handler); } - $newInstance = $reflect->newInstanceArgs($construct); + $isController = class_exists(\Kiri\Router\Base\Controller::class) && $reflect->isSubclassOf(\Kiri\Router\Base\Controller::class); + $needsProxy = !$isController && class_exists(\Kiri\Router\Annotate\DeferRegistry::class) && \Kiri\Router\Annotate\DeferRegistry::hasAny($reflect->getName()); + + if ($needsProxy) { + $newInstance = \Kiri\Router\Annotate\DeferProxyGenerator::create($reflect->getName(), $construct); + } else { + $newInstance = $reflect->newInstanceArgs($construct); + } return $this->runInit($reflect, static::configure($newInstance, $config)); } diff --git a/HotReloadState.php b/HotReloadState.php index 3468434..d78f4e6 100644 --- a/HotReloadState.php +++ b/HotReloadState.php @@ -6,13 +6,17 @@ namespace Kiri\Di; class HotReloadState { - private const MAX_AGE_SECONDS = 30; + private const int MAX_AGE_SECONDS = 30; public function store(array $changedFiles): void { $payload = [ 'timestamp' => time(), - 'changed_files' => array_values(array_unique(array_map([$this, 'normalizePath'], array_filter($changedFiles)))), + 'changed_files' => $changedFiles + |> array_filter(...) + |> (fn($x) => array_map([$this, 'normalizePath'], $x)) + |> array_unique(...) + |> array_values(...), ]; $directory = dirname($this->getFilePath()); @@ -46,7 +50,9 @@ class HotReloadState } $files = is_array($data['changed_files'] ?? null) ? $data['changed_files'] : []; - return array_values(array_unique(array_map([$this, 'normalizePath'], $files))); + return array_map([$this, 'normalizePath'], $files) + |> array_unique(...) + |> array_values(...); } private function getFilePath(): string diff --git a/ScanManifest.php b/ScanManifest.php index eda1b77..61c45d5 100644 --- a/ScanManifest.php +++ b/ScanManifest.php @@ -8,6 +8,13 @@ class ScanManifest { private array $entries = []; + + /** + * @param string $path + * @param int $mtime + * @param array $classes + * @return void + */ public function set(string $path, int $mtime, array $classes): void { $this->entries[$path] = [ @@ -16,21 +23,41 @@ class ScanManifest ]; } + + /** + * @param string $path + * @return bool + */ public function has(string $path): bool { return isset($this->entries[$path]); } + + /** + * @param string $path + * @return int|null + */ public function getMtime(string $path): ?int { return $this->entries[$path]['mtime'] ?? null; } + + /** + * @param string $path + * @return array + */ public function getClasses(string $path): array { return $this->entries[$path]['classes'] ?? []; } + + /** + * @param string $path + * @return array + */ public function remove(string $path): array { $classes = $this->getClasses($path); @@ -38,20 +65,37 @@ class ScanManifest return $classes; } + + /** + * @return array + */ public function all(): array { return $this->entries; } + + /** + * @param string|null $prefix + * @return array + */ public function paths(?string $prefix = null): array { if ($prefix === null) { return array_keys($this->entries); } - return array_values(array_filter(array_keys($this->entries), fn(string $path) => str_starts_with($path, $prefix))); + return $this->entries + |> array_keys(...) + |> (fn($x) => array_filter($x, fn(string $path) => str_starts_with($path, $prefix))) + |> array_values(...); } + + /** + * @param array $entries + * @return void + */ public function fromArray(array $entries): void { $this->entries = []; diff --git a/Scanner.php b/Scanner.php index 513ac31..7b1bae6 100644 --- a/Scanner.php +++ b/Scanner.php @@ -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) { diff --git a/composer.json b/composer.json index a77fdea..2ec5b1e 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ ], "license": "MIT", "require": { - "php": ">=8.4", + "php": ">=8.5", "psr/container": "^2.0" }, "autoload": {