[PHP] クラスにおけるメンバ変数(プロパティ)内の関数オブジェクトとメソッドの性質:無名関数(クロージャ)について

【2013年05月21日 修正】 メソッドの一覧について

無名関数とメンバ変数

PHPのバージョン5.3.0から無名関数(関数オブジェクト)が導入されたので、それをクラスのメンバ変数に代入することもできる。

class TestMethod {
	public $fn;
	
	function __construct() {
		// 無名関数を関数オブジェクトとして代入
		$this->fn = function () {
			echo 'closure';
		};
	}
}

しかし、これをアロー演算子「->」と関数呼び出し演算子「()」を使って、インスタンスから直接呼び出すことはできない。

$ins = new TestMethod;
$ins->fn();
// 「fnというメソッドが定義されていない」とエラーが出る

おそらく、メソッドとの名前の衝突を避けるためにこういう仕様になっていると思われる。

以下の呼び出し方も駄目。エラーが出る。

($ins->fn)(); // 言語的に文法の面でNG
$ins->{'fn'}(); // $ins->fn()と同じこと

この場合、「call_user_func()」か変数を使う。

// 変数に関数オブジェクトを代入
$fn = $ins->fn;
$fn();

// または
call_user_func($ins->fn);

一行で実行できる「call_user_func()」のほうがいいだろう。

マジックメソッドの「__call()」を使う方法もあるが、これだと下記のようにメンバ変数と同名のメソッドが存在した場合にいずれにせよ呼ばれないうえに、さすがに面倒。

同名のメンバ変数とメソッド

PHPでは、クラスのメンバ変数とメソッドがそれぞれ分けて管理されるので、識別子(名前)が重複しても構わない。

では、以下のように、無名関数の関数オブジェクトをpublicなメンバ変数に渡して実行したら、同名のメンバ変数とメソッドはどちらが優先されるのか。

class TestMethod {
	public $fn;
	
	function __construct() {
		// 無名関数を関数オブジェクトとして代入
		$this->fn = function () {
			echo 'closure';
		};
	}
	
	// メンバ変数と同名のメソッド
	public function fn() {
		echo 'method';
	}
}

$ins = new TestMethod;
$ins->fn();

この場合、「method」と表示される。上記のとおり、そもそもメンバ変数内の関数オブジェクトは直接呼び出せないため。

メソッドの受け渡し

メソッドを関数オブジェクト的に受け渡したい場合は、インスタンスとメソッド名の文字列を渡せばいいだけ。

function callMethod($obj, $name) {
	$obj->$name();
	// または文字列を直接指定
	$obj->{'fn'}();
}

補足

メンバ変数とメソッドの存在確認

機能 メンバ変数 メソッド
存在確認 property_exists($obj, $name) method_exists($obj, $name)
マジックメソッド __get($name, $args), __set($name, $args) __call($name, $args)
一覧 get_class_vars($class_name)
get_object_vars($object)
foreach($obj as $memberName => $curValue)
get_class_methods($class_name)

__get、__set、__callのマジックメソッドは、アクセスできるメンバ変数・メソッドがない場合のみ呼ばれる。

メンバ変数の一覧は「get_class_vars($class_name)」で取得できるが、この関数はバージョンによって動作が異なるので「get_object_vars($object)」を使ったほうがいい。

「get_class_methods($class_name)」は、メソッドの名前(文字列)の一覧が配列で返ってくる。

いずれも、あくまで基本的に「そのスコープでアクセス可能なもの」だけ出力される。

【2013年05月15日 作成】