PHP 面向对象

项目大了之后,面向过程(一堆函数 + 关联数组)会越来越难维护。面向对象编程(OOP)用"类"和"对象"组织代码,把数据和行为绑在一起,大幅提升代码复用性、可测试性、可维护性。现代 PHP 框架(Laravel、Symfony)全部基于 OOP。

1. 类与对象:基本概念

<?php
// 定义类
class User {
    // 属性(支持类型声明和默认值)
    public string $name;
    public int $age;
    private string $email;     // 私有:仅类内部可访问

    // 构造函数(__construct 是魔术方法)
    public function __construct(string $name, int $age) {
        $this->name = $name;   // $this 指当前对象
        $this->age = $age;
    }

    // 方法
    public function greet(): string {
        return "你好,我是 {$this->name}";
    }
}

// 实例化(new)
$user = new User("小明", 25);
echo $user->name;              // 小明(访问 public 属性)
echo $user->greet();           // 你好,我是小明
// echo $user->email;          // 报错:private 不可外部访问

2. 可见性:public / private / protected

可见性控制"谁能访问",是封装的核心:

<?php
class BankAccount {
    public  string $owner;     // 公开:任何地方都能访问
    private float $balance;    // 私有:仅本类内部
    protected int $id;         // 受保护:本类 + 子类

    public function __construct(string $owner, float $balance = 0) {
        $this->owner = $owner;
        $this->balance = $balance;
    }

    // getter / setter:控制对 private 属性的访问
    public function getBalance(): float {
        return $this->balance;
    }

    public function deposit(float $amount): void {
        if ($amount <= 0) {
            throw new InvalidArgumentException("金额必须为正");
        }
        $this->balance += $amount;
    }

    // private 方法:仅类内部使用
    private function log(string $msg): void {
        error_log("[{$this->owner}] {$msg}");
    }
}

$acc = new BankAccount("小明", 100);
echo $acc->owner;              // 小明(public)
// echo $acc->balance;         // 报错(private)
echo $acc->getBalance();       // 100(通过方法访问)
$acc->deposit(50);
echo $acc->getBalance();       // 150

最佳实践:属性默认 privateprotected,通过 getter/setter 暴露——这样你能在 setter 里加校验(如金额必须为正)、能在 getter 里加日志。这是封装的核心价值。

3. 继承:extends

继承让子类复用父类的代码,并扩展或重写:

<?php
// 父类
class Animal {
    protected string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }

    public function eat(): string {
        return "{$this->name} 在吃东西";
    }
}

// 子类用 extends 继承
class Dog extends Animal {
    public function bark(): string {
        return "{$this->name}: 汪汪!";   // 可以访问 protected $name
    }

    // 重写父类方法
    public function eat(): string {
        return parent::eat() . "(狗粮)";  // parent:: 调用父类方法
    }
}

$dog = new Dog("旺财");
echo $dog->eat();             // 旺财 在吃东西(狗粮)
echo $dog->bark();            // 旺财: 汪汪!

几个要点:

4. 静态成员与常量

static 成员属于类本身,不是某个对象——所有实例共享。常量(const)则是不可修改的固定值:

<?php
class Counter {
    // static 属性:属于类本身,所有实例共享
    private static int $count = 0;

    // 常量(const):不可修改
    public const MAX = 100;

    public function __construct() {
        self::$count++;       // self:: 引用本类的 static
    }

    // static 方法:无需实例化就能调用
    public static function getCount(): int {
        return self::$count;
    }
}

new Counter();
new Counter();
echo Counter::getCount();     // 2(类名::方法)
echo Counter::MAX;            // 100(类名::常量)

// self vs static(后期静态绑定)
// self:: 引用定义时的类
// static:: 引用调用时的类(用于多态)

关键概念:

5. 接口与抽象类

接口和抽象类都是定义"契约"的工具,用于解耦和实现多态:

<?php
// 接口:定义"能做什么",不实现
interface Swimmer {
    public function swim(): string;   // 只有签名,无实现
}

interface Flyer {
    public function fly(): string;
}

// 抽象类:不能实例化,只能被继承
abstract class Vehicle {
    abstract protected function maxSpeed(): int;  // 子类必须实现

    public function info(): string {
        return "最高时速:" . $this->maxSpeed();
    }
}

// 一个类可以同时继承 + 实现多个接口
class FlyingCar extends Vehicle implements Swimmer, Flyer {
    protected function maxSpeed(): int { return 300; }

    public function swim(): string { return "水上漂"; }
    public function fly(): string { return "飞上天"; }
}

$car = new FlyingCar();
echo $car->info();            // 最高时速:300
echo $car->fly();             // 飞上天

对比:

6. PHP 8 新特性

<?php
class Article {
    public function __construct(
        public string $title,        // 构造函数属性提升(PHP 8+)
        public string $content,
    ) {}

    // __get:访问不存在/私有的属性时调用
    public function __get(string $name): mixed {
        if ($name === "summary") {
            return mb_substr($this->content, 0, 50);
        }
        return null;
    }

    // __toString:对象被当字符串使用时调用
    public function __toString(): string {
        return $this->title;
    }

    // __clone:对象被 clone 时调用
    public function __clone(): void {
        // 深拷贝内部对象
    }
}

$art = new Article("标题", "内容...");
echo $art->summary;           // 调用 __get
echo $art;                    // 调用 __toString,输出 "标题"

7. 魔术方法

PHP 内置一组双下划线开头的魔术方法,在特定时机自动调用:

8. 命名空间与自动加载

真实项目用 namespace 避免类名冲突,用PSR-4 自动加载自动加载类文件:

小结

这一章你掌握了 PHP OOP 的核心:类与对象、可见性、继承、static、接口/抽象类、PHP 8 新特性、魔术方法、命名空间。下一篇PDO 数据库——把数据存进 MySQL。

← 上一篇 PHP 会话管理

下一篇 PHP 数据库(PDO)

✈️💬