Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 689fd09358 | |||
| 2753a42d82 | |||
| 8d340a8a45 | |||
| e1a23090d6 | |||
| b08fdb1801 | |||
| a0b975003a | |||
| 81250722ea | |||
| b7712d3d9d | |||
| c38fb6ac4c | |||
| 7827b8d5b1 | |||
| 0ff2cd7a06 | |||
| 807b86c4cc | |||
| d23e55391e | |||
| 754042b994 | |||
| 8a4f68e984 | |||
| 3b7e6f992f | |||
| de0a39d207 | |||
| 0110f1947a | |||
| 93fc09342a | |||
| e16ceadaa7 | |||
| 34685ead22 | |||
| c643be8548 | |||
| f9e23c59ef | |||
| f89e8106f5 | |||
| 2f02c5af12 |
+157
@@ -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(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
+139
-2
@@ -153,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
|
||||||
@@ -171,7 +294,14 @@ class Container implements ContainerInterface
|
|||||||
$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));
|
||||||
}
|
}
|
||||||
@@ -198,7 +328,14 @@ class Container implements ContainerInterface
|
|||||||
if (empty($construct) && ($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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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": {
|
||||||
|
|||||||
Reference in New Issue
Block a user