mirror of
https://devops.lemonos.cn/lawson/FendxPHP.git
synced 2026-06-15 23:12:49 +08:00
153 lines
4.4 KiB
PHP
153 lines
4.4 KiB
PHP
|
|
<?php
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Fendx\Web\Scanner;
|
||
|
|
|
||
|
|
use ReflectionClass;
|
||
|
|
use ReflectionMethod;
|
||
|
|
use ReflectionException;
|
||
|
|
use Fendx\Core\Annotation\Controller;
|
||
|
|
use Fendx\Web\Annotation\GetRoute;
|
||
|
|
use Fendx\Web\Annotation\PostRoute;
|
||
|
|
use Fendx\Web\Route\Router;
|
||
|
|
|
||
|
|
final class RouteScanner
|
||
|
|
{
|
||
|
|
private Router $router;
|
||
|
|
private array $controllers = [];
|
||
|
|
|
||
|
|
public function __construct(Router $router)
|
||
|
|
{
|
||
|
|
$this->router = $router;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function scan(string $scanPath): void
|
||
|
|
{
|
||
|
|
$this->scanControllers($scanPath);
|
||
|
|
$this->registerRoutes();
|
||
|
|
}
|
||
|
|
|
||
|
|
private function scanControllers(string $path): void
|
||
|
|
{
|
||
|
|
if (!is_dir($path)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$files = glob($path . '/**/*.php');
|
||
|
|
|
||
|
|
foreach ($files as $file) {
|
||
|
|
$this->scanControllerFile($file);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function scanControllerFile(string $file): void
|
||
|
|
{
|
||
|
|
$className = $this->getClassNameFromFile($file);
|
||
|
|
|
||
|
|
if ($className === null || class_exists($className) === false) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
$reflection = new ReflectionClass($className);
|
||
|
|
|
||
|
|
// 检查是否有Controller注解
|
||
|
|
$controllerAttributes = $reflection->getAttributes(Controller::class);
|
||
|
|
if (empty($controllerAttributes)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$controllerAttribute = $controllerAttributes[0]->newInstance();
|
||
|
|
$prefix = $controllerAttribute->prefix;
|
||
|
|
|
||
|
|
// 扫描方法上的路由注解
|
||
|
|
$this->scanControllerMethods($className, $reflection, $prefix);
|
||
|
|
|
||
|
|
} catch (ReflectionException $e) {
|
||
|
|
error_log("Failed to scan controller $className: " . $e->getMessage());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function scanControllerMethods(string $className, ReflectionClass $reflection, string $prefix): void
|
||
|
|
{
|
||
|
|
$methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
|
||
|
|
|
||
|
|
foreach ($methods as $method) {
|
||
|
|
if ($method->getName() === '__construct') {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->scanMethodRoutes($className, $method, $prefix);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function scanMethodRoutes(string $className, ReflectionMethod $method, string $prefix): void
|
||
|
|
{
|
||
|
|
// 扫描GetRoute注解
|
||
|
|
$getRouteAttributes = $method->getAttributes(GetRoute::class);
|
||
|
|
foreach ($getRouteAttributes as $attribute) {
|
||
|
|
$routeAttribute = $attribute->newInstance();
|
||
|
|
$path = $prefix . $routeAttribute->path;
|
||
|
|
$this->controllers[] = [
|
||
|
|
'method' => 'GET',
|
||
|
|
'path' => $path,
|
||
|
|
'controller' => $className,
|
||
|
|
'action' => $method->getName()
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
// 扫描PostRoute注解
|
||
|
|
$postRouteAttributes = $method->getAttributes(PostRoute::class);
|
||
|
|
foreach ($postRouteAttributes as $attribute) {
|
||
|
|
$routeAttribute = $attribute->newInstance();
|
||
|
|
$path = $prefix . $routeAttribute->path;
|
||
|
|
$this->controllers[] = [
|
||
|
|
'method' => 'POST',
|
||
|
|
'path' => $path,
|
||
|
|
'controller' => $className,
|
||
|
|
'action' => $method->getName()
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function registerRoutes(): void
|
||
|
|
{
|
||
|
|
foreach ($this->controllers as $route) {
|
||
|
|
$handler = [$route['controller'], $route['action']];
|
||
|
|
|
||
|
|
switch ($route['method']) {
|
||
|
|
case 'GET':
|
||
|
|
$this->router->get($route['path'], $handler);
|
||
|
|
break;
|
||
|
|
case 'POST':
|
||
|
|
$this->router->post($route['path'], $handler);
|
||
|
|
break;
|
||
|
|
case 'PUT':
|
||
|
|
$this->router->put($route['path'], $handler);
|
||
|
|
break;
|
||
|
|
case 'DELETE':
|
||
|
|
$this->router->delete($route['path'], $handler);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function getClassNameFromFile(string $file): ?string
|
||
|
|
{
|
||
|
|
$content = file_get_contents($file);
|
||
|
|
|
||
|
|
if (preg_match('/namespace\s+([^;]+);/', $content, $matches)) {
|
||
|
|
$namespace = trim($matches[1]);
|
||
|
|
$className = basename($file, '.php');
|
||
|
|
return $namespace . '\\' . $className;
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getRoutes(): array
|
||
|
|
{
|
||
|
|
return $this->controllers;
|
||
|
|
}
|
||
|
|
}
|