| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- <?php
- declare(strict_types=1);
- /**
- * Glacier 单一入口(前端控制器)。
- * 所有动态请求都经此进入 —— URL 与物理路径完全解耦,见 docs/architecture.md §3.1。
- */
- define('APP_ROOT', dirname(__DIR__));
- require APP_ROOT . '/vendor/autoload.php';
- use Glacier\Core\Config;
- use Glacier\Core\Logger;
- use Glacier\Core\Middleware;
- use Glacier\Core\ModuleLoader;
- use Glacier\Core\Response;
- use Glacier\Core\Router;
- use Glacier\Core\Session;
- use Glacier\Middleware\AuthMiddleware;
- use Glacier\Middleware\CsrfMiddleware;
- use Glacier\Middleware\PermissionMiddleware;
- date_default_timezone_set('Asia/Shanghai'); // 架构约定 UTC+8
- $config = require APP_ROOT . '/app/config/env.php';
- Config::load($config);
- Logger::init(Config::get('storage.logs', APP_ROOT . '/storage/logs'));
- // 会话启动(安全 Cookie 参数见 Session 封装)
- Session::start();
- // 注册中间件(命名中间件,路由表按名挂载)
- Middleware::register('csrf', [new CsrfMiddleware(), 'handle']);
- Middleware::register('auth', [new AuthMiddleware(), 'handle']);
- Middleware::register('permission', [new PermissionMiddleware(), 'handle']);
- try {
- // 解析请求路径(去 query,规范化尾部斜杠)
- $path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
- if (strlen($path) > 1) {
- $path = rtrim($path, '/');
- }
- $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
- // 开发辅助:静态资源(/assets/*)直接服务。
- // 生产环境由 Web 服务器完成(Caddy/ Apache 的"真实文件直接返回"语义)。
- // 仅限 public/assets 目录内,realpath 校验防路径穿越;URL 不映射业务物理路径。
- if (str_starts_with($path, '/assets/')) {
- $assetFile = APP_ROOT . '/public' . $path;
- $real = realpath($assetFile);
- $assetRoot = realpath(APP_ROOT . '/public/assets');
- if ($real !== false && $assetRoot !== false && str_starts_with($real, $assetRoot)) {
- // 常见类型映射(Windows fileinfo 对 css 等识别不稳定)
- $ext = strtolower(pathinfo($real, PATHINFO_EXTENSION));
- $mimeMap = [
- 'css' => 'text/css',
- 'js' => 'application/javascript',
- 'json' => 'application/json',
- 'png' => 'image/png',
- 'jpg' => 'image/jpeg',
- 'jpeg' => 'image/jpeg',
- 'gif' => 'image/gif',
- 'svg' => 'image/svg+xml',
- 'webp' => 'image/webp',
- 'ico' => 'image/x-icon',
- 'woff' => 'font/woff',
- 'woff2' => 'font/woff2',
- ];
- $mime = $mimeMap[$ext] ?? (function_exists('mime_content_type') ? mime_content_type($real) : false) ?: 'application/octet-stream';
- header('Content-Type: ' . $mime);
- readfile($real);
- exit;
- }
- }
- Logger::info('request', ['method' => $method, 'path' => $path]);
- // 注册路由:先根路由表,再挂载业务模块(子空间 /<name>)
- $router = new Router();
- foreach ((array) require APP_ROOT . '/app/config/routes.php' as $route) {
- $router->add($route['method'], $route['pattern'], $route['handler'], $route['middleware'] ?? []);
- }
- (new ModuleLoader())->load($router);
- $match = $router->match($path, $method);
- if ($match === null) {
- Logger::warning('route not found', ['method' => $method, 'path' => $path]);
- Response::html(
- '<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>404</title>'
- . '<style>body{font-family:system-ui;text-align:center;padding-top:80px;color:#666}'
- . 'h1{font-size:48px;color:#c33;margin-bottom:8px}</style></head>'
- . '<body><h1>404</h1><p>页面不存在。</p></body></html>',
- 404
- );
- }
- // 执行路由中间件(拒绝时中间件自行终止响应)
- Middleware::run($match['middleware'], $match['params']);
- call_user_func($match['handler'], $match['params']);
- } catch (\Throwable $e) {
- Logger::error('uncaught exception', [
- 'message' => $e->getMessage(),
- 'file' => $e->getFile(),
- 'line' => $e->getLine(),
- ]);
- $detail = Config::get('app.debug', false)
- ? '<pre>' . e($e->getMessage()) . PHP_EOL . e($e->getFile() . ':' . $e->getLine()) . '</pre>'
- : '<p>服务器开小差了,请稍后再试。</p>';
- Response::html(
- '<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>500</title>'
- . '<style>body{font-family:system-ui;text-align:center;padding-top:80px;color:#666}'
- . 'h1{font-size:48px;color:#c33}</style></head>'
- . '<body><h1>500</h1>' . $detail . '</body></html>',
- 500
- );
- }
|