PHP: 实战:面向对象综合

学了 5 课面向对象,知识都在脑子里。这节课把它们焊在一起——两个项目:一个完整覆盖继承/枚举/Trait/接口的用户系统,一个让你初窥 MVC 架构的简易框架。

1. 项目一:用户角色管理系统

综合运用类、继承、枚举、Trait、接口,搭建一个完整的用户角色管理框架。

▶ 示例:完整 OOP 用户系统

PHP 📖 仅展示
<?php
// === 枚举 ===
enum UserRole: string {
    case ADMIN  = 'admin';
    case EDITOR = 'editor';
    case MEMBER = 'member';
    case GUEST  = 'guest';
}

enum UserStatus: string {
    case ACTIVE   = 'active';
    case INACTIVE = 'inactive';
    case BANNED   = 'banned';
}

// === 接口 ===
interface Loggable {
    public function getLogIdentifier(): string;
}

interface Manageable {
    public function canAccess(string $resource): bool;
    public function getPermissions(): array;
}

// === Trait ===
trait HasTimestamps {
    public readonly string $createdAt;
    public readonly string $updatedAt;

    public function touch(): void {
        $now = date("Y-m-d H:i:s");
        if (!isset($this->createdAt)) {
            $this->createdAt = $now;
        }
        $this->updatedAt = $now;
    }
}

trait ArraySerializable {
    public function toArray(): array {
        return get_object_vars($this);
    }
}

// === 基类 ===
abstract class User implements Loggable, Manageable {
    use HasTimestamps, ArraySerializable;

    public function __construct(
        public readonly string $id,
        public string $name,
        public string $email,
        public UserRole $role = UserRole::MEMBER,
        public UserStatus $status = UserStatus::ACTIVE,
    ) {
        $this->touch();
    }

    public function getLogIdentifier(): string {
        return "User#{$this->id} ({$this->name})";
    }

    abstract public function canAccess(string $resource): bool;
    abstract public function getPermissions(): array;
    abstract public function getRoleLabel(): string;
}

// === 具体角色类 ===
class Admin extends User {
    public function __construct(
        string $id, string $name, string $email
    ) {
        parent::__construct($id, $name, $email, UserRole::ADMIN);
    }

    public function canAccess(string $resource): bool {
        return true;  // 管理员能访问一切
    }

    public function getPermissions(): array {
        return ['create', 'read', 'update', 'delete', 'manage_users', 'system_config'];
    }

    public function getRoleLabel(): string {
        return '🔧 管理员';
    }
}

class Editor extends User {
    public function __construct(
        string $id, string $name, string $email
    ) {
        parent::__construct($id, $name, $email, UserRole::EDITOR);
    }

    public function canAccess(string $resource): bool {
        return in_array($resource, ['articles', 'media', 'comments']);
    }

    public function getPermissions(): array {
        return ['create', 'read', 'update', 'manage_comments'];
    }

    public function getRoleLabel(): string {
        return '✏️ 编辑';
    }
}

class Member extends User {
    public function __construct(
        string $id, string $name, string $email
    ) {
        parent::__construct($id, $name, $email, UserRole::MEMBER);
    }

    public function canAccess(string $resource): bool {
        return in_array($resource, ['articles', 'comments', 'profile']);
    }

    public function getPermissions(): array {
        return ['read', 'comment'];
    }

    public function getRoleLabel(): string {
        return '👤 会员';
    }
}

// === 权限管理器 ===
class AccessManager {
    public function checkAccess(User $user, string $resource): string {
        if ($user->status === UserStatus::BANNED) {
            return "{$user->getRoleLabel()} {$user->name}:已封禁,拒绝访问 {$resource}";
        }
        
        $allowed = $user->canAccess($resource)
            ? "✅ 允许访问" : "❌ 拒绝访问";
        
        return "{$user->getRoleLabel()} {$user->name}:{$allowed} {$resource}";
    }
    
    public function listAllAccess(array $users, array $resources): void {
        foreach ($users as $user) {
            echo "<h4>{$user->getLogIdentifier()}</h4>";
            echo "权限:" . implode(', ', $user->getPermissions()) . "<br>";
            foreach ($resources as $res) {
                echo $this->checkAccess($user, $res) . "<br>";
            }
        }
    }
}

// === 测试系统 ===
$users = [
    new Admin("u1", "小明", "admin@example.com"),
    new Editor("u2", "小红", "editor@example.com"),
    new Member("u3", "小刚", "member@example.com"),
];

