PHP 類別裡的屬性與方法,可以用 publicprivateprotected 控制「誰能存取」。搜尋 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,程式碼短、上手快。但公開屬性等於把修改權也交出去;若未來要限制尺寸只能是 smallmediumlarge,就會發現大家早已習慣直接改它。

實務上,通常讓屬性保持 privateprotected,再透過 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()

三種修飾符怎麼選?決策流程

  1. 這是使用者需要呼叫的功能嗎?→ 考慮 public 方法(盡量不要公開屬性)。
  2. 這是類別內部的資料或輔助方法嗎?→ 優先 private
  3. 子類別明確需要重用或覆寫嗎?→ 才考慮 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 只有 publicprotectedprivate 三種。

結語

publicprotectedprivate 的差異,可以濃縮成一句話:先決定誰需要知道,再決定誰可以使用。

把真正要給外部呼叫的功能設為 public,把內部實作優先留在 private,只有在繼承設計確實需要時才使用 protected。接下來閱讀 PHP 繼承與介面時,你會更容易看懂每扇門到底是為誰開的。

參考資料