当前位置:首页>php>PHP 面向对象编程速查:类/封装/继承/多态/Trait/命名空间

PHP 面向对象编程速查:类/封装/继承/多态/Trait/命名空间

  • 2026-08-18 23:11:17
PHP 面向对象编程速查:类/封装/继承/多态/Trait/命名空间

资料在文章末尾

前六篇写的都是面向过程的代码,这篇把博客系统整个推倒重来,用 OOP 的方式重构一遍。类、对象、封装、继承、多态、接口、Trait、命名空间、自动加载全部讲透,最后附完整可跑的博客系统源码。

一、为什么要从面向过程转向面向对象

前六篇的代码有个共同特征:变量存数据、函数处理逻辑、按顺序一个个调用。这种写法在功能少的时候很顺手,但项目一旦膨胀,几个问题会同时冒出来——全局变量满天飞、函数名容易撞车、同一段逻辑被复制到多处、改一个地方牵连一片。

面向对象编程(OOP)换了个思路:把程序看成一组互相协作的对象,每个对象有自己的属性(数据)和方法(行为),对应现实中的实体——用户、文章、订单。代码天然分块,扩展和维护都方便很多。

打个比方,面向过程像一个人从头到尾做一顿饭:买菜、洗菜、切菜、炒菜、装盘,所有步骤写在一张清单上。面向对象则像去餐厅点菜,你告诉厨师"一份宫保鸡丁",厨师(对象)自己知道怎么做(方法),用冰箱(另一个对象)里的食材(属性),最后给你成品,你不用管炒菜细节。

二、类与对象:蓝图和实例

2.1 类是什么

类是对象的模板,定义一类事物的共同属性和方法。比如"汽车"这个类,有颜色、品牌、速度等属性,有启动、加速、刹车等方法。对象是根据类创建的具体实例,"一辆红色的宝马 X5"就是汽车类的一个对象。

2.2 定义类与创建对象

<?php// 定义类class Car {    // 属性    public $color;    public $brand;    // 方法    public function start() {        echo "汽车已启动!";    }    public function drive() {        echo "正在行驶...";    }}// 创建对象(实例化)$myCar = new Car();$myCar->color = '红色';$myCar->brand = '宝马';$myCar->start(); // 输出:汽车已启动!echo "我开的是一辆{$myCar->color}的{$myCar->brand}。";?>

new 根据类生成对象,用 -> 访问对象的属性和方法。注意 PHP 的 -> 和 Java 的 .、C++ 的 :: 都不一样,新手容易写错。

2.3 $this 伪变量

类的方法内部,$this 代表当前对象本身,用来访问自身的属性和方法。

<?phpclass Car {    public $color = '白色';    public function showColor() {        echo "这辆车的颜色是:" . $this->color;    }}$car = new Car();$car->showColor(); // 这辆车的颜色是:白色?>
实战提醒:
类内方法里访问本对象的属性必须用 $this->属性名$属性名 是访问不到的。$this 是伪变量,不能在静态方法里用。

三、构造函数与析构函数

3.1 构造函数 __construct()

在 new 对象时自动调用,通常用来初始化属性。

<?phpclass Car {    public $color;    public $brand;    public function __construct($color, $brand) {        $this->color = $color;        $this->brand = $brand;    }    public function info() {        echo "品牌:{$this->brand},颜色:{$this->color}";    }}$car = new Car('黑色', '奔驰');$car->info(); // 品牌:奔驰,颜色:黑色?>

有构造函数后,创建对象时可以直接传参设置初始状态,不用手动一个个赋值。

3.2 析构函数 __destruct()

对象被销毁时(脚本结束或变量赋值为 null)自动调用,用来释放资源——关闭文件、断开数据库连接等。PHP 自己会做垃圾回收,所以析构函数实际用得不多,知道有这东西就行。

<?phpclass Car {    public function __destruct() {        echo "汽车对象被销毁了。";    }}$car = new Car();// 脚本结束时输出:汽车对象被销毁了。?>

四、封装:用访问修饰符保护数据

封装是 OOP 的核心原则之一,通过访问修饰符控制属性和方法的可见性。

4.1 三种修饰符

• public:公开,任何地方都能访问。

• protected:受保护,只能在类本身和子类中访问。

• private:私有,只能在类本身内部访问,子类也不行。

