PHP5.3以降で動的にスタティックメソッドを呼ぶ(call_user_func()を使わない)

「クラスを動的に(文字列で)指定して、これまた動的に(文字列で)指定したメソッドを呼ぶ」ような処理はちょいちょい必要になってきますよね。登場するクラスが少ない時はべた書きして分岐、とかやっちゃいそうだけど、そんなの許されるの小学生までデスヨネー。

PHPでこれを実現するのってどうやるんだろう?でも、がっつりPHP使ってたのもだいだいぶ昔の話だし、調べるのもめんどくさいなあ、とtodoのまま放置していたのですが。。。


きっかけはたまたま見ていたPHPのコードでした。唐突にeval()を使ってて、なんでeval()がこんなところに!?と思ったら動的にスタティックメソッドを呼ぶために使っていたのです!(※このコードが実際に動いていたのかどうかは確認していません。いくつか試している過程で書いたコードのようです。生きているコードはcall_user_func()を使っていました。)
「おいちょっと待て!さすがにeval()はないだろ!」と思ったものの、なぜeval()を使うことになったのかが気になって、調べてみたら。。。

  • 少なくとも5.3より前のPHPではスタティックメソッドを呼ぶのはなんとも微妙な方法しかなかったらしい
  • PHP 5.3からは他の言語のようなイマドキの書き方ができるようになった模様

ってなわけで。その方法をご紹介しましょう。

クラス名(文字列)からインスタンスを生成して文字列で指定されたメソッドを呼ぶ方法

<?php
class class1{
  function function1(){
    print "hi";
  }
}
$classname = 'class1';
$methodname = 'function1';
$klass = new $classname;
$klass->$methodname();

ポイントは

の二つでしょう

ちなみに、メソッド名をリテラルにすると動かなくなります。まあリテラルにすることも無いと思いますが。

インスタンスを生成せず、スタティックメソッドを呼ぶ方法(PHP 5.3まではこれが一般的)

<?php
class class1{
  static function function1(){
    print "hi";
  }
}
class1::function1();
$classname = 'class1';
$functionname = 'function1';
call_user_func(array('class1', 'function1')); // pre PHP 5.3 way
  • 「call_user_func(array('class1', 'function1'));」です!
  • 「call_user_func(array('class1', 'function1'));」です!!
  • 「call_user_func(array('class1', 'function1'));」です!!!

call_user_func()に関わりたくないな、と思うのは僕だけでしょうか?
なんとも微妙な解決方法のように思います。

インスタンスを生成せず、スタティックメソッドを呼ぶ方法(PHP 5.3からできるようになった方法)

<?php
class class1{
  static function function1(){
    print "hi";
  }
}
class1::function1();
$classname = 'class1';
$functionname = 'function1';
$classname::$functionname(); // since 5.3, PHP accepts this style.

すっきりしましたね。今風ですね!

  • $classname::$functionname();

わかりやすい!

PHP 5.3から変数でクラスを参照できるようになったのです!やりました!

5.3.0 変更: 変数を使ってクラスを参照することができるようになりました (例: echo $classname::constant;)。 この変数の値に、キーワード (self や parent、static など) を使うことはできません。

http://php.net/manual/ja/language.oop5.changelog.php

ここでメソッドを変数で参照しているのは可変関数(variable functions)です。一つ目の例と同じ仕組みです。

ちなみに可変変数は英語だと「Variable Variables」です。バリバリー。


PHPじわじわと進化してますね。

蛇足

ユーザーが使用する面からしか調べていないけれど、クラス周りを動的に取り扱う方法はなんだか変な感じですね。
変数に「::」を付けた時には文字列をクラス名として扱ったり「new $classname;」とすると文字列からnewできたり。
でも、この辺ってクラスオブジェクトがあれば一気にすっきりしそうなのに。。