PHPでは __(アンダースコア2つ)で始まる名前の関数(またはメソッド)は特殊な働きをするものが割り当てられている。(マジックメソッド)
代表的なものは __construct()、__destruct()など
これらを使いこなせたらいよいよPHP初級脱出といった感じかな?


■__autoload()という関数

未定義のクラスをインスタンス化しようとする(new演算子を使う)と、エラーを出す前の最終猶予段階として呼ばれる__autoload()関数(当然定義されてなければ呼ばれない)があります。

なので__autoload()関数を定義しておき、その内部で所定のクラス置き場からクラス定義が書かれたファイルを呼び出すように小細工ができる。
うまくいけば無事クラス定義を読み込むことができエラーを回避できる。

<?php

function __autoload($className) {

   require_once '/path/to/class/' . $className . '.php';
}

$obj = new foo(); // __autoload("foo")としてコールされる

なので「クラス名」と「クラス定義が書かれたファイル名」は一致させておくのが定石ということになる。

include_pathが通っていれば "/path/to/class/" が無くとも呼んでくれるだろうが、ソースコードを見て置き場所が明確に伝わってこないのは私の流儀に反するのでそれはやめておくことにしよう。。
なので実践で使う時は以下のようにシンプルでいいのさ

<?php

define("DIR_CLASS", "/path/to/class/"); // クラス置き場


function __autoload($className) {

   require_once DIR_CLASS . $className . '.php';
}

$obj = new foo(); // __autoload("foo")としてコールされる

クラス定義が読み出せなかったら即エラーになるので、file_exists()で事前のファイル存在チェックはもはや必要ないでしょ
でもそれよりもなによりもこんなもの使わないで事前にクラスぐらい呼んどけって話ではあるが。。
一体どう書くのがエレガントなんだろうか?


■__set()と__get()、ときどき__toString()

オブジェクトが持たないプロパティ(メンバ変数)にアクセスしようとすると呼ばれるメソッドがあります。
値を代入しようとすると呼ばれるのが__set()で、値を参照しようとすると呼ばれるのが__get()です。

<?php

class foo
{

}

$obj = new foo();
$obj->bar = 'test'; // 未定義プロパティにセット
echo $obj->bar; // セットした値を参照

上記のように、クラスに定義していないはずのプロパティに値セットして使うことはできます。
値セットする前にいきなり参照しようとすると当然未定義なので警告レベル設定によっては警告表示されることになります。

そこで__get()メソッドを定義しておくことで回避できそうです。
と同時に、アクセスするタイミングで小細工とかもできそうです。

<?php

class foo
{
   public function __set($name, $value) {

      $this->$name = '[' . $value . ']';
   }
   public function __get($name) {

      return '[]';
   }
}

$obj = new foo();
$obj->bar = 'test'; // 未定義プロパティにセット
echo $obj->fuga; // 未定義プロパティを参照
echo $obj->bar; // セットした値を参照

上記はどんなプロパティにアクセスしても必ず [ ] で囲まれるよう小細工したものです。

更にオブジェクト内部で値保持用のプロパティを用意すれば、もっといろいろ小細工できそうです。

<?php

class foo
{
   private $_data = array(); // 値保持用プロパティ

   public function __set($name, $value) {

      $this->_data[$name] = $value;
   }
   public function __get($name) {

      if (!isset($this->_data[$name])) $this->__set($name, '');
      return $this->_data[$name];
   }
   public function printProperty() {
      foreach ($this->_data as $key => $val) {
         echo "($key: $val)\n";
      }
   }
}

$obj = new foo();
$obj->bar = 'test'; // 未定義プロパティにセット
echo $obj->fuga; // 未定義プロパティを参照
echo $obj->bar; // セットした値を参照
$obj->printProperty(); // 扱ったプロパティを表示

これでオブジェクト外部からどんなプロパティ名にアクセスしても、まるで事前に用意されていたかのような感じになると同時に値の再利用を柔軟にできることになる。
上記はわざわざprintProperty()メソッドを用意したが、マジックメソッド__toString()で代用することもできる。

public function __toString() {
   $cat = '';
   foreach ($this->_data as $key => $val) {
      $cat .= "($key: $val)\n";
   }
   return $cat;
}

echo $obj; // オブジェクトを表示させてみる

__toString()メソッドから文字列を返すよう定義しておけば、オブジェクトを表示させようとするタイミングでこれが呼ばれることになる。


■__call()と__callStatic()

未定義のメソッド(メンバ関数)をコールすると、エラーを出す前の最終猶予段階として呼ばれるメソッドがあります。
作成済みのオブジェクトから呼ぶと__call()をコールしようと試み、静的に呼び出すと__callStatic()をコールしようと試みます。

なのでクラス内部に以下のように__call()や__callStatic()メソッドを定義しておけば、無いはずのメソッドが定義されていたことと同等になりエラーを回避できます。

<?php

class foo
{
   public function __call($name, $params) {
      echo "foo::__call()";
   }
   public static function __callStatic($name, $params) {
      echo "foo::__callStatic()";
   }
}

$obj = new foo();
$obj->bar(10); // __call('bar', array(10))としてコールされる

foo::fuga(1, 'a'); // __callStatic('fuga', array(1, 'a'))としてコールされる

これを使えば、よくあるセッターやゲッターの定義を自動化することなど便利に使えそうですね。


以上のようなマジックメソッドやリフレクション系のものなどは、どんな使われ方をするか事前にわからないことに対処するスマートな解決法だったりするのでココ一番という時に効果的に使っていきたい。
事実フレームワーク内部では多用されている。
要は使う人の為を思って、使いやすくしておくための裏工作に使う関数と言ったところでしょうか。

使う人は内部でどう処理されているかまでは分からなくとも、なんか便利に使えてしまう。

これが狙い。
これが目標。

そして・・・ 思いやり。