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

從零構建輕量級 PHP MVC 框架核心組件

     編輯:PHP 相關知識

設計目標

  • 理解框架核心運作原理:路由分發、控制器基類、模型基類、自動加載
  • 代碼極簡,便於學習與二次擴展
  • 採用 Composer PSR-4 自動加載,符合現代標準

目錄結構

myframe/
├── app/
│   ├── controllers/
│   ├── models/
│   └── views/
├── core/
│   ├── Application.php
│   ├── Controller.php
│   ├── Model.php
│   └── Router.php
├── public/
│   └── index.php          # 唯一入口
├── vendor/
├── composer.json
└── .htaccess              # Apache 重寫

核心類實現

1. 自動加載

// composer.json
{
    "autoload": {
        "psr-4": {
            "Core\\": "core/",
            "App\\": "app/"
        }
    },
    "require": {
        "php": ">=8.0"
    }
}

執行 composer install 生成 vendor/autoload.php。

2. 路由器

// core/Router.php
namespace Core;

class Router {
    protected array $routes = [];

    public function get(string $path, callable|array $action): void {
        $this->routes['GET'][$path] = $action;
    }
    public function post(string $path, callable|array $action): void {
        $this->routes['POST'][$path] = $action;
    }

    public function resolve(string $method, string $uri): ?array {
        $uri = parse_url($uri, PHP_URL_PATH);
        $routes = $this->routes[$method] ?? [];
        
        foreach ($routes as $pattern => $action) {
            // 簡單參數匹配:/user/{id:\d+}
            $regex = preg_replace('/\{(\w+)(?::([^}]+))?\}/', '(?P<$1>$2)', $pattern);
            $regex = '@^' . $regex . '$@';
            if (preg_match($regex, $uri, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                return ['action' => $action, 'params' => $params];
            }
        }
        return null;
    }
}

3. 應用程序核心

// core/Application.php
namespace Core;

class Application {
    public Router $router;
    public string $basePath;
    public Controller $controller;

    public function __construct(string $basePath) {
        $this->basePath = $basePath;
        $this->router = new Router();
        require $basePath . '/config/routes.php'; // 載入路由定義
    }

    public function run(): void {
        $method = $_SERVER['REQUEST_METHOD'];
        $uri = $_SERVER['REQUEST_URI'];
        $route = $this->router->resolve($method, $uri);

        if ($route) {
            [$class, $method] = $route['action'];
            $this->controller = new $class($this);
            call_user_func_array([$this->controller, $method], $route['params']);
        } else {
            http_response_code(404);
            echo "404 Not Found";
        }
    }

    public function view(string $view, array $data = []): void {
        extract($data);
        include $this->basePath . "/app/views/{$view}.php";
    }
}

4. 控制器基類

// core/Controller.php
namespace Core;

abstract class Controller {
    protected Application $app;

    public function __construct(Application $app) {
        $this->app = $app;
    }

    protected function render(string $view, array $data = []): void {
        $this->app->view($view, $data);
    }
    protected function json(mixed $data, int $status = 200): void {
        http_response_code($status);
        header('Content-Type: application/json');
        echo json_encode($data);
    }
    protected function redirect(string $url, int $status = 302): void {
        header("Location: $url", true, $status);
        exit;
    }
}

5. 模型基類(PDO 封裝)

// core/Model.php
namespace Core;

use PDO;

abstract class Model {
    protected PDO $db;

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

    protected function query(string $sql, array $params = []): PDOStatement {
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        return $stmt;
    }
    protected function findOne(string $sql, array $params = []): ?array {
        return $this->query($sql, $params)->fetch(PDO::FETCH_ASSOC) ?: null;
    }
    protected function findAll(string $sql, array $params = []): array {
        return $this->query($sql, $params)->fetchAll(PDO::FETCH_ASSOC);
    }
    protected function execute(string $sql, array $params = []): int {
        return $this->query($sql, $params)->rowCount();
    }
    protected function lastInsertId(): string {
        return $this->db->lastInsertId();
    }
}

使用示例

配置路由

// config/routes.php
use Core\Router;
use App\Controllers\SiteController;
use App\Controllers\ApiController;

$router->get('/', [SiteController::class, 'home']);
$router->get('/post/{id:\d+}', [SiteController::class, 'show']);
$router->post('/comment', [SiteController::class, 'comment']);
$router->get('/api/users', [ApiController::class, 'users']);

控制器

// app/controllers/SiteController.php
namespace App\Controllers;

use Core\Controller;
use App\Models\PostModel;

class SiteController extends Controller {
    public function home(): void {
        $postModel = new PostModel($this->app->db);
        $posts = $postModel->findAll('SELECT * FROM posts ORDER BY created_at DESC LIMIT 10');
        $this->render('home', ['posts' => $posts]);
    }

    public function show(int $id): void {
        $postModel = new PostModel($this->app->db);
        $post = $postModel->findOne('SELECT * FROM posts WHERE id = ?', [$id]);
        if (!$post) { $this->app->redirect('/'); return; }
        $this->render('post/show', ['post' => $post]);
    }

    public function comment(int $postId): void {
        // 驗證、入庫...
        $this->json(['status' => 'ok']);
    }
}

模型

// app/models/PostModel.php
namespace App\Models;

use Core\Model;

class PostModel extends Model {
    // 可添加業務專用方法
    public function latest(int $limit = 10): array {
        return $this->findAll('SELECT * FROM posts ORDER BY created_at DESC LIMIT ?', [$limit]);
    }
}

視圖


<h1>最新文章</h1>
<ul>
<?php foreach ($posts as $p): ?>
    <li><a href="/post/<?= $p['id'] ?>"><?= htmlspecialchars($p['title']) ?></a></li>
<?php endforeach; ?>
</ul>

入口文件

// public/index.php
require __DIR__ . '/../vendor/autoload.php';

$app = new Core\Application(__DIR__ . '/..');

// 資料庫連接(可提取到配置文件)
$pdo = new PDO('mysql:host=127.0.0.1;dbname=myframe;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$app->db = $pdo; // 掛載到 Application,Controller/Model 可通過 $this->app->db 訪問

$app->run();

.htaccess

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

擴展方向

  • 中間件管道(認證、CSRF、限流)
  • 依賴注入容器(自動解析 Controller 構造函數)
  • 模板引擎整合
  • 異常處理與錯誤頁面
  • CLI 命令總線

掌握這套核心結構後,閱讀 Laravel、Symfony 源碼將不再陌生,所有框架本質上都是在這套基礎上疊加功能模組。

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