$resources = ['articles', 'media', 'comments', 'system_config', 'manage_users'];

$manager = new AccessManager();
$manager->listAllAccess($users, $resources);

// 测试枚举 + 序列化
echo "<br>Admin 数据:" . json_encode($users[0]->toArray(), JSON_UNESCAPED_UNICODE);
?>
逻辑代码 130 行(超过 40 行限制,仅展示)

2. 项目二:简易 MVC 框架

理解路由、控制器、视图的 MVC 概念——这是 Laravel 等框架的核心思想。

▶ 示例:迷你 MVC

PHP 📖 仅展示
<?php
// === Router 路由类 ===
class Router {
    private array $routes = [];

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

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

    public function dispatch(string $method, string $uri): void {
        $path = parse_url($uri, PHP_URL_PATH);
        $handler = $this->routes[$method][$path] ?? null;

        if ($handler) {
            echo $handler($_GET);
        } else {
            http_response_code(404);
            echo "<h2>404 — 页面未找到</h2>";
        }
    }
}

// === View 视图基类 ===
class View {
    public static function render(string $template, array $data = []): string {
        extract($data);
        ob_start();
        include __DIR__ . "/{$template}.php";
        return ob_get_clean();
    }
}

// === Controller 控制器基类 ===
abstract class Controller {
    protected function json(array $data, int $code = 200): void {
        http_response_code($code);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data, JSON_UNESCAPED_UNICODE);
    }
}

// === 具体控制器 ===
class HomeController extends Controller {
    public function index(): void {
        echo View::render('home', [
            'title'   => '首页',
            'message' => '欢迎来到我的博客!',
        ]);
    }

    public function about(): void {
        echo View::render('about', [
            'title'   => '关于我们',
            'team'    => ['小明', '小红', '小刚'],
            'version' => '1.0.0',
        ]);
    }

    public function api(): void {
        $this->json([
            'status' => 'ok',
            'data'   => ['version' => '1.0.0', 'uptime' => time()],
        ]);
    }
}

// === 设置路由 ===
$router = new Router();
$home = new HomeController();

$router->get('/', fn() => $home->index());
$router->get('/about', fn() => $home->about());
$router->get('/api/status', fn() => $home->api());

// === 启动应用 ===
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
?>
逻辑代码 63 行(超过 40 行限制,仅展示)

配套的视图文件 home.php

PHP
<!DOCTYPE html>
<html lang="zh">
<head><title><?= $title ?></title></head>
<body>
    <h1><?= $message ?></h1>
    <p>这是一个用 MVC 思想构建的页面。</p>
</body>
</html>

3. 感悟:从过程式到 OOP 的思维跃迁

过程式思路 OOP 思路
"我要读取用户数据" → 写个 getUser() 函数 "用户是什么?有什么属性和行为?" → 设计 User 类
数据和函数分散在各文件 数据+行为封装在类里
代码重复了 → 复制粘贴或抽函数 代码重复了 → 用继承或 Trait
'admin' 字符串判断权限 UserRole::ADMIN 枚举,类型安全
💡 提示: OOP 不是目的,是手段。小脚本过程式完全够。当你的代码出现这些信号时转向 OOP:(1) 相同参数在多个函数间传递;(2) 全局变量越来越多;(3) 条件判断基于"类型字符串"(if ($role === 'admin'))。

❓ 常见问题

Q MVC 中 Controller 到底做什么?
A Controller 是"交通指挥员"——接收请求、调用 Model 获取数据、选择 View 渲染。它自己不处理业务逻辑(那是 Model 的事),也不写 HTML(那是 View 的事)。
Q 写框架和用框架怎么抉择?
A 学习阶段自己写迷你框架理解原理(就像这课做的),生产环境用 Laravel/Symfony。自己做一遍 Router/Controller/View 后,用 Laravel 时你会说"哦,原来背后是这样"。

❓ 常见问题

Q 这个概念和 XXX 有什么区别?
A 简洁对比两者的核心差异和使用场景。

📖 小节

📝 作业

  1. 基于用户系统,新增 Guest 角色(只能访问 articles,无任何编辑权限),并添加到测试中。
  2. 给路由器增加 addRoute('GET/POST', $path, $handler) 方法,让一条语句注册两种方法。
  3. 在 MVC 框架中增加一个 UserController,实现用户列表(从数组读取)和用户详情页(通过 ID 查询)。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