PHP 類別裡的屬性與方法,可以用 public、private、protected 控制「誰能存取」。搜尋 php protected vs private 的人,通常卡在:子類別到底能不能碰?外部能不能碰? 這篇用對照表直接回答,再帶你理解封裝原則與實務選擇。
三欄速查對照表
| 修飾符 | 類別內 | 子類別 | 類別外 |
|---|---|---|---|
public | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ |
private | ✅ | ❌ | ❌ |
| 修飾符 | 典型用途 |
|---|---|
public | 對外 API 方法、確定可公開的屬性 |
protected | 留給子類別覆寫或重用的 hook |
private | 類別內部實作細節,連子類別也不直接碰 |
延伸:PHP 類別與物件入門 · PHP 新手入門
目錄
protected vs private:差在哪?
這是搜尋這個主題時最常問的問題。一句話區分:
private:只有宣告它的那個類別能存取。子類別不能直接讀寫父類別的private成員。protected:宣告它的類別和子類別都能存取,但類別外不能直接碰。
對照範例
<?php
class Base
{
private string $secret = '只有 Base 自己知道';
protected string $family = '子類別可以讀';
public function reveal(): string
{
return $this->secret . ' / ' . $this->family;
}
}
class Child extends Base
{
public function check(): void
{
// echo $this->secret; // 錯誤:不能存取父類別的 private
echo $this->family; // OK:protected 子類別可存取
}
}
$child = new Child();
$child->check(); // OK
// echo $child->family; // 錯誤:類別外不能存取 protected
什麼時候用 protected、什麼時候用 private?
| 情境 | 建議 | 原因 |
|---|---|---|
| 只有這個類別內部用的輔助方法 | private | 子類別不需要知道實作細節 |
| 子類別需要覆寫或重用父類別邏輯 | protected | 提供擴充點,但不對外開放 |
| 不確定要不要給子類別用 | 先 private | 需要時再改成 protected,比反過來安全 |
| 確定是對外契約的一部分 | public 方法 | 不要直接公開屬性,除非資料真的無需驗證 |
protected 會把父類別的內部細節暴露給所有子類別;繼承層次變深時,修改 protected 成員可能牽動多處。若某方法只屬於父類別自己的實作,用 private 往往更安全。
public:公開給外部使用
標記為 public 的屬性或方法,可以在類別外直接使用:
<?php
class CoffeeOrder
{
public string $size = 'medium';
public function summary(): string
{
return "一杯 {$this->size} 咖啡";
}
}
$order = new CoffeeOrder();
$order->size = 'large';
echo $order->summary(); // 一杯 large 咖啡
外部可以直接修改 $size,程式碼短、上手快。但公開屬性等於把修改權也交出去;若未來要限制尺寸只能是 small、medium 或 large,就會發現大家早已習慣直接改它。
實務上,通常讓屬性保持 private 或 protected,再透過 public 方法提供有意義的操作。
private:把實作細節留在類別內
<?php
class Person
{
private int $age = 0;
public function setAge(int $age): void
{
if ($age < 0) {
throw new InvalidArgumentException('年齡不能是負數');
}
$this->age = $age;
}
public function getAge(): int
{
return $this->age;
}
}
$person = new Person();
$person->setAge(25);
echo $person->getAge(); // 25
所有修改年齡的規則都集中在類別內。未來若要加入上限或記錄異動,外部呼叫程式不必跟著知道內部怎麼做。
protected:讓子類別共享
<?php
class Report
{
protected function formatTitle(string $title): string
{
return "【報表】{$title}";
}
}
class SalesReport extends Report
{
public function render(): string
{
return $this->formatTitle('本月銷售');
}
}
$report = new SalesReport();
echo $report->render(); // 【報表】本月銷售
// $report->formatTitle('測試'); // 錯誤:類別外不能直接呼叫 protected 方法
SalesReport 可以使用父類別的 protected 方法,但外部只能呼叫它公開的 render()。
三種修飾符怎麼選?決策流程
- 這是使用者需要呼叫的功能嗎?→ 考慮
public方法(盡量不要公開屬性)。 - 這是類別內部的資料或輔助方法嗎?→ 優先
private。 - 子類別明確需要重用或覆寫嗎?→ 才考慮
protected。
覆寫時的可見性規則
子類別可以用相容或更寬鬆的可見性覆寫父類別方法,但不能把繼承而來的公開方法改得更不公開。例如,不能把 public 覆寫成 protected。
封裝:公開行為、隱藏狀態
封裝(Encapsulation)的目的,是把資料與操作資料的規則放在一起,並控制外部能依賴的介面:
<?php
class Wallet
{
private int $balance = 0;
public function deposit(int $amount): void
{
if ($amount <= 0) {
throw new InvalidArgumentException('存入金額必須大於零');
}
$this->balance += $amount;
}
public function balance(): int
{
return $this->balance;
}
}
外部只能透過 deposit() 增加餘額,不能直接把餘額改成負數。這種公開「行為」、隱藏「狀態」的設計,通常比單純公開欄位更能守住規則。
常見問題
Q:protected 和 private 最大的差別?
A:protected 子類別可存取;private 只有宣告它的類別可存取,子類別也不行。
Q:屬性一定要寫 getter/setter 嗎?
A:不必機械式地為每個欄位都寫。若欄位需要驗證或之後可能改實作,用 private + 公開方法;若真的是單純、不會變的公開資料,偶爾用 public 屬性也可以接受。
Q:PHP 有 package-private 嗎?
A:沒有。PHP 只有 public、protected、private 三種。
結語
public、protected、private 的差異,可以濃縮成一句話:先決定誰需要知道,再決定誰可以使用。
把真正要給外部呼叫的功能設為 public,把內部實作優先留在 private,只有在繼承設計確實需要時才使用 protected。接下來閱讀 PHP 繼承與介面時,你會更容易看懂每扇門到底是為誰開的。