<?phpclass User {    private $password;   // 外部无法直接读取或修改    public function setPassword($pwd) {        // 可以在这里添加验证或加密逻辑        $this->password = password_hash($pwd, PASSWORD_DEFAULT);    }    public function checkPassword($pwd) {        return password_verify($pwd, $this->password);    }}$user = new User();$user->setPassword('secret123');var_dump($user->checkPassword('secret123')); // true// echo $user->password; // 报错,不能访问私有属性?>
为什么要封装:
直接暴露属性意味着任何代码都能改它,可能出现"年龄设为负数"这种无效状态。通过方法访问可以在入口加校验,保证数据安全。这是"把细节藏起来,只暴露必要接口"的设计思路,也是很多面试题的考点。

五、继承:用 extends 减少重复代码

继承允许一个类(子类)继承另一个类(父类)的属性和方法,并可以增加或重写。比如父类"哺乳动物"有哺乳方法,子类"狗"自动拥有哺乳方法,还能增加"吠叫"方法。

<?php// 父类class Animal {    public $name;    public function __construct($name) {        $this->name = $name;    }    public function eat() {        echo "{$this->name} 正在吃东西。";    }}// 子类继承class Dog extends Animal {    public function bark() {        echo "{$this->name} 汪汪叫!";    }}$dog = new Dog('旺财');$dog->eat();   // 旺财 正在吃东西。(继承的方法)$dog->bark();  // 旺财 汪汪叫!   (新增的方法)?>

访问修饰符在继承中的表现:public 和 protected 会被子类继承,private 不会。子类可以通过 parent:: 调用父类的构造方法或普通方法。

<?phpclass Dog extends Animal {    public function __construct($name, $breed) {        parent::__construct($name); // 调用父类构造函数        $this->breed = $breed;    }}?>
踩坑提醒:
PHP 只支持单继承,一个类只能继承一个父类。如果需要"多继承"的能力,用接口(implements 多个)或 Trait 解决。子类重写父类方法时,参数签名要兼容,否则会报致命错误。

六、多态与方法重写

多态指同一个方法在不同类中表现出不同行为。PHP 里主要通过方法重写实现:子类定义和父类同名的方法。

<?phpclass Animal {    public function sound() {        echo "动物发出声音。";    }}class Dog extends Animal {    public function sound() {        echo "汪汪!";    }}class Cat extends Animal {    public function sound() {        echo "喵喵!";    }}function animalSound(Animal $animal) {    $animal->sound();}animalSound(new Dog()); // 汪汪!animalSound(new Cat()); // 喵喵!?>

即使参数类型声明的是 Animal,实际执行的还是具体对象的方法。代码只关心"是一个动物",不关心具体是什么动物——这就是多态的威力,也是 Laravel 这类框架依赖注入的基础。

七、抽象类:定义规范,禁止直接实例化

如果一个类只用于被继承,不希望被直接 new,可以定义为抽象类。抽象类中可以包含抽象方法(没有方法体,只定义签名),强制子类必须实现它们。

<?phpabstract class Shape {    // 抽象方法没有大括号    abstract public function area();    // 普通方法可以有实现    public function describe() {        echo "这是一个图形。";    }}class Circle extends Shape {    private $radius;    public function __construct($r) {        $this->radius = $r;    }    // 必须实现area(),否则报错    public function area() {        return pi() * $this->radius * $this->radius;    }}$circle = new Circle(5);echo $circle->area(); // 78.5398...?>

抽象类本身不能实例化(new Shape() 会报错),它像一份合同,子类必须遵守。

八、接口:更高层次的规范

接口(interface)只定义方法签名,不包含任何实现。类通过 implements 实现接口,必须实现所有方法。一个类可以实现多个接口。

<?phpinterface Loggable {    public function log($message);}interface Notifiable {    public function sendNotification($user);}class UserService implements Loggable, Notifiable {    public function log($message) {        echo "日志:$message";    }    public function sendNotification($user) {        echo "给 $user 发送通知";    }}?>

接口 vs 抽象类怎么选:

• 接口只能定义公共方法签名,不能有属性;抽象类可以有属性和已实现的方法。

• 一个类可以实现多个接口,但只能继承一个抽象类。

• 接口强调"能做什么",抽象类强调"是什么"。

九、Trait:横向代码复用

PHP 单继承有时不够灵活,Trait 提供了一种在类之间横向共享代码的方式,弥补单继承的局限。

<?phptrait Logger {    public function log($msg) {        echo "[LOG] " . $msg;    }}trait Authenticator {    public function authenticate($user) {        // ...验证逻辑        return true;    }}class Admin {    use Logger, Authenticator; // 使用多个 Trait    public function doSomething() {        $this->log('操作执行了');    }}?>

如果多个 Trait 有同名方法,需要用 insteadof 或 as 解决冲突。

trait A { public function test() { echo "A"; } }trait B { public function test() { echo "B"; } }class C {    use A, B {        A::test insteadof B; // 使用 A 的 test        B::test as testB;    // 把 B 的 test 重命名为 testB    }}

十、静态属性与方法

静态成员属于类本身,而不是某个对象。用 static 关键字声明,通过 ::(范围解析操作符)访问。

<?phpclass Counter {    public static $count = 0;    public static function increment() {        self::$count++; // self 代表当前类    }}Counter::increment();Counter::increment();echo Counter::$count; // 2?>

静态方法里不能用 $this,因为它不属于任何对象。

后期静态绑定(进阶):
在继承中,self:: 指向定义方法的类,static:: 指向运行时实际调用的类。这是高级特性,框架底层经常用,日常业务写得不多了解即可。

十一、类常量

类中可以用 const 定义常量,属于类本身,通过 :: 访问,值不可变。

<?phpclass Config {    const VERSION = '1.0';    const DEBUG = true;}echo Config::VERSION; // 1.0?>

十二、魔术方法:自动触发的特殊方法

PHP 提供了一系列以 __ 开头的方法,在特定情况下自动调用。

魔术方法
触发场景
__construct()
创建对象时
__destruct()
销毁对象时
__get($name)
访问不可访问(私有/不存在)属性时
__set($name, $value)
给不可访问属性赋值时
__isset($name)
对不可访问属性调用 isset() 或 empty()
__unset($name)
对不可访问属性调用 unset()
__call($name, $args)
调用不可访问的方法时
__callStatic($name, $args)
调用不可访问的静态方法时
__toString()
对象被当作字符串使用时(如 echo $obj
__clone()
对象被 clone 时
<?phpclass User {    private $data = [];    public function __set($name, $value) {        $this->data[$name] = $value;    }    public function __get($name) {        return $this->data[$name] ?? null;    }    public function __toString() {        return "用户:" . json_encode($this->data);    }}$user = new User();$user->name = '张三'; // 触发 __setecho $user->name;     // 触发 __get,输出 张三echo $user;           // 触发 __toString,输出 用户:{"name":"张三"}?>

十三、命名空间:告别类名冲突

项目变大后,类名冲突是常事——两个模块都有 User 类。命名空间解决这个问题。

<?phpnamespace Blog\Controllers;class UserController {    // ...}// 使用$controller = new \Blog\Controllers\UserController();?>

或者用 use 导入:

<?phpnamespace App;use Blog\Controllers\UserController;$controller = new UserController();?>

没有指定命名空间的代码属于全局空间,访问全局类时要加 \ 前缀,比如 \PDO\Exception

十四、自动加载:告别 require 地狱

传统方式每个文件都要 require 'User.php'; require 'Post.php';,文件一多就崩溃。自动加载让你遵循命名约定后,类在被使用时自动加载对应文件。

Composer 自动加载是现代 PHP 的标准方案。安装 Composer 后,在项目根目录创建 composer.json

{    "autoload": {        "psr-4": {            "MyApp\\": "src/"        }    }}

运行 composer dump-autoload 后,只要引入 vendor/autoload.php,就能自动加载 src/ 目录下遵循 PSR-4 规范的类。比如 MyApp\Models\User 会被映射到 src/Models/User.php

不用 Composer 也能自动加载:
本篇综合实战用 spl_autoload_register() 写了一个简易 PSR-4 加载器,适合学习理解原理。生产环境还是用 Composer。

十五、综合实战:把博客系统整个重构成 OOP 版

前六篇的博客系统是面向过程写的,这一节用 OOP 重构一遍。核心思路:把数据库操作封装成 Database 单例类,把用户和文章各自封装成 User / Post 模型类,页面只负责调用模型方法 + 渲染 HTML。

15.1 目录结构

项目根目录├── Core/│   └── Database.php      # 数据库单例类├── Models/│   ├── User.php          # 用户模型│   └── Post.php          # 文章模型├── register.php          # 注册页面├── login.php             # 登录页面├── home.php              # 文章首页列表├── create_post.php       # 发布文章├── view_post.php         # 文章详情├── edit_post.php         # 编辑文章├── delete_post.php       # 删除文章├── welcome.php           # 个人中心├── delete_user.php       # 注销用户└── logout.php            # 退出登录

15.2 数据库 SQL

CREATE DATABASE IF NOT EXISTS blog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;USE blog;CREATE TABLE users (    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,    username VARCHAR(50) NOT NULL UNIQUE,    password VARCHAR(255) NOT NULL,    email VARCHAR(100) NOT NULL UNIQUE,    avatar VARCHAR(255) NULL,    create_time DATETIME DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB;CREATE TABLE posts (    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,    user_id INT UNSIGNED NOT NULL,    title VARCHAR(200) NOT NULL,    content TEXT NOT NULL,    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE) ENGINE=InnoDB;

15.3 Core/Database.php 数据库单例类

<?phpnamespace Blog\Core;class Database{    private static ?self $instance = null;    private \PDO $pdo;    // 私有构造,禁止外部new    private function __construct()    {        $host = '127.0.0.1';        $dbname = 'blog';        $dbUser = 'root';        $dbPass = '';        $charset = 'utf8mb4';        $dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";        $options = [            \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,            \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,            \PDO::ATTR_EMULATE_PREPARES   => false,        ];        $this->pdo = new \PDO($dsn, $dbUser, $dbPass, $options);    }    // 获取单例实例    public static function getInstance(): self    {        if (self::$instance === null) {            self::$instance = new self();        }        return self::$instance;    }    // 获取PDO对象    public function getPdo(): \PDO    {        return $this->pdo;    }    // 禁止克隆    private function __clone() {}}
单例模式要点:
构造函数私有化(外部不能 new)、getInstance() 静态方法返回唯一实例、禁止克隆。这样整个应用只创建一个 PDO 连接,避免重复连接数据库。第六篇面向过程版每次都 new PDO,重构后只连接一次。

15.4 Models/User.php 用户模型

<?phpnamespace Blog\Models;use Blog\Core\Database;class User{    public ?int $id = null;    public string $username = '';    public string $email = '';    public ?string $avatar = null;    /**     * 根据用户名查询用户(不含密码)     */    public static function findByUsername(string $username): ?self    {        $pdo = Database::getInstance()->getPdo();        $stmt = $pdo->prepare("SELECT id, username, email, avatar FROM users WHERE username = :u");        $stmt->execute([':u' => $username]);        $data = $stmt->fetch();        if (!$data) return null;        $user = new self();        $user->id = $data['id'];        $user->username = $data['username'];        $user->email = $data['email'];        $user->avatar = $data['avatar'];        return $user;    }    /**     * 根据ID查询完整用户(含密码用于校验)     */    public static function findById(int $id): ?self    {        $pdo = Database::getInstance()->getPdo();        $stmt = $pdo->prepare("SELECT id, username, email, avatar, password FROM users WHERE id = :id");        $stmt->execute([':id' => $id]);        $data = $stmt->fetch();        if (!$data) return null;        $user = new self();        $user->id = $data['id'];        $user->username = $data['username'];        $user->email = $data['email'];        $user->avatar = $data['avatar'];        return $user;    }    /**     * 注册新用户,返回用户ID     */    public static function create(string $username, string $password, string $email, ?string $avatarPath = null): int    {        $pdo = Database::getInstance()->getPdo();        $hashPwd = password_hash($password, PASSWORD_DEFAULT);        $stmt = $pdo->prepare(            "INSERT INTO users (username, password, email, avatar) VALUES (:u, :p, :e, :avatar)"        );        $stmt->execute([            ':u' => $username,            ':p' => $hashPwd,            ':e' => $email,            ':avatar' => $avatarPath        ]);        return (int)$pdo->lastInsertId();    }    /**     * 校验密码是否匹配     */    public function verifyPassword(string $inputPwd): bool    {        if (!$this->id) return false;        $pdo = Database::getInstance()->getPdo();        $stmt = $pdo->prepare("SELECT password FROM users WHERE id = :id");        $stmt->execute([':id' => $this->id]);        $row = $stmt->fetch();        return $row && password_verify($inputPwd, $row['password']);    }    /**     * 校验用户名/邮箱是否已存在     */    public static function isExists(string $username, string $email): bool    {        $pdo = Database::getInstance()->getPdo();        $stmt = $pdo->prepare("SELECT id FROM users WHERE username = :u OR email = :e");        $stmt->execute([':u' => $username, ':e' => $email]);        return (bool)$stmt->fetch();    }    /**     * 删除当前用户账号(注销)     */    public function delete(): void    {        if (!$this->id) return;        $pdo = Database::getInstance()->getPdo();        $stmt = $pdo->prepare("DELETE FROM users WHERE id = :uid");        $stmt->execute([':uid' => $this->id]);    }}

15.5 Models/Post.php 文章模型

<?phpnamespace Blog\Models;use Blog\Core\Database;class Post{    public ?int $id = null;    public int $userId = 0;    public string $title = '';    public string $content = '';    public string $createdAt = '';    public string $authorName = '';    /**     * 获取全部文章(联表作者,倒序)     * @return Post[]     */    public static function getAll(): array    {        $pdo = Database::getInstance()->getPdo();        $sql = "            SELECT p.*, u.username as authorName            FROM posts p            JOIN users u ON p.user_id = u.id            ORDER BY p.created_at DESC        ";        $stmt = $pdo->query($sql);        $list = [];        while ($row = $stmt->fetch()) {            $post = new self();            $post->id = $row['id'];            $post->userId = $row['user_id'];            $post->title = $row['title'];            $post->content = $row['content'];            $post->createdAt = $row['created_at'];            $post->authorName = $row['authorName'];            $list[] = $post;        }        return $list;    }    /**     * 根据ID查询单篇文章     */    public static function find(int $id): ?self    {        $pdo = Database::getInstance()->getPdo();        $sql = "            SELECT p.*, u.username as authorName            FROM posts p            JOIN users u ON p.user_id = u.id            WHERE p.id = :pid        ";        $stmt = $pdo->prepare($sql);        $stmt->execute([':pid' => $id]);        $row = $stmt->fetch();        if (!$row) return null;        $post = new self();        $post->id = $row['id'];        $post->userId = $row['user_id'];        $post->title = $row['title'];        $post->content = $row['content'];        $post->createdAt = $row['created_at'];        $post->authorName = $row['authorName'];        return $post;    }    /**     * 保存:新增 / 更新     */    public function save(): self    {        $pdo = Database::getInstance()->getPdo();        if ($this->id) {            // 更新            $stmt = $pdo->prepare("UPDATE posts SET title=:t, content=:c WHERE id=:id");            $stmt->execute([                ':t' => $this->title,                ':c' => $this->content,                ':id' => $this->id            ]);        } else {            // 新增            $stmt = $pdo->prepare("INSERT INTO posts (user_id, title, content) VALUES (:uid, :t, :c)");            $stmt->execute([                ':uid' => $this->userId,                ':t' => $this->title,                ':c' => $this->content            ]);            $this->id = (int)$pdo->lastInsertId();        }        return $this;    }    /**     * 删除当前文章     */    public function delete(): void    {        if (!$this->id) return;        $pdo = Database::getInstance()->getPdo();        $stmt = $pdo->prepare("DELETE FROM posts WHERE id = :id");        $stmt->execute([':id' => $this->id]);    }}
save() 方法的巧思:
同一个 save() 方法既能新增也能更新——判断 $this->id 是否存在来决定走 INSERT 还是 UPDATE。这是 ActiveRecord 模式的雏形,Laravel 的 Eloquent 也是这个思路。

15.6 register.php 注册页面(头像上传 + 模型校验)

<?php// 自定义PSR4自动加载器,无需composerspl_autoload_register(function ($className) {    // 命名空间根 Blog\ 对应项目根目录    $prefix = 'Blog\\';    $baseDir = __DIR__ . DIRECTORY_SEPARATOR;    // 匹配命名空间前缀    if (str_starts_with($className, $prefix)) {        $relativeClass = substr($className, strlen($prefix));        // 命名空间转文件路径 Blog\Models\User → Models/User.php        $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';        if (file_exists($file)) {            require $file;        }    }});session_start();header("X-XSS-Protection: 1; mode=block");header("X-Frame-Options: DENY");use Blog\Models\User;// CSRFif (empty($_SESSION['csrf_reg'])) {    $_SESSION['csrf_reg'] = bin2hex(random_bytes(32));}$errors = [];$fill = ['username' => '', 'email' => ''];$uploadDir = __DIR__ . '../uploads/avatar/';if (!file_exists($uploadDir)) @mkdir($uploadDir, 0755, true);$avatarPath = null;if ($_SERVER['REQUEST_METHOD'] === 'POST') {    // CSRF校验    if (!hash_equals($_SESSION['csrf_reg'], $_POST['csrf_token'] ?? '')) {        $errors['global'] = "非法提交请求";    }    $username = trim($_POST['username'] ?? '');    $password = $_POST['password'] ?? '';    $password2 = $_POST['password2'] ?? '';    $email = trim($_POST['email'] ?? '');    $fill['username'] = $username;    $fill['email'] = $email;    // 基础校验    if (empty($username)) $errors['username'] = "用户名不能为空";    elseif (mb_strlen($username) < 3) $errors['username'] = "用户名至少3字符";    if (empty($password)) $errors['password'] = "密码不能为空";    elseif (strlen($password) < 6) $errors['password'] = "密码最少6位";    if ($password !== $password2) $errors['password2'] = "两次密码不一致";    if (empty($email)) $errors['email'] = "邮箱不能为空";    elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors['email'] = "邮箱格式错误";    // 头像上传    if (isset($_FILES['avatar']) && $_FILES['avatar']['error'] !== UPLOAD_ERR_NO_FILE) {        $file = $_FILES['avatar'];        if ($file['error'] !== UPLOAD_ERR_OK) {            $errors['avatar'] = "文件上传失败";        } else {            $finfo = new finfo(FILEINFO_MIME_TYPE);            $mime = $finfo->file($file['tmp_name']);            $allow = ['image/jpeg','image/png'];            if (!in_array($mime, $allow)) $errors['avatar'] = "仅支持jpg/png";            elseif ($file['size'] > 1048576) $errors['avatar'] = "头像不超过1MB";            else {                $ext = $mime === 'image/png' ? 'png' : 'jpg';                $name = md5(uniqid(true).$username).".".$ext;                $dest = $uploadDir . $name;                move_uploaded_file($file['tmp_name'], $dest);                $avatarPath = "uploads/avatar/".$name;            }        }    }    // 无错误校验账号是否存在    if (empty($errors)) {        if (User::isExists($username, $email)) {            $errors['global'] = "用户名或邮箱已被注册";        } else {            $uid = User::create($username, $password, $email, $avatarPath);            // 自动登录            $_SESSION['logged_in'] = true;            $_SESSION['user_id'] = $uid;            $_SESSION['username'] = $username;            $_SESSION['avatar'] = $avatarPath;            header("Location: welcome.php", true, 302);            exit;        }    }}?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>用户注册</title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f4f6f9;padding:60px 20px;}.card{width:420px;margin:0 auto;background:#fff;padding:32px;border-radius:10px;box-shadow:0 2px 12px rgba(0,0,0,0.07);}h2{text-align:center;margin-bottom:24px;color:#2d3748;}.global-err{background:#fee;color:#dc2626;padding:10px;border-radius:6px;margin-bottom:16px;text-align:center;}.item{margin-bottom:16px;}label{display:block;margin-bottom:6px;color:#4a5568;}input{width:100%;padding:10px 12px;border:1px solid #cbd5e0;border-radius:6px;font-size:15px;}.err-text{color:#dc2626;font-size:13px;margin-top:4px;display:block;}button{width:100%;padding:11px;background:#2563eb;color:#fff;border:none;border-radius:6px;font-size:16px;cursor:pointer;}button:hover{background:#1d4ed8;}.link{text-align:center;margin-top:16px;font-size:14px;}.link a{color:#2563eb;text-decoration:none;}</style></head><body><div class="card">    <h2>新用户注册</h2>    <?php if(!empty($errors['global'])): ?>        <div class="global-err"><?=htmlspecialchars($errors['global'],ENT_QUOTES)?></div>    <?php endif; ?>    <form method="post" enctype="multipart/form-data">        <input type="hidden" name="csrf_token" value="<?=htmlspecialchars($_SESSION['csrf_reg'],ENT_QUOTES)?>">        <div class="item">            <label>用户名</label>            <input type="text" name="username" value="<?=htmlspecialchars($fill['username'],ENT_QUOTES)?>" placeholder="至少3字符">            <?php if(!empty($errors['username'])):?><span class="err-text"><?=$errors['username']?></span><?php endif; ?>        </div>        <div class="item">            <label>密码</label>            <input type="password" name="password" placeholder="最少6位">            <?php if(!empty($errors['password'])):?><span class="err-text"><?=$errors['password']?></span><?php endif; ?>        </div>        <div class="item">            <label>确认密码</label>            <input type="password" name="password2">            <?php if(!empty($errors['password2'])):?><span class="err-text"><?=$errors['password2']?></span><?php endif; ?>        </div>        <div class="item">            <label>邮箱</label>            <input type="email" name="email" value="<?=htmlspecialchars($fill['email'],ENT_QUOTES)?>">            <?php if(!empty($errors['email'])):?><span class="err-text"><?=$errors['email']?></span><?php endif; ?>        </div>        <div class="item">            <label>头像(选填,1MB内jpg/png)</label>            <input type="file" name="avatar" accept="image/jpeg,image/png">            <?php if(!empty($errors['avatar'])):?><span class="err-text"><?=$errors['avatar']?></span><?php endif; ?>        </div>        <button type="submit">注册</button>    </form>    <div class="link">已有账号?<a href="login.php">去登录</a></div></div></body></html>

15.7 login.php 登录页面

<?phpspl_autoload_register(function ($className) {    $prefix = 'Blog\\';    $baseDir = __DIR__ . DIRECTORY_SEPARATOR;    if (str_starts_with($className, $prefix)) {        $relativeClass = substr($className, strlen($prefix));        $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';        if (file_exists($file)) {            require $file;        }    }});session_start();header("X-XSS-Protection: 1; mode=block");header("X-Frame-Options: DENY");use Blog\Models\User;$error = '';$fillUser = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {    $username = trim($_POST['username'] ?? '');    $password = $_POST['password'] ?? '';    $fillUser = $username;    if (empty($username) || empty($password)) {        $error = "用户名和密码不能为空";    } else {        $user = User::findByUsername($username);        if (!$user || !$user->verifyPassword($password)) {            $error = "用户名或密码错误";        } else {            $_SESSION['logged_in'] = true;            $_SESSION['user_id'] = $user->id;            $_SESSION['username'] = $user->username;            $_SESSION['avatar'] = $user->avatar;            header("Location: welcome.php", true, 302);            exit;        }    }}?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>账号登录</title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f4f6f9;padding:60px 20px;}.card{width:380px;margin:0 auto;background:#fff;padding:32px;border-radius:10px;box-shadow:0 2px 12px rgba(0,0,0,0.07);}h2{text-align:center;margin-bottom:24px;color:#2d3748;}.err-box{background:#fee;color:#dc2626;padding:10px;border-radius:6px;margin-bottom:16px;text-align:center;}.row{margin-bottom:16px;}label{display:block;margin-bottom:6px;color:#4a5568;}input{width:100%;padding:10px 12px;border:1px solid #cbd5e0;border-radius:6px;font-size:15px;}button{width:100%;padding:11px;background:#2563eb;color:#fff;border:none;border-radius:6px;font-size:16px;cursor:pointer;}button:hover{background:#1d4ed8;}.reg-link{text-align:center;margin-top:16px;font-size:14px;}.reg-link a{color:#2563eb;text-decoration:none;}</style></head><body><div class="card">    <h2>账号登录</h2>    <?php if($error): ?>        <div class="err-box"><?=htmlspecialchars($error,ENT_QUOTES)?></div>    <?php endif; ?>    <form method="post">        <div class="row">            <label>用户名</label>            <input type="text" name="username" value="<?=htmlspecialchars($fillUser,ENT_QUOTES)?>">        </div>        <div class="row">            <label>密码</label>            <input type="password" name="password">        </div>        <button type="submit">登录</button>    </form>    <div class="reg-link">没有账号?<a href="register.php">注册</a></div></div></body></html>

15.8 delete_user.php 注销用户

<?phpspl_autoload_register(function ($className) {    $namespacePrefix = 'Blog\\';    $rootPath = __DIR__ . DIRECTORY_SEPARATOR;    if (strpos($className, $namespacePrefix) === 0) {        $classPath = substr($className, strlen($namespacePrefix));        $file = $rootPath . str_replace('\\', DIRECTORY_SEPARATOR, $classPath) . '.php';        if (file_exists($file)) {            require_once $file;        }    }});session_start();header("X-XSS-Protection: 1; mode=block");header("X-Frame-Options: DENY");use Blog\Models\User;// 未登录拦截if (empty($_SESSION['logged_in'])) {    header("Location: login.php", true, 302);    exit;}// CSRF令牌if (empty($_SESSION['csrf_del_user'])) {    $_SESSION['csrf_del_user'] = bin2hex(random_bytes(32));}$error = '';$currentUser = User::findById($_SESSION['user_id']);if (!$currentUser) {    header("Location: login.php", true, 302);    exit;}// 提交注销逻辑if ($_SERVER['REQUEST_METHOD'] === 'POST') {    $token = $_POST['csrf_token'] ?? '';    $inputPwd = trim($_POST['password'] ?? '');    // CSRF校验    if (!hash_equals($_SESSION['csrf_del_user'], $token)) {        $error = "非法操作";    } elseif (empty($inputPwd)) {        $error = "请输入登录密码确认注销";    } elseif (!$currentUser->verifyPassword($inputPwd)) {        $error = "密码错误,无法注销账号";    } else {        // 删除用户账号        $currentUser->delete();        // 清空session跳转登录页        $_SESSION = [];        if (ini_get("session.use_cookies")) {            $params = session_get_cookie_params();            setcookie(session_name(), '', time() - 86400,                $params["path"], $params["domain"],                $params["secure"], $params["httponly"]            );        }        session_destroy();        header("Location: login.php", true, 302);        exit;    }}?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>注销账号确认</title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f5f7fa;padding:80px 20px;}.card{width:480px;margin:0 auto;background:#fff;padding:36px;border-radius:10px;box-shadow:0 2px 12px rgba(0,0,0,0.08);text-align:center;}h2{color:#dc2626;margin-bottom:16px;font-size:20px;}.tip{margin-bottom:24px;color:#444;line-height:1.7;}.err{color:#dc2626;margin-bottom:16px;}.input-box{width:100%;padding:10px 12px;border:1px solid #cbd5e0;border-radius:6px;margin-bottom:20px;font-size:15px;}.btn-group{display:flex;gap:12px;width:100%;}.btn-col{flex: 1 1 50%;}.btn{display:block;width:100%;height:48px;line-height:48px;font-size:16px;text-align:center;border-radius:6px;border:none;text-decoration:none;cursor:pointer;color:#fff;}.btn-del{background:#dc2626;}.btn-cancel{background:#666666;}</style></head><body><div class="card">    <h2>永久注销账号</h2>    <?php if($error): ?>        <div class="err"><?=htmlspecialchars($error,ENT_QUOTES)?></div>    <?php endif; ?>    <div class="tip">        注销后你的账号、头像、所有发布文章将被永久删除,数据无法恢复!<br>        请输入登录密码确认操作    </div>    <form method="post">        <input type="hidden" name="csrf_token" value="<?=htmlspecialchars($_SESSION['csrf_del_user'],ENT_QUOTES)?>">        <input class="input-box" type="password" name="password" placeholder="输入你的登录密码" required>        <div class="btn-group">            <div class="btn-col">                <button class="btn btn-del" type="submit">确认注销</button>            </div>            <div class="btn-col">                <a class="btn btn-cancel" href="welcome.php">取消返回</a>            </div>        </div>    </form></div></body></html>

15.9 welcome.php 个人中心

<?phpspl_autoload_register(function ($className) {    $namespacePrefix = 'Blog\\';    $rootPath = __DIR__ . DIRECTORY_SEPARATOR;    if (strpos($className, $namespacePrefix) === 0) {        $classPath = substr($className, strlen($namespacePrefix));        $file = $rootPath . str_replace('\\', DIRECTORY_SEPARATOR, $classPath) . '.php';        if (file_exists($file)) {            require_once $file;        }    }});session_start();if (empty($_SESSION['logged_in'])) {    header("Location: login.php", true, 302);    exit;}use Blog\Models\User;$userName = htmlspecialchars($_SESSION['username'], ENT_QUOTES);$avatar = '';$dbUser = User::findById($_SESSION['user_id']);if ($dbUser) {    $avatar = $dbUser->avatar;}?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>个人中心</title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f4f6f9;padding-top:100px;text-align:center;}.box{width:460px;margin:0 auto;background:#fff;padding:40px 30px;border-radius:12px;box-shadow:0 2px 14px rgba(0,0,0,0.07);}h2{color:#2d3748;margin-bottom:20px;font-size:22px;}.avatar-wrap{margin:26px 0 32px;}.avatar-img{width:140px;height:140px;border-radius:50%;object-fit:cover;border:3px solid #2563eb;}.avatar-empty{width:140px;height:140px;border-radius:50%;background:#e2e8f0;display:inline-flex;align-items:center;justify-content:center;color:#666;font-size:14px;}.btn-wrap{display:grid;grid-template-columns: 1fr 1fr;gap:14px;}.btn{display:block;width:100%;height:46px;line-height:46px;font-size:16px;text-decoration:none;border-radius:8px;color:#fff;transition:opacity 0.2s ease;}.btn:hover{opacity:0.88;}.btn-blue{background:#2563eb;}.btn-gray{background:#6b7280;}.btn-red{background:#dc2626;}</style></head><body><div class="box">    <h2>欢迎你,<?=$userName?>!</h2>    <div class="avatar-wrap">        <?php if(!empty($avatar) && file_exists($avatar)): ?>            <img class="avatar-img" src="<?=htmlspecialchars($avatar,ENT_QUOTES)?>" alt="用户头像">        <?php else: ?>            <div class="avatar-empty">暂无头像</div>        <?php endif; ?>    </div>    <div class="btn-wrap">        <a href="home.php" class="btn btn-blue">浏览全部文章</a>        <a href="create_post.php" class="btn btn-blue">发布新文章</a>        <a href="logout.php" class="btn btn-gray">安全退出登录</a>        <a href="delete_user.php" class="btn btn-red">永久注销账号</a>    </div></div></body></html>

15.10 logout.php 安全退出

<?phpsession_start();$_SESSION = [];if (ini_get("session.use_cookies")) {    $params = session_get_cookie_params();    setcookie(session_name(), '', time()-86400,        $params["path"], $params["domain"],        $params["secure"], $params["httponly"]    );}session_destroy();header("Location: login.php", true, 302);exit;?>

15.11 home.php 文章首页列表

<?phpspl_autoload_register(function ($className) {    $prefix = 'Blog\\';    $baseDir = __DIR__ . DIRECTORY_SEPARATOR;    if (str_starts_with($className, $prefix)) {        $relativeClass = substr($className, strlen($prefix));        $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';        if (file_exists($file)) {            require $file;        }    }});session_start();use Blog\Models\Post;$postList = Post::getAll();?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>全部文章</title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f5f7fa;padding:40px 20px;}.wrap{max-width:720px;margin:0 auto;}.top{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px;}h1{color:#2d3748;font-size:24px;}.btn{padding:8px 16px;background:#2563eb;color:#fff;text-decoration:none;border-radius:6px;}.item{background:#fff;padding:20px;border-radius:10px;box-shadow:0 1px 8px rgba(0,0,0,0.06);margin-bottom:16px;}.title{font-size:18px;margin-bottom:8px;}.title a{color:#2563eb;text-decoration:none;}.meta{color:#666;font-size:14px;}.empty{text-align:center;padding:40px;color:#888;background:#fff;border-radius:10px;}</style></head><body><div class="wrap">    <div class="top">        <h1>文章列表</h1>        <div>            <?php if(!empty($_SESSION['logged_in'])): ?>                <a href="create_post.php" class="btn">发布文章</a>                <a href="welcome.php" class="btn" style="background:#666;margin-left:8px;">个人中心</a>            <?php else: ?>                <a href="login.php" class="btn">登录</a>            <?php endif; ?>        </div>    </div>    <?php if(empty($postList)): ?>        <div class="empty">暂无文章</div>    <?php else: ?>        <?php foreach($postList as $p): ?>            <div class="item">                <div class="title">                    <a href="view_post.php?id=<?=$p->id?>"><?=htmlspecialchars($p->title)?></a>                </div>                <div class="meta">作者:<?=htmlspecialchars($p->authorName)?> · <?=$p->createdAt?></div>            </div>        <?php endforeach; ?>    <?php endif; ?></div></body></html>

15.12 create_post.php 发布文章

<?phpspl_autoload_register(function ($className) {    $prefix = 'Blog\\';    $baseDir = __DIR__ . DIRECTORY_SEPARATOR;    if (str_starts_with($className, $prefix)) {        $relativeClass = substr($className, strlen($prefix));        $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';        if (file_exists($file)) {            require $file;        }    }});session_start();header("X-XSS-Protection: 1; mode=block");header("X-Frame-Options: DENY");if (empty($_SESSION['logged_in'])) {    header("Location: login.php", true, 302);    exit;}use Blog\Models\Post;if (empty($_SESSION['csrf_post'])) $_SESSION['csrf_post'] = bin2hex(random_bytes(32));$errors = [];$tFill = $cFill = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {    if (!hash_equals($_SESSION['csrf_post'], $_POST['csrf_token']??'')) $errors[] = "非法请求";    $tFill = trim($_POST['title']??'');    $cFill = trim($_POST['content']??'');    if(empty($tFill)) $errors[] = "标题不能为空";    if(mb_strlen($tFill)>200) $errors[] = "标题不能超过200字";    if(empty($cFill)) $errors[] = "内容不能为空";    if(empty($errors)){        $post = new Post();        $post->userId = $_SESSION['user_id'];        $post->title = $tFill;        $post->content = $cFill;        $post->save();        header("Location: home.php", true, 302);        exit;    }}?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>发布文章</title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f5f7fa;padding:60px 20px;}.card{width:620px;margin:0 auto;background:#fff;padding:32px;border-radius:10px;box-shadow:0 2px 12px rgba(0,0,0,0.08);}h2{text-align:center;margin-bottom:24px;color:#2d3748;}.err{background:#fee;color:#dc2626;padding:10px;border-radius:6px;margin-bottom:16px;}.item{margin-bottom:16px;}label{display:block;margin-bottom:6px;color:#444;}input,textarea{width:100%;padding:12px;border:1px solid #cbd5e0;border-radius:6px;font-size:15px;}textarea{height:240px;resize:none;}button{width:100%;padding:12px;background:#2563eb;color:#fff;border:none;border-radius:6px;font-size:16px;cursor:pointer;}button:hover{background:#1d4ed8;}.back{display:block;text-align:center;margin-top:16px;color:#666;text-decoration:none;}</style></head><body><div class="card">    <h2>发布新文章</h2>    <?php if(!empty($errors)): ?>        <div class="err"><?=implode('<br>', array_map(fn($v)=>htmlspecialchars($v,ENT_QUOTES),$errors))?></div>    <?php endif; ?>    <form method="post">        <input type="hidden" name="csrf_token" value="<?=htmlspecialchars($_SESSION['csrf_post'],ENT_QUOTES)?>">        <div class="item">            <label>标题</label>            <input type="text" name="title" value="<?=htmlspecialchars($tFill,ENT_QUOTES)?>">        </div>        <div class="item">            <label>正文</label>            <textarea name="content"><?=htmlspecialchars($cFill,ENT_QUOTES)?></textarea>        </div>        <button type="submit">发布</button>    </form>    <a class="back" href="home.php">返回列表</a></div></body></html>

15.13 view_post.php 文章详情

<?phpspl_autoload_register(function ($className) {    $prefix = 'Blog\\';    $baseDir = __DIR__ . DIRECTORY_SEPARATOR;    if (str_starts_with($className, $prefix)) {        $relativeClass = substr($className, strlen($prefix));        $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';        if (file_exists($file)) {            require $file;        }    }});session_start();use Blog\Models\Post;$id = (int)($_GET['id'] ?? 0);$post = Post::find($id);if(!$post){    header("Location: home.php", true, 302);    exit;}$isAuthor = !empty($_SESSION['logged_in']) && $_SESSION['user_id'] == $post->userId;?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title><?=htmlspecialchars($post->title)?></title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f5f7fa;padding:40px 20px;}.box{max-width:700px;margin:0 auto;background:#fff;padding:36px;border-radius:10px;box-shadow:0 2px 12px rgba(0,0,0,0.08);}h1{text-align:center;margin-bottom:16px;color:#222;}.meta{text-align:center;color:#666;margin-bottom:24px;padding-bottom:16px;border-bottom:1px solid #eee;}.content{line-height:1.8;font-size:16px;color:#333;white-space:pre-wrap;}.btn-group{margin-top:30px;text-align:center;}.btn{display:inline-block;padding:8px 16px;margin:0 6px;text-decoration:none;border-radius:6px;color:#fff;}.edit{background:#059669;}.del{background:#dc2626;}.back{background:#666;}</style></head><body><div class="box">    <h1><?=htmlspecialchars($post->title)?></h1>    <div class="meta">作者:<?=htmlspecialchars($post->authorName)?> | <?=$post->createdAt?></div>    <div class="content"><?=nl2br(htmlspecialchars($post->content))?></div>    <div class="btn-group">        <?php if($isAuthor): ?>            <a href="edit_post.php?id=<?=$post->id?>" class="btn edit">编辑</a>            <a href="delete_post.php?id=<?=$post->id?>" class="btn del">删除</a>        <?php endif; ?>        <a href="home.php" class="btn back">返回列表</a>    </div></div></body></html>

15.14 edit_post.php 编辑文章

<?phpspl_autoload_register(function ($className) {    $prefix = 'Blog\\';    $baseDir = __DIR__ . DIRECTORY_SEPARATOR;    if (str_starts_with($className, $prefix)) {        $relativeClass = substr($className, strlen($prefix));        $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';        if (file_exists($file)) {            require $file;        }    }});session_start();header("X-XSS-Protection: 1; mode=block");header("X-Frame-Options: DENY");if (empty($_SESSION['logged_in'])) {    header("Location: login.php", true, 302);    exit;}use Blog\Models\Post;$pid = (int)($_GET['id'] ?? 0);$post = Post::find($pid);if(!$post || $post->userId != $_SESSION['user_id']){    header("Location: home.php", true, 302);    exit;}if(empty($_SESSION['csrf_edit'])) $_SESSION['csrf_edit'] = bin2hex(random_bytes(32));$errors = [];$tFill = $post->title;$cFill = $post->content;if ($_SERVER['REQUEST_METHOD'] === 'POST') {    if (!hash_equals($_SESSION['csrf_edit'], $_POST['csrf_token']??'')) $errors[] = "非法请求";    $tFill = trim($_POST['title']??'');    $cFill = trim($_POST['content']??'');    if(empty($tFill)) $errors[] = "标题不能为空";    if(mb_strlen($tFill)>200) $errors[] = "标题超长";    if(empty($cFill)) $errors[] = "内容不能为空";    if(empty($errors)){        $post->title = $tFill;        $post->content = $cFill;        $post->save();        header("Location: view_post.php?id=$pid", true, 302);        exit;    }}?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>编辑文章</title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f5f7fa;padding:60px 20px;}.card{width:620px;margin:0 auto;background:#fff;padding:32px;border-radius:10px;box-shadow:0 2px 12px rgba(0,0,0,0.08);}h2{text-align:center;margin-bottom:24px;color:#2d3748;}.err{background:#fee;color:#dc2626;padding:10px;border-radius:6px;margin-bottom:16px;}.item{margin-bottom:16px;}label{display:block;margin-bottom:6px;color:#444;}input,textarea{width:100%;padding:12px;border:1px solid #cbd5e0;border-radius:6px;font-size:15px;}textarea{height:240px;resize:none;}button{width:100%;padding:12px;background:#059669;color:#fff;border:none;border-radius:6px;font-size:16px;cursor:pointer;}button:hover{background:#047857;}.back{display:block;text-align:center;margin-top:16px;color:#666;text-decoration:none;}</style></head><body><div class="card">    <h2>编辑文章</h2>    <?php if(!empty($errors)): ?>        <div class="err"><?=implode('<br>', array_map(fn($v)=>htmlspecialchars($v,ENT_QUOTES),$errors))?></div>    <?php endif; ?>    <form method="post">        <input type="hidden" name="csrf_token" value="<?=htmlspecialchars($_SESSION['csrf_edit'],ENT_QUOTES)?>">        <div class="item">            <label>标题</label>            <input type="text" name="title" value="<?=htmlspecialchars($tFill,ENT_QUOTES)?>">        </div>        <div class="item">            <label>正文</label>            <textarea name="content"><?=htmlspecialchars($cFill,ENT_QUOTES)?></textarea>        </div>        <button type="submit">保存修改</button>    </form>    <a class="back" href="view_post.php?id=<?=$pid?>">取消返回</a></div></body></html>

15.15 delete_post.php 删除文章

<?phpspl_autoload_register(function ($className) {    $prefix = 'Blog\\';    $baseDir = __DIR__ . DIRECTORY_SEPARATOR;    if (str_starts_with($className, $prefix)) {        $relativeClass = substr($className, strlen($prefix));        $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';        if (file_exists($file)) {            require $file;        }    }});session_start();header("X-XSS-Protection: 1; mode=block");header("X-Frame-Options: DENY");if (empty($_SESSION['logged_in'])) {    header("Location: login.php", true, 302);    exit;}use Blog\Models\Post;$pid = (int)($_GET['id'] ?? 0);$post = Post::find($pid);if(!$post || $post->userId != $_SESSION['user_id']){    header("Location: home.php", true, 302);    exit;}if(empty($_SESSION['csrf_del'])) $_SESSION['csrf_del'] = bin2hex(random_bytes(32));$err = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {    if (!hash_equals($_SESSION['csrf_del'], $_POST['csrf_token']??'')) {        $err = "非法操作";    } else {        $post->delete();        header("Location: home.php", true, 302);        exit;    }}?><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>确认删除</title><style>*{margin:0;padding:0;box-sizing:border-box;font-family:Microsoft YaHei;}body{background:#f5f7fa;padding:80px 20px;}.card{width:460px;margin:0 auto;background:#fff;padding:36px;border-radius:10px;box-shadow:0 2px 12px rgba(0,0,0,0.08);text-align:center;}h2{color:#dc2626;margin-bottom:16px;font-size:20px;}.tip{margin-bottom:24px;color:#444;line-height:1.7;}.err{color:#dc2626;margin-bottom:16px;}.btn-group{display:flex;gap:12px;}.btn-group form, .btn-group .wrap{flex:1;}.btn-group button, .btn-group a{    display:block;width:100%;height:44px;line-height:44px;padding:0;border-radius:6px;font-size:16px;text-decoration:none;border:none;cursor:pointer;text-align:center;color:#fff;}.confirm{background:#dc2626;}.cancel{background:#666;}</style></head><body><div class="card">    <h2>删除确认</h2>    <?php if($err): ?><div class="err"><?=htmlspecialchars($err,ENT_QUOTES)?></div><?php endif; ?>    <div class="tip">确定永久删除文章:<br><strong><?=htmlspecialchars($post->title)?></strong>?<br>删除后无法恢复</div>    <div class="btn-group">        <form method="post">            <input type="hidden" name="csrf_token" value="<?=htmlspecialchars($_SESSION['csrf_del'],ENT_QUOTES)?>">            <button class="confirm" type="submit">确认删除</button>        </form>        <div class="wrap">            <a class="cancel" href="view_post.php?id=<?=$pid?>">取消</a>        </div>    </div></div></body></html>

运行效果展示

下面是博客系统 OOP 版的实际运行效果,从注册、登录到发布文章、编辑删除的完整流程截图:

十六、总结

• 类与对象:类是蓝图,new 创建对象。

• 属性与方法$this 引用自身,构造函数初始化。

• 封装public/protected/private 控制访问,保护数据。

• 继承extends 扩展父类,减少代码重复。

• 多态:方法重写,同一操作不同表现。

• 抽象类与接口:定义规范,强制实现。

• Trait:横向代码复用,解决单继承局限。

• 静态成员与常量:类本身的数据和行为。

• 魔术方法:自动化对象行为。

• 命名空间:避免类名冲突,组织代码。

• 自动加载:Composer PSR-4 一键搞定。

• 综合实战:重构博客系统,代码分离清晰,可维护性飞跃。

到这里,现代 PHP 开发的基石就搭起来了。Laravel 这类框架底层正是大量运用了这些 OOP 概念——依赖注入、服务容器、中间件,都建立在类、接口、多态之上。理解了本篇,你就不只是"会用框架",而是能"看懂框架"。


声明:文字整理过程中借助了 AI 工具辅助排版,代码与知识点均经过实际测试验证。

本文配套资料包

代码合集
速查手册
实战练习题
面试考点卡
公众号回复php07获取

资料下载

-END-

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 14:38:16 HTTP/2.0 GET : https://f.mffb.com.cn/a/506740.html
  2. 运行时间 : 0.366620s [ 吞吐率:2.73req/s ] 内存消耗:4,741.15kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=458f9cbd5f6e069cd39b9f5738a2cec9
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000908s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001266s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.010083s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.021492s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001620s ]
  6. SELECT * FROM `set` [ RunTime:0.001254s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.002096s ]
  8. SELECT * FROM `article` WHERE `id` = 506740 LIMIT 1 [ RunTime:0.005341s ]
  9. UPDATE `article` SET `lasttime` = 1787294296 WHERE `id` = 506740 [ RunTime:0.058242s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000744s ]
  11. SELECT * FROM `article` WHERE `id` < 506740 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.004174s ]
  12. SELECT * FROM `article` WHERE `id` > 506740 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.016119s ]
  13. SELECT * FROM `article` WHERE `id` < 506740 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002079s ]
  14. SELECT * FROM `article` WHERE `id` < 506740 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.030046s ]
  15. SELECT * FROM `article` WHERE `id` < 506740 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.064306s ]
0.369915s