← PHP 抽象类与接口:两个“模板”的较量 PHP 访问修饰符:public、private、protected 的权限边界 →

PHP 封装:把数据和方法“打包”的艺术

原创 2026-05-12 PHP 已有人查阅

封装(Encapsulation)是面向对象编程的四大基石之一。在PHP里,封装的核心思想可以用一句话概括:把数据(属性)和操作这些数据的方法捆绑在一起,形成一个独立的单元——对象,同时对外隐藏内部的具体实现细节。

这样做的好处很实在:外界只能通过你暴来的公开方法来跟对象打交道,而不能直接伸手去修改内部的数据。这让你可以随时调整类的内部实现,而不影响整个系统的其他部分。PHP 通过三个访问控制关键词——publicprivateprotected——来实现封装。

三个访问修饰符:封装的语法支柱

理解这三个关键词的权限范围,是掌握封装的前提。它们就像是三道不同级别的门禁:

  • public(公开的):没有任何限制。类内、类外、子类,任何地方都能访问。PHP 中类成员默认就是 public

  • private(私有的):较高级别的隔离。只有定义它的那个类自己可以访问,子类也碰不到。

  • protected(受保护的):介于两者之间。类内部和其子类可以访问,但类的外部不行。

我个人的一个习惯是:写新类时先把所有属性都设为 private,只有当明确需要子类继承访问时才改为 protected。这个“默认小权限”的原则帮我避免了很多意料之外的外部篡改。

Public:毫无保留的开放

当一个类的成员被标记为 public 时,就相当于敞开了大门。下面这个 Book 类,属性和方法全是公开的:

class Book {
    public $title;
    public $price;

    public function __construct(string $title = "PHP Basics", int $price = 380) {
        $this->title = $title;
        $this->price = $price;
    }

    public function getPrice() {
        echo "Price: $this->price\n";
    }

    public function getTitle() {
        echo "Title: $this->title\n";
    }
}

$book1 = new Book();
echo "Title: {$book1->title} | Price: {$book1->price}";

输出:

Title: PHP Basics | Price: 380

这里你既可以通过 $book1->getTitle() 方法获取书名,也能直接 $book1->title 拿到值。全公开在写一些小工具或临时脚本时确实方便,但在正式项目里,这种写法几乎等于放弃了封装带来的所有保护——任何一段外部代码都可以随意修改对象的状态,排查问题时你会很难定位是谁改了数据。

Private:把数据锁在类内部

封装的正统实践,是把数据成员设为 private,然后提供公开的方法来作为读写入口。这样外界就无法绕过你设定的逻辑直接触碰数据:

class Book {
    private $title;
    private $price;

    public function __construct(string $title = "PHP Basics", int $price = 380) {
        $this->title = $title;
        $this->price = $price;
    }

    public function getTitle() {
        echo "Title: $this->title\n";
    }

    public function getPrice() {
        echo "Price: $this->price\n";
    }
}

$book1 = new Book();
$book1->getTitle();
$book1->getPrice();

// 下面这行会导致致命错误
// echo "Title: $book1->title Price: $book1->price";

输出:

Title: PHP Basics
Price: 380

如果取消注释之后一行,你会得到一个 Fatal error,因为 $title 和 $price 是 private 的,外部根本无法直接触及。这种做法的好处在于:未来你可以在 getPrice() 里加上折扣计算、货币格式转换等逻辑,调用方一行代码都不用改。相比全 public 的写法,private 加公开方法的组合赋予了类对自己内部数据的绝对控制权。

Protected:为继承留一道门

protected 的权限粒度正好在 public 和 private 之间——外部代码不能访问,但子类可以。这在你设计一个有继承体系的类族时特别有用。

先定义一个 Publication 类,包含一个 private 的 $cost 和一个 protected 的 $name

class Publication {
    private $cost;
    protected $name;

    public function __construct(string $title = "Learning PHP", int $amount = 450) {
        $this->name = $title;
        $this->cost = $amount;
    }

    public function showCost() {
        echo "Cost: $this->cost <br/>";
    }

    public function showName() {
        echo "Name: $this->name <br/>";
    }
}

$pub = new Publication();
$pub->showName();
$pub->showCost();

输出:

Name: Learning PHP
Cost: 450

同一个类自己的公开方法访问 private 和 protected 属性都是没问题的。接下来让子类试试:

class CustomPublication extends Publication {
    public function getInternalName() {
        return $this->name;  // 可以访问,因为 $name 是 protected
    }
}

$childPub = new CustomPublication();
echo $childPub->getInternalName();  // 正常输出: Learning PHP

但如果把 CustomPublication 改成不继承 Publication 的独立类,然后尝试从外部访问 protected 成员,就会触发错误:

class InfoFetcher {
    public function showLabel($item) {
        echo "Label: $item->label <br/>";
    }
}

$fetcher = new InfoFetcher();
$pubObj = new Publication();
$fetcher->showLabel($pubObj);  // 报错:不能访问 protected 属性

再看一个完整的错误示例:

class CustomBook {
    public function displayTitle($pub) {
        echo "Book Name: $pub->name <br/>";
    }
}

$bookObj = new CustomBook();
$pubObj = new Publication();
$bookObj->displayTitle($pubObj);

输出:

Fatal error: Uncaught Error: Cannot access protected property Publication::$name

这个错误清晰地说明了 protected 的边界:CustomBook 不是 Publication 的子类,它站在外部代码的位置,无权触碰 $name。这也正是为什么不直接用 public 的原因——protected 给了子类通道,却没有对外敞开,在灵活性和安全性之间取了一个平衡点。

封装的价值不止于“隐藏”

很多人会把封装单纯理解为“把属性藏起来”,但它的真正价值在于 “控制访问通道” 。一旦你通过公开方法统一了数据的进出入口,你就可以在这些方法里添加:

  • 数据校验:确保传入的值符合业务规则

  • 格式转换:内部用一种格式存,对外输出另一种格式

  • 日志记录:追踪数据变更

  • 延迟加载:方法被调用时才初始化数据

这些都是直接暴露 public 属性绝对做不到的。

在开发中,我发现封装带来的另一个隐性好处是团队协作效率的提升。当每个人都清楚某个类的公开方法是数据操作的唯一合法入口时,代码审查和问题排查的范围就大幅缩小了——你不需要在代码库的每个角落去寻找可能的属性修改点。

本节课程知识要点

  • 封装是把数据和操作数据的方法捆绑成对象,对外隐藏内部实现。

  • public 成员在任何地方都能访问,PHP 类成员默认即为 public

  • private 成员仅有定义它的类自身可以访问,子类也无法触及。

  • protected 成员在类内部和子类中可访问,外部代码不可访问,是继承场景下的折中方案。

  • 养成“属性设 private、通过公开方法开放访问”的习惯,有助于构建可维护、可扩展的系统。

← PHP 抽象类与接口:两个“模板”的较量 PHP 访问修饰符:public、private、protected 的权限边界 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号