こんにちは、かつコーチです。
前回は継承とインターフェースについて解説しました。
ここまでの記事のコード例では、プロパティやメソッドにさりげなく public というキーワードを付けてきました。
今回は、この public を含めたアクセス修飾子(public / protected / private)について、しっかり整理していきます。
「なんとなくpublicを付けておけば動くから」で済ませている人も多いテーマですが、正しく使い分けられると、コードの安全性が大きく変わります。
アクセス修飾子とは?
なぜアクセスを制限する必要があるのか
アクセス修飾子とは、クラスのプロパティやメソッドに対して「どこからアクセスできるか」を制限するキーワードです。
「制限」と聞くと不便に感じるかもしれませんが、これは「不正な使われ方を防ぐための安全装置」だと考えてください。
たとえば銀行口座を表すクラスを考えてみます。
❌ Before:すべて public にしてしまう
<?php
class BankAccount
{
public int $balance = 0;
public function deposit(int $amount): void
{
$this->balance += $amount;
}
}
$account = new BankAccount();
$account->deposit(1000);
// balanceがpublicなので、外部から直接書き換えられてしまう
$account->balance = 1000000; // 入金処理を通さずに残高を操作できてしまう
echo $account->balance; // 1000000
?>
$balance が public になっていると、deposit() メソッドを通さずに残高を直接書き換えられてしまいます。
これでは「入金処理を必ず通す」というルールを、クラス自身が守れません。
3つのアクセス修飾子
public:どこからでもアクセス可能
public を付けたプロパティ・メソッドは、クラスの外からも自由にアクセスできます。
外部に公開したい「窓口」の役割を持つメソッドに使います。
private:そのクラスの中だけ
private を付けたプロパティ・メソッドは、そのクラスの内部からしかアクセスできません。
継承した子クラスからもアクセスできない、もっとも厳しい制限です。
protected:そのクラスと子クラスの中だけ
protected を付けたプロパティ・メソッドは、そのクラス自身に加えて、継承した子クラスからもアクセスできます。
クラスの外部からはアクセスできません。
比較表で整理する
| 修飾子 | 同じクラスの中 | 子クラスの中 | クラスの外 |
|---|---|---|---|
public | ○ | ○ | ○ |
protected | ○ | ○ | ✕ |
private | ○ | ✕ | ✕ |
private で内部データを守る
getterメソッドで安全に値を取り出す
先ほどの BankAccount を、private を使って安全に書き直してみます。
✅ After:private にして、専用のメソッド経由でしか操作できないようにする
<?php
class BankAccount
{
private int $balance = 0;
public function deposit(int $amount): void
{
if ($amount <= 0) {
throw new InvalidArgumentException('入金額は1円以上を指定してください');
}
$this->balance += $amount;
}
public function getBalance(): int
{
return $this->balance;
}
}
$account = new BankAccount();
$account->deposit(1000);
echo $account->getBalance(); // 1000
// $account->balance = 1000000; // Error: Cannot access private property
?>
$balance を private にしたことで、外部からは deposit() を通してしか残高を増やせなくなりました。
さらに deposit() の中で「0円以下の入金を拒否する」というルールを追加できるので、不正な値が入り込む余地がなくなります。
値を読み取りたい場合は、getBalance() のようなgetterメソッドを用意して、読み取り専用の窓口を作るのが定石です。
protected で継承を前提にした設計をする
子クラスだけに使わせたいデータ
protected は、「外部には見せたくないが、子クラスには使わせたい」というときに使います。
<?php
class Animal
{
protected string $name;
public function __construct(string $name)
{
$this->name = $name;
}
}
class Dog extends Animal
{
public function bark(): string
{
// protectedなので子クラスからは$nameにアクセスできる
return $this->name . 'がワンワン吠えた';
}
}
$dog = new Dog('ポチ');
echo $dog->bark(); // ポチがワンワン吠えた
// echo $dog->name; // Error: Cannot access protected property
?>
$name を protected にしたことで、Dog クラスの中からは自由に使えますが、クラスの外からは $dog->name のように直接アクセスできません。
「継承する前提のクラスなら protected、継承しない前提なら private」というのが、最初の判断基準として分かりやすいです。
私がつまずいたポイント:とりあえず public にする癖
正直に告白すると、私はオブジェクト指向を学び始めたころ、「エラーが出ると面倒だから」という理由だけで、プロパティにはとりあえず全部 public を付けていた時期があります。
あるとき、複数人で開発しているプロジェクトのコードを触っていて、$user->status という public プロパティが、あちこちのファイルから直接書き換えられていることに気づきました。
active という文字列しか入るはずのない $status に、どこかのコードから actve(タイプミス)という値が代入されており、原因を探すのに半日近くかかったことがあります。
もし $status が private になっていて、専用のメソッド(例:activate())経由でしか変更できないようになっていれば、そもそもタイプミスの文字列が入り込む余地はありませんでした。
このときから、「まず private にしてみて、本当に外部公開が必要なメソッドだけを public にする」という順番でコードを書くようになりました。
慣れないうちは面倒に感じるかもしれませんが、この習慣がバグを未然に防いでくれます。
実務での判断基準
迷ったときのチェックリスト
アクセス修飾子を選ぶときは、次の順番で考えるとスムーズです。
- 外部から直接読み書きされて困るデータか? →
privateを検討する - 子クラスで再利用・上書きする可能性があるか? →
protectedを検討する - クラスの「窓口」として外部から使ってほしいメソッドか? →
publicにする
迷ったら、まず一番厳しい private から始めて、必要になった時点で protected や public に緩める、という順番がおすすめです。
最初から public にしてしまうと、後から「やっぱり制限したい」と気づいても、すでに外部の色々なコードから直接アクセスされてしまっていて、修正が大掛かりになりがちです。
Laravelとの接点
Laravelのモデルクラスでも、$fillable(一括代入を許可する項目)や $hidden(JSON変換時に隠す項目)といったプロパティは protected で定義されています。
これは「Laravelの内部やモデル自身は扱えるが、外部から不用意に書き換えさせたくない」という、まさに今回学んだ考え方そのものです。
アクセス修飾子の感覚を持っておくと、Laravelのソースコードを読んだときに「なぜここが protected なんだろう」と設計意図まで読み取れるようになります。
まとめ
この記事のポイント
publicはどこからでも、protectedはクラスと子クラスから、privateはそのクラスの中だけからアクセス可能- プロパティを
privateにし、getterメソッド経由で値を公開すると、不正な書き換えを防げる protectedは、継承を前提にしたクラス設計で「子クラスにだけ使わせたいデータ」に使う- 迷ったらまず
privateから始め、必要になった時点で緩めるのが安全 - Laravelのモデルでも
protectedが多用されており、外部からの不用意な書き換えを防ぐ設計思想が共通している
次に読むべき記事
→ 次の記事:PHPの静的メソッド・静的プロパティ(static)とは