PHP: 类与对象

你写了 21 课的过程式代码,可能开始觉得代码一多就乱了。面向对象编程(OOP)就是来解决这个问题的——把相关的数据和函数打包成"类",让代码更有组织、更容易复用。

1. 为什么需要类和对象?

用现实世界类比:

TEXT 📖 仅展示
过程式:你每次做蛋糕都列清单——打鸡蛋→加面粉→搅拌→烘烤
面向对象:你有个"蛋糕机"(类),按"制作"按钮就出蛋糕(对象)

用代码对比:

PHP
<?php
// 过程式:数据和函数分散
$userName = "小明";
$userAge = 25;
function greet($name) {
    return "你好,{$name}";
}
echo greet($userName);

// 面向对象:数据和函数打包在一起
class User {
    public string $name;
    public int $age;
    
    function greet(): string {
        return "你好,{$this->name}";
    }
}
$user = new User();
$user->name = "小明";
$user->age = 25;
echo $user->greet();
?>
💡 提示: OOP 不是"更好"的编程方式——它是组织复杂代码的工具。小脚本用过程式更简单,当代码超过 500 行时 OOP 的优势才显现。


2. 定义类

PHP
<?php
class Car {
    // 属性(数据)
    public string $brand;
    public string $color;
    public int $year;
    
    // 方法(行为)
    public function start(): string {
        return "{$this->brand} 启动了!";
    }
    
    public function honk(): string {
        return "嘟嘟!";
    }
}
?>

语法要点:


3. 创建对象

类只是"蓝图",需要通过 new 关键字创建具体的"对象":

PHP
<?php
// 从蓝图(类)创建3辆具体的车(对象)
$car1 = new Car();
$car1->brand = "丰田";
$car1->color = "白色";
$car1->year = 2024;

$car2 = new Car();
$car2->brand = "宝马";
$car2->color = "黑色";
$car2->year = 2023;

echo $car1->start();  // 丰田 启动了!
echo $car2->start();  // 宝马 启动了!

// 箭头运算符 -> 访问属性和方法
echo $car1->brand;    // 丰田
$car1->color = "红色"; // 修改属性
echo $car1->color;    // 红色
?>

4. 构造函数 __construct

构造函数在 new 时自动调用,用来初始化对象:

PHP
<?php
class Car {
    public string $brand;
    public string $color;
    public int $year;
    
    // 构造函数
    public function __construct(string $brand, string $color, int $year) {
        $this->brand = $brand;
        $this->color = $color;
        $this->year  = $year;
    }
    
    public function getInfo(): string {
        return "{$this->year}款 {$this->color} {$this->brand}";
    }
}

// 创建对象时传参
$car = new Car("丰田", "白色", 2024);
echo $car->getInfo();  // 2024款 白色 丰田
?>
💡 提示: $this->brand = $brand 左边 $this->brand 是属性,右边 $brand 是参数。PHP 8.0+ 可以直接用构造函数属性提升(constructor promotion),见下面。

▶ 示例:PHP 8 构造函数属性提升

PHP
<?php
// PHP 8.0+ 简化写法
class Car {
    public function __construct(
        public string $brand,
        public string $color,
        public int $year,
    ) {
        // 参数自动成为属性!不用再写 $this->brand = $brand
    }
}

$car = new Car("丰田", "白色", 2024);
echo $car->brand;  // 丰田 ✅
?>
▶ 试一试
💡 提示: 这是 PHP 8 的一大改进——构造函数属性提升让代码减少了一半。新项目都推荐用这种写法。


5. 访问修饰符

控制属性和方法的"可见性":

PHP
<?php
class BankAccount {
    public string $bank = "中国银行";   // 任何人可见、可改
    private float $balance = 0;        // 只有类内部可访问
    protected string $accountId;       // 只为内部+子类可访问
    
    public function __construct(
        private string $owner,
        private float $initialDeposit = 0.0
    ) {
        $this->balance = $initialDeposit;
    }
    
    // 通过公开方法安全地操作私有属性
    public function deposit(float $amount): void {
        if ($amount > 0) {
            $this->balance += $amount;
        }
    }
    
    public function getBalance(): float {
        return $this->balance;
    }
}

$account = new BankAccount("小明", 1000);
echo $account->bank;         // 中国银行 ✅ public 可访问
$account->deposit(500);       // ✅ public 方法可调用
echo $account->getBalance(); // 1500 ✅
// echo $account->balance;   // ❌ Error: private 属性不可直接访问
// $account->balance = 9999; // ❌ Error: 不能随便改余额
?>
修饰符 别名 访问范围
public 公开 任何地方都能访问
protected 受保护 当前类和子类内
private 私有 仅当前类内
💡 提示: 属性用 privateprotected,通过公开方法访问——这叫"封装"。它让你能控制数据的修改方式,比如 deposit() 能加金额校验。


6. 对象比较

PHP
<?php
class User {
    public function __construct(
        public string $name,
        public int $age
    ) {}
}

$u1 = new User("小明", 25);
$u2 = new User("小明", 25);
$u3 = $u1;

var_dump($u1 == $u2);   // true(属性值相同)
var_dump($u1 === $u2);  // false(不是同一个对象)
var_dump($u1 === $u3);  // true(指向同一个对象)
?>

7. 完整示例:用户类

▶ 示例:完整的 User 类

PHP
<?php
class User {
    private string $passwordHash;  // 密码哈希,绝对不暴露
    
    public function __construct(
        public string $username,
        public string $email,
        string $password,           // 普通参数(不是属性)
        public int $age = 0,
        public bool $isActive = true,
    ) {
        // 密码在构造时就加密
        $this->passwordHash = password_hash($password, PASSWORD_DEFAULT);
    }
    
    public function verifyPassword(string $password): bool {
        return password_verify($password, $this->passwordHash);
    }
    
    public function getDisplayName(): string {
        return $this->username . ($this->isActive ? '' : '(已停用)');
    }
    
    public function greet(): string {
        return "你好,{$this->username}!你的邮箱是 {$this->email}。";
    }
}

// 使用
$user = new User("小明", "xiaoming@example.com", "secret123", 25);

echo $user->greet();
echo "用户状态:" . $user->getDisplayName() . "<br>";

if ($user->verifyPassword("secret123")) {
    echo "密码正确!";
}
// echo $user->passwordHash; // ❌ Error: private 不可访问
?>
▶ 试一试

❓ 常见问题

Q 什么时候用类,什么时候用函数就够了?
A 当数据和操作这些数据的函数自然成组时,用类。比如"用户"有 name/age/email 属性和 login/logout 方法。如果只是几个无关的工具函数,普通函数就够。
Q $this 是什么?
A $this 是在对象方法中引用"当前正在操作的这个对象"。比如 $car1->start() 内部,$this 指的就是 $car1
Q 为什么不用 var 声明属性?
A PHP 4 时代的语法,已过时。现代 PHP 一律用 public/private/protected + 类型声明。

📖 小节

📝 作业

  1. 创建一个 Book 类,包含属性 title/author/price,构造函数初始化,方法 getInfo() 返回书籍信息。
  2. 创建一个 BankAccount 类,private 余额、public 存款和取款方法(取款不能超余额)、public 查看余额方法。创建几个账户测试操作。
  3. 使用 PHP 8 构造函数属性提升重写第一题,看看少了多少行代码。
Web-Tutorial.com

Web-Tutorial 技术团队

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

100%

🙏 帮我们做得更好

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

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