true, 'cookie_samesite' => 'Lax', 'cookie_secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'), // HTTPS 下仅安全传输 ]); } // 2. 自动加载(Core / App\Controllers / App\Models) spl_autoload_register(function ($class) { $prefixes = [ 'Core\\' => BASE_PATH . '/app/Core/', 'App\\Controllers\\' => BASE_PATH . '/app/Controllers/', 'App\\Models\\' => BASE_PATH . '/app/Models/', ]; foreach ($prefixes as $prefix => $base) { if (strncmp($class, $prefix, strlen($prefix)) === 0) { $rel = substr($class, strlen($prefix)); $file = $base . str_replace('\\', '/', $rel) . '.php'; if (is_file($file)) { require $file; return true; } } } return false; }); } public static function run() { self::init(); self::dispatch(self::parseRoute()); } /** 解析请求路径为段数组 */ public static function parseRoute(): array { $uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/'; // 仅当 SCRIPT_NAME 真实以 index.php 结尾时(Apache/生产模式)才剥离目录前缀; // 内置服务器路由模式下 SCRIPT_NAME 等于请求路径,此时不剥离,避免误删 admin 等段。 $script = $_SERVER['SCRIPT_NAME'] ?? '/index.php'; if (substr($script, -strlen('/index.php')) === '/index.php') { $dir = dirname($script); if ($dir !== '/' && strpos($uri, $dir) === 0) { $uri = substr($uri, strlen($dir)); } } $uri = preg_replace('#/index\.php$#i', '', $uri); $uri = trim($uri, '/'); if ($uri === '') return []; return explode('/', $uri); } /** 路由分发 */ public static function dispatch(array $segments) { // 静态资源直出 $asset = BASE_PATH . '/public/' . implode('/', $segments); if ($segments && is_file($asset) && !is_dir($asset)) { self::serveFile($asset); return; } $admin = self::config('app.admin_path', 'admin'); if (!empty($segments) && $segments[0] === $admin) { self::dispatchAdmin(array_slice($segments, 1)); return; } // 分系统入口:CRM 客户管理 / PSI 进销存(统一受 super_admin 与分系统权限管辖) // 大小写不敏感:/crm、/CRM、/psi、/PSI 均可进入,避免因 URL 大小写不同导致 404 if (!empty($segments)) { $seg0 = strtoupper($segments[0]); if ($seg0 === 'CRM' || $seg0 === 'PSI') { self::dispatchSubsys($seg0, array_slice($segments, 1)); return; } } self::dispatchFront($segments); } private static function dispatchSubsys(string $raw, array $s) { $sys = strtoupper($raw); // CRM / PSI $sysKey = $sys === 'PSI' ? 'psi' : 'crm'; // 未登录或无该系统角色:拦截(拥有 crm/psi 任意角色即可进入;写操作由各 Controller 的 subsys_admin 二次把关) if (!subsys_can_enter($sysKey)) { self::forbidden('您没有访问「' . $sys . '」系统的权限'); return; } // 统一入口:CRM\DashboardController / PSI\DashboardController 内部再做子路由 $ctrl = 'App\\Controllers\\' . $sys . '\\DashboardController'; $action = 'dispatch'; if (!class_exists($ctrl)) { self::notFound("系统不存在: $sys"); return; } $instance = new $ctrl(); if (!method_exists($instance, $action)) { self::notFound("入口不存在: $action"); return; } try { $html = call_user_func_array([$instance, $action], [$s]); } catch (\Throwable $e) { // 子系统异常兜底:保留左导 + 右框,框内显示错误,绝不白屏 error_log('Subsys[' . $sys . '] error: ' . $e->getMessage()); $html = self::subsysErrorFrame($sysKey, $instance, $e); } if (is_string($html)) echo $html; } private static function dispatchFront(array $s) { $key = $s[0] ?? ''; $seg2 = $s[1] ?? null; // 验证码图片输出(独立端点,直接输出 PNG) if ($key === 'captcha' && $seg2 === 'image') { captcha_image(); return; } // Sitemap 动态生成(XML 格式,搜索引擎自动抓取) if ($key === 'sitemap.xml') { self::outputSitemap(); return; } // 资源详情路由(带第二段 slug) if (($key === 'product' || $key === 'products') && $seg2) { self::call('App\\Controllers\\ProductController', 'show', [$seg2]); return; } if ($key === 'news' && $seg2) { self::call('App\\Controllers\\NewsController', 'show', [$seg2]); return; } if ($key === 'cases' && $seg2) { self::call('App\\Controllers\\CaseController', 'show', [$seg2]); return; } // 订单 / 支付(含网关异步通知,无需登录) if ($key === 'order') { $action = $s[1] ?? 'checkout'; $param = $s[2] ?? null; self::call('App\\Controllers\\OrderController', $action, [$param]); return; } if ($key === 'pay') { $action = $s[1] ?? 'notify'; $param = $s[2] ?? null; self::call('App\\Controllers\\PayController', $action, [$param]); return; } $map = [ '' => ['HomeController', 'index'], 'home' => ['HomeController', 'index'], 'products' => ['ProductController', 'index'], 'news' => ['NewsController', 'index'], 'cases' => ['CaseController', 'index'], 'page' => ['PageController', 'show'], 'contact' => ['ContactController', 'index'], ]; if (isset($map[$key])) { [$ctrl, $action] = $map[$key]; $param = $seg2; self::call('App\\Controllers\\' . $ctrl, $action, [$param]); return; } // 未知路径:按单页 slug 处理(/about、/service ...) self::call('App\\Controllers\\PageController', 'show', [$key]); } private static function dispatchAdmin(array $s) { $res = $s[0] ?? ''; $action = $s[1] ?? 'index'; $id = $s[2] ?? null; $map = [ '' => ['Admin\\DashboardController', 'index'], 'dashboard' => ['Admin\\DashboardController', 'index'], 'login' => ['Admin\\AuthController', 'login'], 'logout' => ['Admin\\AuthController', 'logout'], 'password' => ['Admin\\AuthController', 'password'], 'products' => ['Admin\\ProductController', 'index'], 'categories'=> ['Admin\\CategoryController', 'index'], 'news' => ['Admin\\NewsController', 'index'], 'pages' => ['Admin\\PageController', 'index'], 'banners' => ['Admin\\BannerController', 'index'], 'settings' => ['Admin\\SettingController', 'index'], 'theme' => ['Admin\\SettingController', 'theme'], 'seo' => ['Admin\\SettingController', 'seo'], 'users' => ['Admin\\UserController', 'index'], 'system' => ['Admin\\SystemController', 'index'], 'orders' => ['Admin\\OrderController', 'index'], 'payments' => ['Admin\\SettingController', 'payment'], 'cases' => ['Admin\\CaseController', 'index'], 'media' => ['Admin\\MediaController', 'index'], 'upgrade' => ['Admin\\UpgradeController', 'index'], 'db' => ['Admin\\DatabaseController', 'index'], ]; if (!isset($map[$res])) { self::notFound(); return; } // 权限能力检查:未登录或权限不足直接拦截(登录态由对应控制器再兜底) $capMap = [ 'users' => 'users', 'system' => 'users', 'upgrade' => 'users', 'db' => 'users', 'settings' => 'settings', 'theme' => 'settings', 'seo' => 'settings', 'payments' => 'settings', 'products' => 'products', 'categories'=> 'categories', 'news' => 'news', 'pages' => 'pages', 'banners' => 'banners', 'orders' => 'orders', 'cases' => 'cases', 'media' => 'pages', ]; if (isset($capMap[$res]) && !admin_can($capMap[$res])) { self::forbidden('当前账号无访问「' . $res . '」的权限'); return; } [$ctrl, $default] = $map[$res]; $method = ($action === 'index') ? $default : $action; self::call('App\\Controllers\\' . $ctrl, $method, [$id]); } private static function call($class, $method, array $args = []) { if (!class_exists($class)) { self::notFound("类不存在: $class"); return; } $instance = new $class(); if (!method_exists($instance, $method)) { self::notFound("方法不存在: $method"); return; } $html = call_user_func_array([$instance, $method], $args); if (is_string($html)) echo $html; } public static function notFound($msg = '') { http_response_code(404); echo '
系统遇到问题,错误已记录,请稍后重试或联系管理员