路由系統負責將 URL 映射至具體業務模塊、控制器與動作方法。本框架支援三種路由模式,滿足不同場景需求。
| 模式 | URL 範例 | 參數解析 | 適用場景 |
|---|---|---|---|
| 普通模式 | ?_a=web&_u=index.index | _a=模塊, _u=控制器.動作 | 後台管理、API、快速開發 |
| Easy 模式 | ?_easy=web.default.index.index | 模塊.模板.控制器.動作 | 靜態展示頁、無需邏輯直接渲染模板 |
| Rewrite 模式 | /rewrite/web/index/index.html/rewrite/web/index/index/uid/1.html | 偽靜態路徑,支援目錄式與參數編碼 | SEO、微信回調、支付通知、靜態資源偽裝 |
$a = (!empty($_REQUEST['_a']) && is_string($_REQUEST['_a'])) ? $_REQUEST['_a'] : 'web';
if (!preg_match('/^[\\w\\.]+$/', $a)) exit('invalid _app name! ' . htmlspecialchars($a));
$GLOBALS['_UCT']['APP'] = strtolower($a);
$u = (!empty($_REQUEST['_u']) && is_string($_REQUEST['_u'])) ? $_REQUEST['_u'] : 'index.index';
if (!preg_match('/^[\\w\\.]+$/', $u)) exit('invalid _url name! ' . htmlspecialchars($u));
$u = explode('.', $u, 2);
$GLOBALS['_UCT']['CTL'] = !empty($u[0]) ? strtolower($u[0]) : 'index';
$GLOBALS['_UCT']['ACT'] = !empty($u[1]) ? strtolower($u[1]) : 'index';
if (!empty($_REQUEST['_easy']) && is_string($_REQUEST['_easy'])) {
$easy = explode('.', $_REQUEST['_easy']);
switch (count($easy)) {
case 4: // app.tpl.ctl.act
$_GET['_u'] = $_REQUEST['_u'] = $easy[2] . '.' . $easy[3];
if (preg_match('/^[\\w\\.]+$/', $easy[1])) $GLOBALS['_UCT']['TPL'] = $easy[1];
$_GET['_a'] = $_REQUEST['_a'] = $easy[0];
break;
case 3: // app.ctl.act
$_GET['_u'] = $_REQUEST['_u'] = $easy[1] . '.' . $easy[2];
$_GET['_a'] = $_REQUEST['_a'] = $easy[0];
break;
case 2: // app.ctl (act 默認 index)
$_GET['_u'] = $_REQUEST['_u'] = $easy[1];
$_GET['_a'] = $_REQUEST['_a'] = $easy[0];
break;
case 1: // 僅 app
$_GET['_a'] = $_REQUEST['_a'] = $easy[0];
break;
default:
exit('invalid _easy param! ' . htmlspecialchars($_REQUEST['_easy']));
}
}
需 Nginx/Apache 重寫規則:
# Apache
RewriteRule ^rewrite[./](.*)$ /index.php?_rewrite=$1 [R,QSA]
# Nginx
rewrite ^/rewrite[./](.*)$ /index.php?_rewrite=$1 last;
PHP 解析:
if (!empty($_REQUEST['_rewrite']) && is_string($_REQUEST['_rewrite'])) {
// Apache 環境下 QUERY_STRING 可能丟失參數,手動從 SERVER 取回
if (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') === false) {
$_REQUEST['_rewrite'] = urldecode(substr($_SERVER['QUERY_STRING'], strlen('_rewrite=')));
}
// 去除副檔名
$rewrite = substr($_REQUEST['_rewrite'], 0, strrpos($_REQUEST['_rewrite'], '.'));
// 自動檢測分隔符 . 或 /
$sp = '.';
for ($i = 0; $i < strlen($rewrite); $i++) {
if (in_array($rewrite[$i], ['.', '/'])) { $sp = $rewrite[$i]; break; }
}
$rewrite = explode($sp, $rewrite, 4);
switch (count($rewrite)) {
case 3: case 4: // app.ctl.act[.params]
$_GET['_a'] = $_REQUEST['_a'] = $rewrite[0];
$_GET['_u'] = $_REQUEST['_u'] = $rewrite[1] . '.' . $rewrite[2];
if (!empty($rewrite[3])) {
// 解析參數:支援 key/val/... 或 key=val&...
if (strpos($rewrite[3], '/') !== false) {
$params = explode('/', $rewrite[3]);
for ($i = 0; $i + 1 < count($params); $i += 2) {
$_REQUEST[urldecode($params[$i])] = urldecode($params[$i+1]);
}
} else {
foreach (explode('&', $rewrite[3]) as $p) {
list($k, $v) = explode('=', $p, 2);
$_REQUEST[urldecode($k)] = urldecode($v);
}
}
}
break;
case 2: // app.ctl
$_GET['_a'] = $_REQUEST['_a'] = $rewrite[0];
$_GET['_u'] = $_REQUEST['_u'] = $rewrite[1];
break;
case 1: // 僅 app
$_GET['_a'] = $_REQUEST['_a'] = $rewrite[0];
break;
}
}
rewrite.thirdlogin.index.qqcallback.sp_uid%3D1.php 避免 ? & 干擾驗證。rewrite.upload.index.out.uidm%3D310ef4b.png 實際由 PHP 輸出圖片。rewrite.pay.weixin.native2_notify.php 隱藏真實路由。rewrite/web/component/message/_app_id/xxxxxxx.php 目錄式參數。index.php,全局 _UCT 註冊表傳遞路由上下文。