< PHP 5.3.0 なシステム上で static なメソッド・メンバを参照する

前置き

合宿の報告を 1 ヶ月近くサボっているにも関わらず敢えて関係無いネタを投下してみる。

PHP 5.3.0 ではかなり色々な機能が追加されることは有名ですね。

無名関数とかクロージャとか…。 (うろ覚えw)

んで、その中でちょっとしたことなんだけど、個人的に結構大きい変更があります。

それは、static なメソッド・メンバを動的にコール出来るようになるってことです!

寧ろ、今まで出来なかったのが不思議でならない今日この頃ですが、まぁそれは置いておいて。

サンプルコードはこんな感じ。

<?php
class Monry_Sample {

    public static $hoge = "fuga";

    public static function getHoge() {
        return self::$hoge . "!!\n";
    }

}

$class_name = "Monry_Sample";
echo $class_name::$hoge; // fuga
echo $class_name::getHoge(); // fuga!!\n

// 未検証・未調査だけど、以下のも行けるのかな?
$variable_name = "hoge";
$method_name = "getHoge";
echo $class_name::$$variable_name;
echo $class_name::$method_name();

このコードは PHP 5.2.x までは "Paamayim Nekudotayim" がどーとか*1 って怒られます。

じゃあ、PHP 5.2.x で動かすにはどうすれば良いのかを考えてみました。

本題

さて、ここからが本題だったりするわけですが、んじゃぁ、PHP 5.2.x で動的に静的メンバなり静的メソッドを呼びたい場合はどーすんのよって話です。

結論から言うと私が思いついた方法は二つ。

  • Reflection
  • eval

それぞれざっくり説明します。

Reflection

詳しくはマニュアル嫁なわけですが、リバースエンジニアリングを行って強引に処理しちゃうわけです。

使うクラスは ReflectionClass, ReflectionMethod あたりかな?

<?php
$class_name = "Monry_Sample";
$variable_name = "hoge";
$method_name = "getHoge";

$class = new ReflectionClass($class_name);
$method = $class->getMethod($method_name);
echo $class->getStaticPropertyValue($variable_name); // fuga
echo $method->invoke(null); // fuga!!\n

と、いった具合ですな。

若干手続きが多くて面倒な気がするのは私だけじゃないはず…!

eval

こっちはシンプル。

だけど、セキュリティ的に怖いです。

使うクラスなんてありゃしません。言語構造の eval を使うだけです。

<?php
$class_name = "Monry_Sample";
$variable_name = "hoge";
$method_name = "getHoge";

eval("\$result = {$class_name}::{$variable_name};");
echo $result; // fuga
eval("\$result = {$class_name}::{$method_name}();");
echo $result; // fuga!!\n
  • PHP の構文を文字列として構築して、eval させる
  • 以上。

簡単です。

ま、注意点としては、php -l とかしても Syntax Error を吐いてくれないので、バグの温床になるってことです。

あとは、万が一ユーザからの入力を用いて eval させる場合は、最悪のケースも起こりうるってことも注意です。

まとめ

結局のところ、どっちが速いのかはまだベンチ取ってないから分かりません。(ヲイ

けど、PHP 5.3.0 までの繋ぎとしてはアリかなぁとか思ってみたりします。

どうしてもクラス設計で悩む場合には選択するのもありかもです。自己責任で。

おまけ

え?合宿の報告???

サーセン。

そのうち…そのうち、ね?

*1:要するにダブルコロンの使い方がオカシイってこと