32 Commits

Author SHA1 Message Date
as2252258 689fd09358 Add scanner manifest replay control 2026-07-10 17:03:41 +08:00
as2252258 2753a42d82 eee 2026-07-07 17:00:04 +08:00
as2252258 8d340a8a45 eee 2026-07-07 16:00:06 +08:00
as2252258 e1a23090d6 eee 2026-07-07 15:09:38 +08:00
as2252258 b08fdb1801 eee 2026-07-03 14:25:26 +08:00
as2252258 a0b975003a eee 2026-06-28 20:20:20 +08:00
as2252258 81250722ea eee 2026-06-24 20:45:36 +08:00
as2252258 b7712d3d9d eee 2026-06-24 20:21:29 +08:00
as2252258 c38fb6ac4c eee 2026-06-24 20:11:11 +08:00
as2252258 7827b8d5b1 eee 2026-06-12 23:57:19 +08:00
as2252258 0ff2cd7a06 eee 2026-04-17 16:47:04 +08:00
as2252258 807b86c4cc eee 2026-04-17 16:45:30 +08:00
as2252258 d23e55391e eee 2026-04-17 16:30:52 +08:00
as2252258 754042b994 11
Signed-off-by: 向林 <as2252258@163.com>
2025-12-31 06:57:29 +00:00
as2252258 8a4f68e984 eee 2025-12-31 01:50:09 +08:00
as2252258 3b7e6f992f eee 2025-12-31 01:10:27 +08:00
as2252258 de0a39d207 eee 2025-12-31 01:09:30 +08:00
as2252258 0110f1947a eee 2025-12-31 01:07:57 +08:00
as2252258 93fc09342a eee 2025-12-31 00:42:12 +08:00
as2252258 e16ceadaa7 eee 2025-12-31 00:19:29 +08:00
dongzhiyong 34685ead22 a 2025-12-23 14:39:55 +08:00
as2252258 c643be8548 eee 2025-12-22 23:14:45 +08:00
as2252258 f9e23c59ef 添加opcache重置
Signed-off-by: 向林 <as2252258@163.com>
2025-12-22 06:57:32 +00:00
as2252258 f89e8106f5 eee 2025-12-18 15:39:42 +08:00
as2252258 2f02c5af12 eee 2025-07-14 15:34:56 +08:00
as2252258 9e954135f6 eee 2025-07-11 15:55:15 +08:00
as2252258 7de790d65f eee 2025-07-11 15:32:33 +08:00
as2252258 855da03137 eee 2025-07-11 15:28:10 +08:00
as2252258 0081a30f22 eee 2025-07-11 15:25:06 +08:00
as2252258 3f2d3b0f04 eee 2025-07-11 15:22:52 +08:00
as2252258 5fa5646024 eee 2025-07-11 15:11:27 +08:00
as2252258 045c9293d5 eee 2025-07-11 11:51:05 +08:00
6 changed files with 1452 additions and 122 deletions
+157
View File
@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
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) {
$this->addChangedFile($file);
}
foreach ($changeSet->getRemovedFiles() as $file) {
$this->addRemovedFile($file);
}
foreach ($changeSet->getChangedClasses() as $class) {
$this->addChangedClass($class);
}
foreach ($changeSet->getRemovedClasses() as $class) {
$this->addRemovedClass($class);
}
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 array
*/
public function toArray(): array
{
return [
'changed_files' => $this->getChangedFiles(),
'removed_files' => $this->getRemovedFiles(),
'changed_classes' => $this->getChangedClasses(),
'removed_classes' => $this->getRemovedClasses(),
];
}
}
+148 -6
View File
@@ -14,6 +14,7 @@ use Closure;
use Exception; use Exception;
use Kiri\Di\Interface\InjectTargetInterface; use Kiri\Di\Interface\InjectTargetInterface;
use Kiri\Router\Interface\ValidatorInterface; use Kiri\Router\Interface\ValidatorInterface;
use Kiri\Server\Task\OnTaskFinish;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use ReflectionAttribute; use ReflectionAttribute;
use ReflectionClass; use ReflectionClass;
@@ -91,7 +92,8 @@ class Container implements ContainerInterface
*/ */
public function get(string $id): object public function get(string $id): object
{ {
if (isset($this->_singletons[$id])) return $this->_singletons[$id]; if (isset($this->_singletons[$id]))
return $this->_singletons[$id];
if (isset($this->_interfaces[$id])) { if (isset($this->_interfaces[$id])) {
return $this->_singletons[$id] = $this->make($this->_interfaces[$id]); return $this->_singletons[$id] = $this->make($this->_interfaces[$id]);
} else { } else {
@@ -151,6 +153,129 @@ class Container implements ContainerInterface
} }
public function forgetReflection(string $className): void
{
unset($this->_reflection[$className]);
}
public function forgetMethodParams(string $className, ?string $method = null): void
{
if (!isset($this->_parameters[$className])) {
return;
}
if ($method === null) {
unset($this->_parameters[$className]);
return;
}
unset($this->_parameters[$className][$method]);
if ($this->_parameters[$className] === []) {
unset($this->_parameters[$className]);
}
}
public function forgetClass(string $className): void
{
unset($this->_singletons[$className]);
$this->forgetReflection($className);
$this->forgetMethodParams($className);
}
public function forgetNamespace(string $prefix): void
{
foreach (array_keys($this->_singletons) as $className) {
if ($className !== ContainerInterface::class && str_starts_with($className, $prefix)) {
unset($this->_singletons[$className]);
}
}
foreach (array_keys($this->_reflection) as $className) {
if (str_starts_with($className, $prefix)) {
unset($this->_reflection[$className]);
}
}
foreach (array_keys($this->_parameters) as $className) {
if (str_starts_with($className, $prefix)) {
unset($this->_parameters[$className]);
}
}
}
/**
* 清理所有非基础设施类的单例引用,释放 Swoole 常驻进程中的内存
* 保留框架核心类(Kiri、Database、Psr、Symfony)的单例
* 应在请求/任务完成后调用,防止单例映射无限增长
* @return void
*/
public function clearNonInfrastructure(): void
{
$keepPrefixes = [
ContainerInterface::class,
'Kiri\\', 'Database\\', 'Psr\\', 'Symfony\\',
'Swoole\\', 'MongoDB\\',
];
foreach (array_keys($this->_singletons) as $className) {
$shouldKeep = false;
foreach ($keepPrefixes as $prefix) {
if (str_starts_with($className, $prefix)) {
$shouldKeep = true;
break;
}
}
if (!$shouldKeep) {
unset($this->_singletons[$className]);
}
}
foreach (array_keys($this->_reflection) as $className) {
$shouldKeep = false;
foreach ($keepPrefixes as $prefix) {
if (str_starts_with($className, $prefix)) {
$shouldKeep = true;
break;
}
}
if (!$shouldKeep) {
unset($this->_reflection[$className]);
}
}
foreach (array_keys($this->_parameters) as $className) {
$shouldKeep = false;
foreach ($keepPrefixes as $prefix) {
if (str_starts_with($className, $prefix)) {
$shouldKeep = true;
break;
}
}
if (!$shouldKeep) {
unset($this->_parameters[$className]);
}
}
}
/**
* 获取当前单例缓存统计信息,用于内存监控
* @return array{singletons: int, reflections: int, params: int}
*/
public function getMemoryStats(): array
{
return [
'singletons' => count($this->_singletons),
'reflections' => count($this->_reflection),
'params' => count($this->_parameters),
];
}
/** /**
* @param string $className * @param string $className
* @param array $construct * @param array $construct
@@ -165,15 +290,24 @@ class Container implements ContainerInterface
throw new ReflectionException('Class ' . $className . ' cannot be instantiated'); throw new ReflectionException('Class ' . $className . ' cannot be instantiated');
} }
if (($handler = $reflect->getConstructor()) !== null) { if (empty($construct) && ($handler = $reflect->getConstructor()) !== null) {
$construct = $this->getMethodParams($handler); $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\Defer\DeferRegistry::class) && \Kiri\Router\Defer\DeferRegistry::hasAny($className);
if ($needsProxy) {
$newInstance = \Kiri\Router\Defer\DeferProxyGenerator::create($className, $construct);
} else {
$newInstance = $reflect->newInstanceArgs($construct);
}
return $this->runInit($reflect, static::configure($newInstance, $config)); return $this->runInit($reflect, static::configure($newInstance, $config));
} }
/** /**
* @param ReflectionClass $reflect * @param ReflectionClass $reflect
* @param array $construct * @param array $construct
@@ -191,10 +325,17 @@ class Container implements ContainerInterface
throw new ReflectionException('Class ' . $reflect->getName() . ' cannot be instantiated'); throw new ReflectionException('Class ' . $reflect->getName() . ' cannot be instantiated');
} }
if (($handler = $reflect->getConstructor()) !== null) { if (empty($construct) && ($handler = $reflect->getConstructor()) !== null) {
$construct = $this->getMethodParams($handler); $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\Defer\DeferRegistry::class) && \Kiri\Router\Defer\DeferRegistry::hasAny($reflect->getName());
if ($needsProxy) {
$newInstance = \Kiri\Router\Defer\DeferProxyGenerator::create($reflect->getName(), $construct);
} else {
$newInstance = $reflect->newInstanceArgs($construct);
}
return $this->runInit($reflect, static::configure($newInstance, $config)); return $this->runInit($reflect, static::configure($newInstance, $config));
} }
@@ -304,7 +445,8 @@ class Container implements ContainerInterface
{ {
$className = $parameters->getDeclaringClass()->getName(); $className = $parameters->getDeclaringClass()->getName();
$methodName = $parameters->getName(); $methodName = $parameters->getName();
if (!isset($this->_parameters[$className])) $this->_parameters[$className] = []; if (!isset($this->_parameters[$className]))
$this->_parameters[$className] = [];
if (!isset($this->_parameters[$className][$methodName])) { if (!isset($this->_parameters[$className][$methodName])) {
return $this->_parameters[$className][$methodName] = $this->resolveMethodParams($parameters); return $this->_parameters[$className][$methodName] = $this->resolveMethodParams($parameters);
} else { } else {
+72
View File
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Kiri\Di;
class HotReloadState
{
private const int MAX_AGE_SECONDS = 30;
public function store(array $changedFiles): void
{
$normalizedFiles = array_map([$this, 'normalizePath'], array_filter($changedFiles));
$normalizedFiles = array_values(array_unique($normalizedFiles));
$payload = [
'timestamp' => time(),
'changed_files' => $normalizedFiles,
];
$directory = dirname($this->getFilePath());
if (!is_dir($directory)) {
mkdir($directory, 0755, true);
}
file_put_contents($this->getFilePath(), json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
public function consume(): array
{
return $this->peek();
}
public function peek(): array
{
$file = $this->getFilePath();
if (!file_exists($file)) {
return [];
}
$data = json_decode((string)file_get_contents($file), true);
if (!is_array($data)) {
return [];
}
$timestamp = (int)($data['timestamp'] ?? 0);
if ($timestamp < 1 || (time() - $timestamp) > self::MAX_AGE_SECONDS) {
return [];
}
$files = is_array($data['changed_files'] ?? null) ? $data['changed_files'] : [];
$files = array_map([$this, 'normalizePath'], $files);
$files = array_values(array_unique($files));
return $files;
}
private function getFilePath(): string
{
$basePath = $this->normalizePath($_SERVER['PWD'] ?? APP_PATH ?? getcwd());
$runtimePath = defined('APP_PATH')
? rtrim(str_replace('\\', '/', APP_PATH), '/') . '/storage/.kiri-hot-reload/'
: sys_get_temp_dir() . '/kiri-hot-reload/';
return $runtimePath . md5($basePath) . '.json';
}
private function normalizePath(string $path): string
{
$resolved = realpath($path) ?: $path;
return str_replace('\\', '/', $resolved);
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace Kiri\Di;
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] = [
'mtime' => $mtime,
'classes' => array_values(array_unique($classes)),
];
}
/**
* @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);
unset($this->entries[$path]);
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);
}
$keys = array_keys($this->entries);
$filtered = array_filter($keys, fn(string $path) => str_starts_with($path, $prefix));
return array_values($filtered);
}
/**
* @param array $entries
* @return void
*/
public function fromArray(array $entries): void
{
$this->entries = [];
foreach ($entries as $path => $entry) {
$mtime = (int)($entry['mtime'] ?? 0);
$classes = is_array($entry['classes'] ?? null) ? $entry['classes'] : [];
$this->set($path, $mtime, $classes);
}
}
}
+967 -115
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -9,7 +9,7 @@
], ],
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": ">=8.0", "php": ">=8.5",
"psr/container": "^2.0" "psr/container": "^2.0"
}, },
"autoload": { "autoload": {