[PHP] 非オブジェクト指向の「変数の参照」

2012 年 5 月 1 日

【2012.05.01 追記】foreachでの利用

PHPの非オブジェクト指向における参照(リファレンス)とは、あくまで「変数の参照」。

つまり、変数の別名(エイリアス)をつくっているにすぎない。

C/C++のポインタなどとは根本的に異なり、値やオブジェクトの参照(メモリのアドレス)をとっているわけではないことに注意。

		$a = 5;
		$b = &$a;
		echo $b; // 5
		
		$a = new stdClass;
		echo is_object($b); // 1
		// 数値の5でないということは、値やオブジェクトの参照を
		// とっていないということ

関数の引数に変数の参照をとる

この場合も、引数を特定の変数のエイリアスにしているのと同じ(1)。

function test(&$param = 5) {
	echo $param;
	$param = 10;
}

$a = 1;
test($a); // 1---(1)
echo $a; // 10

test(); // 5---(2)

その引数にデフォルト値を設定できる(2)。

あくまでエイリアスをつくっているのだから、そもそも実引数を指定しない(エイリアスの元がない)のはおかしいと思うのだが、PHPではこの場合、参照をとる仮引数(エイリアスとなるはずだった引数)を通常の引数として扱う。

変数の参照(エイリアス)を返す関数

function & doFunction() {
	static $local = 1;
	echo $local;
	
	return $local;
}

class Sample {
	private $prop = 1;
	
	public function & doSample() {
		echo $this->prop;
		return $this->prop;
	}
	
	public function echoProp() {
		echo $this->prop;
	}
}

$outerLoacl = &doFunction(); // 1
++$outerLoacl;
doFunction(); // 2

$sample = new Sample;
$outerProp = &$sample->doSample(); // 1
++$outerProp;
$sample->echoProp(); // 2

関数の側でも、受ける変数(エイリアス)の側でも「&」を指定する必要がある(そうしないと、通常の代入になる)。

関数内の静的変数も、クラスのprivateなプロパティもエイリアスをつくれてしまう。

柔軟性が高いが、リスクも高いので使う場面に気をつけたほうがいい。

foreachでの利用

使い方としては単純。(1)

$arr1 = array(0, 1, 2);

foreach ($arr1 as &$value) { // (1)
	++$value;
}

$arr2 = array(10, 11, 12);

foreach ($arr2 as $value) { // (2)
	++$value;
}

echo $arr1[2]; // 10 (3)

(2)で$valueを使い回してしまうと、前のforeachのarr1[2]の変数の参照(エイリアス)が残っているため、(2)でそのエイリアスに$arr2[0]の値が代入されて、(3)のような結果になる。

これを防ぐには、unset($value)すればいいのだが、それ以前にローカル変数の使い回しをやめるべきだろう。

(PHPは関数単位のスコープしかない)