PHP < 5.3の環境でどうしても遅延静的束縛を使った実装がしたくていろいろ調べてみました。
結論
ほぼむりそうでした。うまいことできそうだったら教えてください。
遅延静的束縛について
そもそも遅延静的束縛というのは、以下のようなかんじでstaticな継承コンテキストで、呼び出しもとのクラスを参照できるというもの。
class Member
{
public static function getInstance()
{
return new static;
}
}
class Leader extends Member {}
echo get_class(Member::getInstance()), PHP_EOL; // Member
echo get_class(Leader::getInstance()), PHP_EOL; // Leader
このstatic::
という呼び出し方で、呼び出しもとの値を参照できて、static::$_property
とかstatic::hogeFunction()
とかっていうことができる。
これをself::
で書くと以下のような感じで、selfが書かれたクラスを参照してしまう。
class Member
{
public static function getInstance()
{
return new self;
}
}
class Leader extends Member {}
echo get_class(Member::getInstance()), PHP_EOL; // Member
echo get_class(Leader::getInstance()), PHP_EOL; // Member
php < 5.3では静的遅延束縛をサポートされていないので、なんとかできないものかと調べてみたけど、どうもいけてる解決策がなかった。
呼び出し元のクラス名さえわかれば、call_user_func(array($class_name, $function_name))
とか駆使してなんとかいけるんだけど、呼び出しもとを取得するget_called_class()
という関数もphp >= 5.3でのサポート。
ということで詰みました。
いちおういけてない妥協案をいくつか
1. 全クラスに書く
class Member
{
public static function getInstance()
{
return new self;
}
}
class Leader extends Member
{
public static function getInstance()
{
return new self;
}
}
echo get_class(Member::getInstance()), PHP_EOL; // Member
echo get_class(Leader::getInstance()), PHP_EOL; // Leader
DRY原則とはなんだったのか!
2. class名を引数で渡す
class Member
{
public static function getInstance($class_name)
{
return new $class_name;
}
}
class Leader extends Member {}
echo get_class(Member::getInstance('Member')), PHP_EOL; // Member
echo get_class(Leader::getInstance('Leader')), PHP_EOL; // Leader
staticに呼び出しているということはクラス名はわかっているはずなので、解決策1よりは行けてる気がする。ただ、全く関係ないクラスのfunctionも実行できてしまうという諸刃の剣。(ReflectionClassとか使ってチェックすれば防げるといえば防げる気はする)
3. debug_backtraceから呼び出し元classを取得する
このサイトに書いてあるやつ。
debug_backtraceから呼び出し元ファイルを行数指定で取得して、正規表現にマッチさせてクラス名を取得するという豪快な技。
すごいけど、コストが高そう。
4. php >= 5.3にする
最強の解決策