面對 Laravel、Symfony、ThinkPHP 等成熟框架,**重複造輪子毫無意義**。但理解 MVC 核心契約、依賴注入、約定優於配置 (CoC) 等設計原理,能讓你更深層次地駕馭現有框架、進行架構決策、甚至在微服務/Serverless 場景下構建極簡應用層。本文提出一套**教學/參考級**的輕量級 MVC 標準規範。
<?php
// 無需實現任何介面,僅遵循以下約定
class ArticleController
{
// 1. 請求參數自動注入 (setter 注入,支援層級屬性 book.title)
public function setTitle(string $title) { $this->title = $title; }
public function setBook(Book $book) { $this->book = $book; } // 嵌套對象自動構建
// 2. 視圖數據輸出 (getter 供 View 讀取 ${property})
public function getTitle(): string { return $this->title; }
public function getArticles(): array { return $this->articles; }
// 3. Session 參數同樣走 setter 注入 (loginUserId 等),請求參數同名會被 Session 覆蓋
public function setLoginUserId(int $id) { $this->loginUserId = $id; }
// 4. 動作方法:任意非 setter/getter 的 public 方法
public function listAction(): ?string {
$this->articles = ArticleModel::all();
return null; // null 表示渲染默認 View (list)
}
public function saveAction(): string {
// 處理邏輯...
return 'list'; // 返回字串 -> 跳轉至另一 Controller (listAction)
// 不允許一個 Controller 對應多個 View
}
// 5. Model 依賴同樣走 setter 自動裝配
public function setArticleModel(ArticleModel $model) { $this->articleModel = $model; }
}
?>
https://host[:port][/app]/module/controller/action.html[?params] // 例:/admin/article/list.html?page=2&status=published // 映射:AdminModule -> ArticleController -> listAction // View:view/admin/article/list.php
副檔名 `.html` 偽靜態,路由層解析去除。
| 類型 | 命名空間/目錄約定 | 實例化策略 |
|---|---|---|
| Model | `App\Module\*Model` 或 `app/module/*/model/` | 單例 |
| Controller | `App\Module\*Controller` 或 `app/module/*/controller/` | 原型 (每請求新建) |
| View | `views/module/controller/action.ext` | 按需渲染 |
框架啟動時掃描符合約定的類,建立映射表,請求到來時實例化 Controller、注入依賴、執行動作、渲染 View。
<?php
// 入口文件 public/index.php
require __DIR__ . '/../vendor/autoload.php';
$app = new MicroMvc\App([
'module_paths' => [__DIR__ . '/../app'],
'view_paths' => [__DIR__ . '/../views'],
'template_engine' => 'php', // 或 twig, blade
]);
$app->run();
// 核心類 MicroMvc\App (極簡版)
namespace MicroMvc;
class App {
public function run() {
$route = $this->parseRoute($_SERVER['REQUEST_URI']); // 解析 module/controller/action
$controller = $this->instantiateController($route);
$this->injectDependencies($controller); // setter 注入請求參數、Session、Model
$viewName = $controller->{$route['action'] . 'Action'}(); // 執行動作
$this->renderView($controller, $viewName ?? $route['action']);
}
private function injectDependencies($controller) {
$ref = new ReflectionClass($controller);
foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
$name = $method->getName();
if (str_starts_with($name, 'set')) {
$paramName = lcfirst(substr($name, 3)); // setTitle -> title
$value = $this->resolveValue($paramName, $method->getParameters()[0] ?? null);
if ($value !== null) $method->invoke($controller, $value);
}
}
}
// ... resolveValue 從 $_GET/$_POST/$_SESSION/容器 解析
// ... renderView 定位模板、extract(getters)、include
}
?>
「理想很豐滿,現實很骨感。」生產環境**強烈建議直接使用成熟框架** (Laravel/Symfony/Webman/Hyperf)。本規範的價值在於:
- 面試/架構評審時展現「懂框架本質」
- 微服務 Sidecar、Serverless Function、CLI 工具等**極簡場景**自建輕量層
- 老專案重構時,作為「目標架構藍圖」指導分層解耦
理解標準,不是為了造輪子,而是為了**不被輪子綁架**。