首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 實現 MVC 架構全解析:從核心原理到手寫輕量框架

     編輯:PHP 相關知識

MVC(Model-View-Controller)模式自 Smalltalk-80 發明以來,歷經 Struts、Spring MVC、Rails、Laravel 等框架演進,已成 Web 應用架構的事實標準。本文從 PHP 視角深度剖析 MVC 核心職責、為何選擇 MVC、並帶你從零手寫一個極簡 MVC 核心,理解框架底層運作機制。

MVC 三大角色在 PHP 中的具體映射

角色職責PHP 典型實現
Model(模型)業務邏輯、資料存取、規則驗證Eloquent/Doctrine 實體、Repository、Service 類、資料庫抽象層(PDO 封裝)
View(視圖)資料呈現、模板渲染、前端資產Blade/Twig/Smarty 模板、JSON/API 資源類、原生 PHP 模板(include)
Controller(控制器)請求接收、參數驗證、協調 Model/View、回應動作方法、表單請求驗證、中介軟體、資源控制器

為何在 PHP 中堅持 MVC?

  • 關注點分離:業務邏輯不再混雜在 HTML 中,單元測試覆蓋率大幅提升。
  • 團隊協作:後端專注 Model/Controller,前端專注 View/JS,介面契約(DTO/資源類)為界。
  • 可維護性:需求變更時,修改範圍局限於單一層,降低耦合風險。
  • 生態整合:主流套件(認證、驗證、快取、隊列)皆以 MVC 為前提設計。

爭議回應:有人質疑 HTTP 無狀態特性下 MVC「觀察者模式」失效。實則現代 PHP 框架透過「請求生命週期」模擬:每次請求重建 Controller,Model 狀態由資料庫/快取持久化,View 為一次性渲染,完全適配 Request/Response 模型。

極簡 MVC 核心實作(教學用)

以下代碼展示核心調度流程,省略路由解析、依賴注入、異常處理等生產級功能。

入口檔 public/index.php

<?php
require_once __DIR__ . '/../vendor/autoload.php';

use Core\Application;
use Core\Request;

$app = new Application();
$request = Request::capture();
$response = $app->handle($request);
$response->send();

核心應用類 Core/Application.php

namespace Core;

class Application {
    public function handle(Request $request): Response {
        // 1. 路由匹配(簡化為解析 PATH_INFO)
        $route = $this->resolveRoute($request->getPathInfo());
        if (!$route) {
            return new Response('Not Found', 404);
        }

        // 2. 實例化控制器並注入依賴
        $controller = new $route['controller'](
            new \App\Models\UserRepository(),  // 手動注入,生產用容器
            new \App\Views\TemplateEngine()
        );

        // 3. 呼叫動作方法
        $method = $route['action'] . 'Action';
        if (!method_exists($controller, $method)) {
            return new Response('Action Not Found', 404);
        }

        return $controller->$method($request, $route['params']);
    }

    private function resolveRoute(string $path): ?array {
        // 極簡路由表
        $routes = [
            '/'          => ['controller' => \App\Controllers\HomeController::class, 'action' => 'index'],
            '/user/{id}' => ['controller' => \App\Controllers\UserController::class, 'action' => 'show'],
        ];
        // 實際專案請使用 FastRoute、League\Route 等路由庫
        foreach ($routes as $pattern => $route) {
            $regex = '#^' . str_replace('{id}', '(\d+)', $pattern) . '$#';
            if (preg_match($regex, $path, $matches)) {
                array_shift($matches);
                return array_merge($route, ['params' => $matches]);
            }
        }
        return null;
    }
}

基礎控制器 App/Controllers/BaseController.php

namespace App\Controllers;

use Core\Request;
use Core\Response;
use App\Views\TemplateEngine;

abstract class BaseController {
    protected TemplateEngine $view;
    protected Request $request;

    public function __construct(TemplateEngine $view, Request $request) {
        $this->view = $view;
        $this->request = $request;
    }

    protected function render(string $template, array $data = []): Response {
        return new Response($this->view->render($template, $data));
    }

    protected function json(array $data, int $status = 200): Response {
        return (new Response(json_encode($data, JSON_UNESCAPED_UNICODE), $status))
            ->header('Content-Type', 'application/json');
    }
}

具體控制器 App/Controllers/UserController.php

namespace App\Controllers;

use App\Models\UserRepository;
use Core\Request;
use Core\Response;

class UserController extends BaseController {
    private UserRepository $users;

    public function __construct(UserRepository $users, TemplateEngine $view, Request $request) {
        parent::__construct($view, $request);
        $this->users = $users;
    }

    public function showAction(Request $request, array $params): Response {
        $id = (int)$params[0];
        $user = $this->users->find($id);
        if (!$user) {
            return $this->json(['error' => 'User not found'], 404);
        }
        // API 回應
        if ($request->wantsJson()) {
            return $this->json($user->toArray());
        }
        // 網頁回應
        return $this->render('user/profile', ['user' => $user]);
    }
}

模型 Repository App/Models/UserRepository.php

namespace App\Models;

use PDO;

class UserRepository {
    private PDO $db;

    public function __construct(PDO $db) {
        $this->db = $db;
    }

    public function find(int $id): ?User {
        $stmt = $this->db->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([$id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row ? new User($row) : null;
    }
}

視圖引擎 App/Views/TemplateEngine.php

namespace App\Views;

class TemplateEngine {
    public function render(string $template, array $data = []): string {
        extract($data);
        ob_start();
        include __DIR__ . "/../templates/{$template}.php";
        return ob_get_clean();
    }
}

從極簡到生產級:關鍵演進點

  1. 依賴注入容器:用 PHP-DI、Illuminate\Container 替代手動 new,支援自動配線。
  2. 路由系統:引入 FastRoute 或 League\Route,支援 RESTful、中介軟體、群組、命名路由。
  3. 請求/回應物件:實作 Psr\Http\Message\ServerRequestInterface / ResponseInterface,符合 PSR-7,便於中介軟體管線。
  4. 異常處理:全域異常捕獲,區分開發/生產環境輸出詳細/友好錯誤頁。
  5. ORM/資料庫抽象:整合 Eloquent、Doctrine 或 Cycle ORM,提供遷移、關聯、查詢構建器。
  6. 模板引擎:Blade(Laravel)、Twig(Symfony)提供繼承、區塊、沙箱、快取編譯。
  7. 測試支援:PHPUnit + 測試專用資料庫、Mockery 模擬依賴、HTTP Client 模擬請求。

總結

MVC 不是銀彈,但為 PHP 專案提供了經得起時間考驗的組織骨架。理解其核心調度流程(路由 → 控制器 → 模型 → 視圖 → 回應),才能在使用 Laravel、Symfony、ThinkPHP 等成熟框架時,不再被「魔法」困惑,而是能精準診斷效能瓶頸、擴充核心功能、做出架構決策。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved