12月23日 こうしろうは現在,Beginning「PHP5」でオブジェクト指向プログラミングの勉強をしている。この日,取り組んだのは__getと__setというメソッドである(アンダーバー2個に続けて,getもしくはset)。こうしろうは長いサンプルプログラムを打ち込み動作を確認したが,前提を知らないと理解しにくい機能なので,そこから説明を始めたい。

----------------------------------------------------------

<?php
 class Hoge {
  public $a;
 }
 $obj = new Hoge;
 $obj->a = '123';
 print $obj->a;
 $obj->b = '456';
 print $obj->b;
?>

----------------------------------------------------------
 Hogeというクラスを作り,$aというpublicなプロパティを宣言しているので,$obj->a = '123';と値を設定することができる。print $obj->a;と出力すれば当然,123と表示される。ここまでは何の迷いもない。

 次に,$obj->b = '456';と456という値をbというプロパティに設定している。しかし,Hogeクラスにはbというプロパティはない。しかし,このコードはエラーにはならず次のprint文で456と出力される。

 PHPの場合,存在しないプロパティの値を入れようとすると,プロパティがその時点で生成される。オブジェクトはまるで,そんなプロパティが最初から存在したかのように振舞うのだ。

 PHP5からは存在しないプロパティに値を代入したり,値を参照しようとしたときに, __set(),__get()というメソッドを用意しておくと,それが自動的に呼び出されるようになった。
----------------------------------------------------------

<?php
 class Hoge {
  public $a;
  function __set($name, $value) {
   $this->a = $value;
  }
 }
 $obj = new Hoge;
 $obj->a = '123';
 print $obj->a;
 $obj->b = '456';
 print $obj->a;
?> 

----------------------------------------------------------
 たとえば,このサンプルプログラムのHogeクラスには$aというプロパティが1つあるだけだが,__setメソッドの中で値($value)を$aプロパティに代入しているので,どんな名前のプロパティを指定して値を代入しても$aに値が入っていく(__setの第一引数にはプロパティの名前,第二引数には値が渡される)。

 実行結果は,最初のサンプルプログラムと同じだが,このプログラムでは$bというプロパティは作成されない。

 次に,存在しないプロパティを参照した場合に働く__getメソッドの動作を確認してみる。
----------------------------------------------------------

<?php
 class Hoge {
  function __get($name) {
   print "__get() is called with ($name) <br> \n";
   }
  }
 $obj = new Hoge;
 print $obj->b;
?>

----------------------------------------------------------
 print $obj->b;とHogeクラスに存在しないプロパティ$bを参照しようとすると,__getメソッドが実行される。

 さて,これで__set,__getメソッドの使い方はわかったが,はたして何に役立つのだろうか。確かに宣言していないプロパティが勝手に作成されたり,値を参照されたりするよりも__set,__getメソッドで,うまくさばけるのは便利だと思うが,存在しないプロパティへのアクセスをエラーにせず,対応できるようにする狙いはどこにあるのか。

 PHPで処理を作成するときは,頻繁に配列,それも添え字の配列ではなく連想配列を使うことが多い。たとえば,データベースから取り出したレコードもフィールド名をキーとする連想配列として扱うことが多い。

 クラスのメンバーとして配列を宣言し,名前で判断し値をセットしたり,返したりという使い方が考えられる。
----------------------------------------------------------

<?php
 class Hoge {
  public $properties = array(
   'fname' => null,
   'age' => null
  );
  function __set($name, $value) {
   if(array_key_exists($name, $this->properties)) {
     $this->properties[$name] = $value;
   }
  }
  function __get($name) {
   if(array_key_exists($name, $this->properties)) {
     return $this->properties[$name];
   };
  }
  }
 $obj = new Hoge;
 $obj->fname = 'Kazu';
 $obj->age = 13;

 print $obj->fname;
 print $obj->age;
?>

----------------------------------------------------------
 Hogeクラスには$propertiesというプロパティがあり,連想配列として宣言されている。fname,ageがキーで,初期値はいずれもNULL値になっている。この連想配列に値を代入しようとすると,__setメソッドの中のarray_key_exists関数で同名のキーが配列中に存在するかどうかをチェックした上で,値を代入する。値を参照にきた場合も__getメソッドがはたらき,キーが一致すれば値を返す。