eee
This commit is contained in:
+160
-132
@@ -18,46 +18,34 @@ class Scanner extends Component
|
|||||||
#[Container(ContainerInterface::class)]
|
#[Container(ContainerInterface::class)]
|
||||||
public ContainerInterface $container;
|
public ContainerInterface $container;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array 已处理的文件路径列表
|
|
||||||
*/
|
|
||||||
public array $files = [];
|
public array $files = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array 文件修改时间缓存
|
|
||||||
*/
|
|
||||||
private array $fileMtimes = [];
|
private array $fileMtimes = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool|null OPcache可用性缓存
|
|
||||||
*/
|
|
||||||
private ?bool $hasOpcache = null;
|
private ?bool $hasOpcache = null;
|
||||||
|
private string $basePath;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array 配置选项
|
|
||||||
*/
|
|
||||||
private array $config = [
|
private array $config = [
|
||||||
'skip_patterns' => ['/vendor/', '/tests/', '/cache/', '/node_modules/'],
|
'skip_patterns' => ['/vendor/', '/tests/', '/cache/', '/node_modules/'],
|
||||||
'skip_directories' => [],
|
'skip_directories' => [],
|
||||||
'extensions' => ['php'],
|
'extensions' => ['php'],
|
||||||
'max_depth' => 20,
|
'max_depth' => 20,
|
||||||
'follow_links' => false,
|
'follow_links' => false,
|
||||||
'cache_enabled' => true,
|
'cache_enabled' => false,
|
||||||
'cache_ttl' => 3600,
|
'cache_ttl' => 3600,
|
||||||
'debug' => false,
|
'debug' => false,
|
||||||
|
'class_name_strategy' => 'auto', // 'auto', 'extract', 'rename', or 'both'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
public function __construct()
|
||||||
* 设置扫描器配置
|
{
|
||||||
*/
|
$this->basePath = $_SERVER['PWD'] ?? APP_PATH ?? getcwd();
|
||||||
|
}
|
||||||
|
|
||||||
public function setConfig(array $config): void
|
public function setConfig(array $config): void
|
||||||
{
|
{
|
||||||
$this->config = array_merge($this->config, $config);
|
$this->config = array_merge($this->config, $config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 主扫描方法,支持缓存
|
|
||||||
*/
|
|
||||||
public function scan(string $path, ?string $cacheFile = null): void
|
public function scan(string $path, ?string $cacheFile = null): void
|
||||||
{
|
{
|
||||||
if ($this->config['cache_enabled']) {
|
if ($this->config['cache_enabled']) {
|
||||||
@@ -75,9 +63,6 @@ class Scanner extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 扫描目录(替换原有的load_directory)
|
|
||||||
*/
|
|
||||||
private function scanDirectory(string $path, int $depth = 0): void
|
private function scanDirectory(string $path, int $depth = 0): void
|
||||||
{
|
{
|
||||||
if ($depth > $this->config['max_depth']) {
|
if ($depth > $this->config['max_depth']) {
|
||||||
@@ -103,7 +88,6 @@ class Scanner extends Component
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是符号链接且不跟随链接,则跳过
|
|
||||||
if ($item->isLink() && !$this->config['follow_links']) {
|
if ($item->isLink() && !$this->config['follow_links']) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -115,22 +99,15 @@ class Scanner extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否应该跳过该目录项
|
|
||||||
*/
|
|
||||||
private function shouldSkipItem(DirectoryIterator $item): bool
|
private function shouldSkipItem(DirectoryIterator $item): bool
|
||||||
{
|
{
|
||||||
return $item->isDot() || str_starts_with($item->getFilename(), '.');
|
return $item->isDot() || str_starts_with($item->getFilename(), '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否应该跳过目录
|
|
||||||
*/
|
|
||||||
private function shouldSkipDirectory(string $path): bool
|
private function shouldSkipDirectory(string $path): bool
|
||||||
{
|
{
|
||||||
$path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
$path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||||
|
|
||||||
// 检查配置文件中的跳过目录
|
|
||||||
$skipDirs = array_merge(
|
$skipDirs = array_merge(
|
||||||
$this->config['skip_directories'],
|
$this->config['skip_directories'],
|
||||||
config('site.scanner.skip', [])
|
config('site.scanner.skip', [])
|
||||||
@@ -140,7 +117,6 @@ class Scanner extends Component
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查跳过模式
|
|
||||||
foreach ($this->config['skip_patterns'] as $pattern) {
|
foreach ($this->config['skip_patterns'] as $pattern) {
|
||||||
if (strpos($path, $pattern) !== false) {
|
if (strpos($path, $pattern) !== false) {
|
||||||
return true;
|
return true;
|
||||||
@@ -150,22 +126,16 @@ class Scanner extends Component
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理单个文件
|
|
||||||
*/
|
|
||||||
private function processFile(string $path): void
|
private function processFile(string $path): void
|
||||||
{
|
{
|
||||||
// 检查文件扩展名
|
|
||||||
if (!in_array(pathinfo($path, PATHINFO_EXTENSION), $this->config['extensions'])) {
|
if (!in_array(pathinfo($path, PATHINFO_EXTENSION), $this->config['extensions'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否已处理
|
|
||||||
if (in_array($path, $this->files)) {
|
if (in_array($path, $this->files)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查文件是否需要重新处理(基于修改时间)
|
|
||||||
if (!$this->shouldProcessFile($path)) {
|
if (!$this->shouldProcessFile($path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -180,16 +150,12 @@ class Scanner extends Component
|
|||||||
'action' => 'process_file',
|
'action' => 'process_file',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 如果是语法错误,可以选择抛出或记录
|
|
||||||
if ($e instanceof \ParseError) {
|
if ($e instanceof \ParseError) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断文件是否需要处理
|
|
||||||
*/
|
|
||||||
private function shouldProcessFile(string $path): bool
|
private function shouldProcessFile(string $path): bool
|
||||||
{
|
{
|
||||||
if (!file_exists($path)) {
|
if (!file_exists($path)) {
|
||||||
@@ -198,7 +164,6 @@ class Scanner extends Component
|
|||||||
|
|
||||||
$mtime = filemtime($path);
|
$mtime = filemtime($path);
|
||||||
|
|
||||||
// 如果没有缓存过修改时间,或者文件已更新,则需要处理
|
|
||||||
if (!isset($this->fileMtimes[$path]) || $this->fileMtimes[$path] < $mtime) {
|
if (!isset($this->fileMtimes[$path]) || $this->fileMtimes[$path] < $mtime) {
|
||||||
$this->fileMtimes[$path] = $mtime;
|
$this->fileMtimes[$path] = $mtime;
|
||||||
return true;
|
return true;
|
||||||
@@ -207,90 +172,92 @@ class Scanner extends Component
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载并解析文件
|
|
||||||
*/
|
|
||||||
private function loadAndParseFile(string $path): void
|
private function loadAndParseFile(string $path): void
|
||||||
{
|
{
|
||||||
// OPcache优化
|
|
||||||
$this->optimizeWithOpcache($path);
|
$this->optimizeWithOpcache($path);
|
||||||
|
|
||||||
// 加载文件
|
|
||||||
require_once $path;
|
require_once $path;
|
||||||
|
|
||||||
// 尝试从文件内容提取类名(更准确)
|
// 尝试获取类名
|
||||||
$class = $this->extractClassNameFromFile($path);
|
$class = $this->getClassNameFromPath($path);
|
||||||
|
|
||||||
if ($class && class_exists($class)) {
|
if ($class && class_exists($class)) {
|
||||||
$this->analyzeClass($class);
|
$this->analyzeClass($class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用OPcache优化文件
|
* 从文件路径获取类名(支持多种策略)
|
||||||
*/
|
*/
|
||||||
private function optimizeWithOpcache(string $path): void
|
private function getClassNameFromPath(string $path): ?string
|
||||||
{
|
{
|
||||||
if ($this->hasOpcache === null) {
|
$strategy = $this->config['class_name_strategy'];
|
||||||
$this->hasOpcache = function_exists('opcache_invalidate')
|
|
||||||
&& function_exists('opcache_compile_file');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->hasOpcache) {
|
// 移除基础路径前缀,类似原代码逻辑
|
||||||
try {
|
$relativePath = str_replace($this->basePath, '', $path);
|
||||||
opcache_invalidate($path, true);
|
$relativePath = str_replace('.php', '', $relativePath);
|
||||||
opcache_compile_file($path);
|
|
||||||
} catch (Throwable $e) {
|
return $this->renamePathToClassName($relativePath);
|
||||||
// 静默处理OPcache错误,不影响主要功能
|
|
||||||
if ($this->config['debug']) {
|
|
||||||
$this->logError($e, [
|
|
||||||
'file' => $path,
|
|
||||||
'action' => 'opcache_optimize',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从文件内容提取完整的类名(命名空间+类名)
|
* 检查是否可以安全地从文件内容提取类名
|
||||||
|
*/
|
||||||
|
private function canExtractClassName(): bool
|
||||||
|
{
|
||||||
|
// 如果有tokenizer扩展,优先使用它
|
||||||
|
return function_exists('token_get_all');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从文件内容提取类名(原代码没有的功能)
|
||||||
*/
|
*/
|
||||||
private function extractClassNameFromFile(string $path): ?string
|
private function extractClassNameFromFile(string $path): ?string
|
||||||
{
|
{
|
||||||
|
if (!$this->canExtractClassName()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$content = @file_get_contents($path);
|
$content = @file_get_contents($path);
|
||||||
if (!$content) {
|
if (!$content) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokens = token_get_all($content);
|
$tokens = @token_get_all($content);
|
||||||
|
if (!$tokens) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$namespace = '';
|
$namespace = '';
|
||||||
$class = '';
|
$class = '';
|
||||||
$classToken = false;
|
$collectingNamespace = false;
|
||||||
$collecting = false;
|
$collectingClass = false;
|
||||||
|
|
||||||
|
var_dump($tokens[1]);
|
||||||
foreach ($tokens as $token) {
|
foreach ($tokens as $token) {
|
||||||
if (is_array($token)) {
|
if (is_array($token)) {
|
||||||
if ($token[0] === T_NAMESPACE) {
|
if ($token[0] === T_NAMESPACE) {
|
||||||
$namespace = '';
|
$namespace = '';
|
||||||
$collecting = true;
|
$collectingNamespace = true;
|
||||||
} elseif ($collecting && $token[0] === T_STRING) {
|
} elseif ($collectingNamespace && $token[0] === T_STRING) {
|
||||||
$namespace .= $token[1];
|
$namespace .= $token[1];
|
||||||
} elseif ($collecting && $token[0] === T_NS_SEPARATOR) {
|
} elseif ($collectingNamespace && $token[0] === T_NS_SEPARATOR) {
|
||||||
$namespace .= '\\';
|
$namespace .= '\\';
|
||||||
} elseif ($collecting && $token[0] === T_WHITESPACE) {
|
} elseif ($collectingNamespace && $token[0] === T_WHITESPACE) {
|
||||||
// 忽略空白
|
continue;
|
||||||
} else {
|
} elseif ($token[0] === T_CLASS) {
|
||||||
$collecting = false;
|
$collectingClass = true;
|
||||||
}
|
} elseif ($collectingClass && $token[0] === T_STRING) {
|
||||||
|
|
||||||
if ($token[0] === T_CLASS || $token[0] === T_INTERFACE || $token[0] === T_TRAIT) {
|
|
||||||
$classToken = true;
|
|
||||||
} elseif ($classToken && $token[0] === T_STRING) {
|
|
||||||
$class = $token[1];
|
$class = $token[1];
|
||||||
break;
|
break;
|
||||||
|
} elseif ($collectingNamespace) {
|
||||||
|
$collectingNamespace = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($token === ';' || $token === '{') {
|
if ($collectingNamespace && ($token === ';' || $token === '{')) {
|
||||||
$collecting = false;
|
$collectingNamespace = false;
|
||||||
|
}
|
||||||
|
if ($token === '{') {
|
||||||
|
$collectingClass = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,8 +270,48 @@ class Scanner extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分析类及其方法
|
* 将路径重命名为类名(原代码的逻辑)
|
||||||
*/
|
*/
|
||||||
|
private function renamePathToClassName(string $path): string
|
||||||
|
{
|
||||||
|
// 清理路径,移除开头和结尾的斜杠
|
||||||
|
$path = trim($path, '/\\');
|
||||||
|
|
||||||
|
// 分割路径部分
|
||||||
|
$parts = explode('/', str_replace('\\', '/', $path));
|
||||||
|
|
||||||
|
// 过滤空值并对每个部分应用ucfirst
|
||||||
|
$parts = array_filter($parts, function ($part) {
|
||||||
|
return !empty($part);
|
||||||
|
});
|
||||||
|
|
||||||
|
$parts = array_map('ucfirst', $parts);
|
||||||
|
|
||||||
|
return implode('\\', $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function optimizeWithOpcache(string $path): void
|
||||||
|
{
|
||||||
|
if ($this->hasOpcache === null) {
|
||||||
|
$this->hasOpcache = function_exists('opcache_invalidate')
|
||||||
|
&& function_exists('opcache_compile_file');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->hasOpcache) {
|
||||||
|
try {
|
||||||
|
opcache_invalidate($path, true);
|
||||||
|
opcache_compile_file($path);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
if ($this->config['debug']) {
|
||||||
|
$this->logError($e, [
|
||||||
|
'file' => $path,
|
||||||
|
'action' => 'opcache_optimize',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function analyzeClass(string $class): void
|
private function analyzeClass(string $class): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -317,12 +324,10 @@ class Scanner extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查跳过注解
|
|
||||||
if ($this->shouldSkipClass($reflect)) {
|
if ($this->shouldSkipClass($reflect)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分析类的方法
|
|
||||||
$this->analyzeClassMethods($reflect, $class);
|
$this->analyzeClassMethods($reflect, $class);
|
||||||
|
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
@@ -333,9 +338,6 @@ class Scanner extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否应该跳过该类
|
|
||||||
*/
|
|
||||||
private function shouldSkipClass(ReflectionClass $reflect): bool
|
private function shouldSkipClass(ReflectionClass $reflect): bool
|
||||||
{
|
{
|
||||||
$attributes = array_map(
|
$attributes = array_map(
|
||||||
@@ -347,14 +349,9 @@ class Scanner extends Component
|
|||||||
in_array(\Attribute::class, $attributes, true);
|
in_array(\Attribute::class, $attributes, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 分析类的方法
|
|
||||||
*/
|
|
||||||
private function analyzeClassMethods(ReflectionClass $reflect, string $class): void
|
private function analyzeClassMethods(ReflectionClass $reflect, string $class): void
|
||||||
{
|
{
|
||||||
var_dump($reflect->getName());
|
|
||||||
foreach ($reflect->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
foreach ($reflect->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||||
// 跳过静态方法和继承的方法
|
|
||||||
if ($method->isStatic() || $method->getDeclaringClass()->getName() !== $class) {
|
if ($method->isStatic() || $method->getDeclaringClass()->getName() !== $class) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -363,9 +360,6 @@ class Scanner extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理方法上的注解
|
|
||||||
*/
|
|
||||||
private function processMethodAttributes(ReflectionMethod $method, string $class): void
|
private function processMethodAttributes(ReflectionMethod $method, string $class): void
|
||||||
{
|
{
|
||||||
foreach ($method->getAttributes() as $attribute) {
|
foreach ($method->getAttributes() as $attribute) {
|
||||||
@@ -392,9 +386,6 @@ class Scanner extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 缓存相关方法
|
|
||||||
*/
|
|
||||||
private function getDefaultCacheFile(): string
|
private function getDefaultCacheFile(): string
|
||||||
{
|
{
|
||||||
$cacheDir = sys_get_temp_dir() . '/kiri-scanner-cache/';
|
$cacheDir = sys_get_temp_dir() . '/kiri-scanner-cache/';
|
||||||
@@ -406,21 +397,16 @@ class Scanner extends Component
|
|||||||
return $cacheDir . 'scanner_' . $projectHash . '.json';
|
return $cacheDir . 'scanner_' . $projectHash . '.json';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 从缓存加载
|
|
||||||
*/
|
|
||||||
private function loadFromCache(string $cacheFile, string $path): bool
|
private function loadFromCache(string $cacheFile, string $path): bool
|
||||||
{
|
{
|
||||||
if (!file_exists($cacheFile)) {
|
if (!file_exists($cacheFile)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查缓存是否过期
|
|
||||||
if (filemtime($cacheFile) + $this->config['cache_ttl'] < time()) {
|
if (filemtime($cacheFile) + $this->config['cache_ttl'] < time()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查目录是否有更新
|
|
||||||
if ($this->hasDirectoryChanged($path, $cacheFile)) {
|
if ($this->hasDirectoryChanged($path, $cacheFile)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -436,14 +422,10 @@ class Scanner extends Component
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查目录是否有更新
|
|
||||||
*/
|
|
||||||
private function hasDirectoryChanged(string $path, string $cacheFile): bool
|
private function hasDirectoryChanged(string $path, string $cacheFile): bool
|
||||||
{
|
{
|
||||||
$cacheMtime = filemtime($cacheFile);
|
$cacheMtime = filemtime($cacheFile);
|
||||||
|
|
||||||
// 简单的实现:检查目录本身和一级子目录
|
|
||||||
$dirIterator = new DirectoryIterator($path);
|
$dirIterator = new DirectoryIterator($path);
|
||||||
foreach ($dirIterator as $item) {
|
foreach ($dirIterator as $item) {
|
||||||
if ($item->isDot()) {
|
if ($item->isDot()) {
|
||||||
@@ -459,42 +441,32 @@ class Scanner extends Component
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存到缓存
|
|
||||||
*/
|
|
||||||
private function saveToCache(string $cacheFile): void
|
private function saveToCache(string $cacheFile): void
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
'files' => $this->files,
|
'files' => $this->files,
|
||||||
'mtimes' => $this->fileMtimes,
|
'mtimes' => $this->fileMtimes,
|
||||||
'timestamp' => time(),
|
'timestamp' => time(),
|
||||||
'version' => '1.0',
|
'version' => '1.1',
|
||||||
];
|
];
|
||||||
|
|
||||||
file_put_contents($cacheFile, json_encode($data, JSON_PRETTY_PRINT));
|
file_put_contents($cacheFile, json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
// 清理旧缓存文件
|
|
||||||
$this->cleanupOldCacheFiles(dirname($cacheFile));
|
$this->cleanupOldCacheFiles(dirname($cacheFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理旧的缓存文件
|
|
||||||
*/
|
|
||||||
private function cleanupOldCacheFiles(string $cacheDir): void
|
private function cleanupOldCacheFiles(string $cacheDir): void
|
||||||
{
|
{
|
||||||
$files = glob($cacheDir . '/scanner_*.json');
|
$files = glob($cacheDir . '/scanner_*.json');
|
||||||
$now = time();
|
$now = time();
|
||||||
|
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
if (filemtime($file) + (7 * 24 * 3600) < $now) { // 清理7天前的缓存
|
if (filemtime($file) + (7 * 24 * 3600) < $now) {
|
||||||
unlink($file);
|
unlink($file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理缓存的文件(批量处理,避免重复加载)
|
|
||||||
*/
|
|
||||||
private function processCachedFiles(): void
|
private function processCachedFiles(): void
|
||||||
{
|
{
|
||||||
foreach ($this->files as $file) {
|
foreach ($this->files as $file) {
|
||||||
@@ -504,17 +476,73 @@ class Scanner extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误记录方法
|
|
||||||
*/
|
|
||||||
private function logError(Throwable $e, array $context = []): void
|
private function logError(Throwable $e, array $context = []): void
|
||||||
{
|
{
|
||||||
json_log($e, $context);
|
$fullContext = array_merge($context, [
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
'code' => $e->getCode(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
'time' => date('Y-m-d H:i:s'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (function_exists('error')) {
|
||||||
|
error($e, $fullContext);
|
||||||
|
} else {
|
||||||
|
error_log(json_encode($fullContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->config['debug'] && php_sapi_name() === 'cli') {
|
||||||
|
echo "Scanner Error: {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}\n";
|
||||||
|
if (!empty($context)) {
|
||||||
|
echo 'Context: ' . json_encode($context) . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置扫描器状态
|
* 原代码的load_directory方法(保持兼容性)
|
||||||
*/
|
*/
|
||||||
|
public function load_directory(string $path): void
|
||||||
|
{
|
||||||
|
$this->scan($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原代码的parseFile方法(保持兼容性)
|
||||||
|
*/
|
||||||
|
protected function parseFile($file): void
|
||||||
|
{
|
||||||
|
// 这里$file是相对路径,需要转换为绝对路径
|
||||||
|
$absolutePath = $this->basePath . ltrim($file, '/') . '.php';
|
||||||
|
|
||||||
|
if (file_exists($absolutePath)) {
|
||||||
|
$this->processFile($absolutePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原代码的skipNames方法(保持兼容性)
|
||||||
|
*/
|
||||||
|
protected function skipNames(ReflectionClass $reflect): array
|
||||||
|
{
|
||||||
|
return array_map(
|
||||||
|
fn($attr) => $attr->getName(),
|
||||||
|
$reflect->getAttributes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStats(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'total_files' => count($this->files),
|
||||||
|
'cached_mtimes' => count($this->fileMtimes),
|
||||||
|
'base_path' => $this->basePath,
|
||||||
|
'config' => $this->config,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function reset(): void
|
public function reset(): void
|
||||||
{
|
{
|
||||||
$this->files = [];
|
$this->files = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user