クラスのマジックメソッド __set, __get は、存在しないクラスプロパティや private, protected 等で保護されたプロパティにアクセスする際に自動的に呼び出されます。(ゲッターメソッド/セッターメソッド)
これを使うことでプロパティを取得する際に値を加工して返したり、セットする値が正しいかどうかを確かめたりすることができます。また、private プロパティに読み取りのための __get 処理を用意すると読み取り専用のような振る舞いをさせることができます。
下記のサンプルはクラスに幅(width)と高さ(height)を渡し、面積(area)を返すものと、読み取り専用のプロパティの値を変更しようと試みるテストです。
<?php class Sample { private $width; private $height; private $readonly = 'test'; function __get($name){ if($name === 'area'){ return $this->width * $this->height; } else if($name === 'readonly'){ return $this->readonly; } } function __set($name, $value){ if($name === 'readonly'){ throw new Exception('Attempted to assign to readonly property'); } else if( $name === 'width' || $name === 'height' ){ if(!is_numeric($value)){ throw new Exception('Invalid value'); } $this->{$name} = $value; } } }
・面積の計算
$sample = new Sample(); $sample->width = 30; $sample->height = 20; echo $sample->area;
結果: 600
インスタンス $sample に幅と高さの値をセットしています。
width と height は private であるため、通常は値の読み書きはできませんが、__set() を用意しているのでそちらを通して値をセットできます。
面積を示すプロパティ area は宣言されていませんが、__get() の中で処理を用意することで存在するプロパティであるかのように動作します。これは getArea() のような専用の関数を用意するのと同じ役割です。
幅と高さを指定する際、数字以外の値が渡されることの無いよう is_numeric() で簡易チェックをしています。これにより「abc」のような文字列をセットしようとしても例外が発生します。
・読み取り専用プロパティへのアクセス
$sample = new Sample(); try { echo $sample->readonly; // 「test」 $sample->readonly = 'hello'; // 例外が発生 } catch(Exception $e){ echo $e->getMessage(); }
プロパティ readonly も private なのでそのままでは読み書きできません。
__get()、__set() を両方用意すれば読み書きが可能になりますが、__set() にセットを禁止する処理を加えているので書き込もうとすると例外が発生します。