◆ __get, __set を使って外からは readonly な getter のみプロパティつくる

最近機能が増えてきましたが まだまだ PHP は不便なところはあります
static プロパティの初期化がクラス定義の外でしないといけなかったり読み取り専用のプロパティがなかったり

言語に機能がない以上完全な readonly プロパティは無理そうですが とりあえず外側からは変更できない getter のみプロパティを作ってみようと思います

PHP にはプロパティ単位で getter/setter を付ける機能はなくてオブジェクト全体につけることになります
JavaScript の Proxy の get みたいな機能です

class C
{
private $_value = 1;

public function __get($name)
{
if($name === 'value') return $this->_value;
}
}

$c = new C();
echo $c->value, PHP_EOL;
// 1
$c->value = 100;
echo $c->value, PHP_EOL;
// 100

PHP だとプロパティを宣言していなくても好きに付け足せるので これだけだとダメです
代入時にプロパティが作られてそれ以降は getter ではなくプロパティが読み取られます

setter をつけてそれを無効にします
class C
{
private $_value = 1;

public function __get($name)
{
if($name === 'value') return $this->_value;
}

public function __set($name, $value)
{
if($name === 'value') return;
$this->$name = $value;
}
}

$c = new C();
echo $c->value, PHP_EOL;
// 1
$c->value = 100;
echo $c->value, PHP_EOL;
// 1

setter ではなにもしないようにしています
エラーもなく更新は行われない JavaScript 風な動きが嫌なら throw すれば例外を起こせます


同じことしてる人はいそうと思ってネットで見てみると 単純に $this->$name の getter をつけるだけで setter は同名の private プロパティがいるのでなくてもいいというつくりでした
class C
{
private $value = 1;

public function __get($name)
{
if($name === 'value') return $this->value;
}
}

$c = new C();
echo $c->value, PHP_EOL;
// 1
$c->value = 100;
// Fatal error: Uncaught Error: Cannot access private property
echo $c->value, PHP_EOL;


これまでのは value を指定していましたが readonly にしたいプロパティがいっぱいあると 全部書いていくのは大変です
命名規則で自動でするようにしたいのですが あくまでそれは private の実体で 外から使うプロパティ名に特別なことはしたくないです
readonly だから __ から始まる みたいのはクラス使う側から特にメリットもないですし クラス内にとどめて置けば命名規則を変えたくなっても使ってる側には影響しません

ネットで見かけた方法では同じ名前を使うことで setter を省くので getter / setter 両方使う方法で対応します
class C
{
private $__value = 1;

public function __get($name)
{
$private_name = '__' . $name;
if(property_exists($this, $private_name)){
return $this->$private_name;
}
}

public function __set($name, $value)
{
$private_name = '__' . $name;
if(!property_exists($this, $private_name)){
$this->$name = $value;
}
}
}

$c = new C();
echo $c->value, PHP_EOL;
// 1
$c->value = 100;
echo $c->value, PHP_EOL;
// 1

これだと __ から始めたプロパティは外からは readonly な getter のみプロパティになります