PHPではprivateと宣言したプロパティ、メソッドは、同じクラスのインスタンスであれば相互にアクセスできます。あまり意識することはないですが、たまにぎょっとすることになります。
<?php class Klass { private $data; function __construct($init) { $this->data = $init; } function get(self $o) { return $o->data; } } $a = new Klass('a instance'); $b = new Klass('b instance'); //echo $a->data; //これはエラーになる echo $b->get($a); //a instance //$bが$aのprivateメンバを読めた
これはprotectedの場合でも同様であり、しかも継承したクラスにまで影響を及ぼすため、これによってパッケージメンバみたいなものを作ることができます。
パッケージメンバという単語ですが、この記事では「あるライブラリの内部クラスだけが直接アクセスでき、ライブラリの外からはアクセスできないメンバー変数」という意味合いで使っています。
オブジェクト指向としては、継承を変な方向に使うのでダメなパターンだと思いますが、面白いので紹介してみようと思います。
ユースケース
Aクラスはprivate変数$aを持っているとしましょう。$aはAクラスの内部だけで使うことを想定しています。
BクラスはAクラスのインスタンスを使う側だとします。さて、Bクラスが$aにアクセスするにはどうすればいいでしょうか?
class A { private $a; //... } class B { function getA(A $obj) { //echo $obj->a; //Aクラスのaメンバにアクセスしたい!! } }
当然、このままでは$aにアクセスできません。そこで普通はgetterを作って、読み出しだけならできる状態にして、Bクラスからアクセスできるようにします。
class A { private $a; function getA() { return $this->a; } //... } class B { function getA(A $obj) { echo $obj->getA(); //Aクラスのaメンバにアクセスできた } }
しかし、「Bクラス限定で公開すれば十分」なのに、getterはpublicなのでありとあらゆる場所からアクセスできてしまいます。権限を広げすぎているのです。
普通は、まあ権限を広げすぎといっても別に使わなければいいし、このままにすることが多いと思いますが、protectedを使うともっと厳密に公開先を限定することができます。
protectedによる変数の相互アクセス
基底クラスを一つ作り、基底クラスにprotectedメンバを作っておくだけです。
先の例ですと、AとBの共通基底クラスとしてBaseクラスを作り、$aはBaseクラスのprotectedメンバとして宣言しておきます。こうすると、Bクラスから$aにアクセスできるようになります。
class Base { protected $a; } class A extends Base { } class B extends Base { function getA(A $obj) { echo $obj->a; //アクセス可能 } }
protectedは継承関係にあるクラスで相互にアクセス可能なので、逆にライブラリを構成するクラスを全て継承関係にしてしまえば、protectedがパッケージメンバ相当になる、ということです。(わかりにくいな。。。)
継承の頂点で宣言されたprotectedメンバはこの三角形の範囲に引き継がれ、しかも相互にアクセス可能になります。継承の関係にないクラスや文脈ではprotectedメンバはアクセスできないため、公開範囲を絞ることができます。

図のように、子クラスで宣言されたprotectedは、その子クラスとその更に子クラスでのみ相互アクセス可能であり、三角形の範囲外からはアクセスできません。これを利用すると、サブパッケージのようなものを作ることも可能です。
考察
これでパッケージメンバを作ることはできるのですが、継承を消費せざるを得ないので、実際にこれが使える場面は少ないかもしれません。あと、なぜgetterなしでアクセスできるのかがわかりにくいし。
keyword: OOP