PHPの代入と参照の違い2

昨日の件、ブコメなどを読んでるとちょっと私の書き方悪かったみたいなので、
PHP.netの内容を引用しつつもう少し解説してみる。
メモ書きなので、元記事にはリンクしない。

php.netによる定義抜粋

リファレンスとは?
http://www.php.net/manual/ja/language.references.whatare.php

PHP において、リファレンスとは同じ変数の内容を異なった名前で コールすることを意味します。これは C のポインタとは異なります。 


http://php.net/manual/ja/language.references.whatdo.php

<?php
$a =& $b;

(略)

ここで、$a と $b は完全に同じで、$a が $b を 指しているわけではなく、その逆でもありません。$a と $b は同じ場所を指しているのです。 

ここまで定義の話。OKね?




元記事への突込み

んで、リンク元の人はこれがわからん!って言ってるんでしょ?

<?php
/* スカラー変数への代入 */
$a = 1;
$b =& $a;
$c = $b;
$c = 7; // $c はリファレンスではないので $a や $b の値は変わりません
$c = $b;

ってやると、$b はリファレンスだから、$c もリファレンスになって、$c は $bと $a と同じ場所指すんじゃないん?ってことでしょ。


違う。

それがやりたいんだったら

$c =& $b;

って書かなきゃいけません。

$c = $b;

これは単なる値の代入だから、$c には( $b がリファレンスであろうとなかろうと)$b の値が代入されるし、$c は $b と同じ場所を指さない。


違いはここだけ。

$c = $b;  // $c には $b の値が代入
$c =& $b; // $c は $b と同じ場所を指す(これを「 $c は $b の別名」という言い方をするのが個人的には好きです)

あと、配列について補足。

http://php.net/manual/ja/language.references.whatdo.php

配列内のリファレンスの挙動はその要素ごとに決まるということです。 個々の要素のリファレンスに関する動きは、 配列コンテナがリファレンスであるかどうかとは独立しています。 

詳しくスクリプトで流れを追いたい場合はこちら

p1.php

<?php

//参照でくっつけた a,bは同じデータを指す。一心同体。
$a = 1;
$b =& $a;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
echo PHP_EOL;

// $c に $b を単に代入しても、 $b と $c は違うデータを指すよ。
$c = $b;
$c = 111;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
xdebug_debug_zval( 'c' );
echo PHP_EOL;

// $c が $b と同じデータを指したければ参照代入演算子(=&)を使う必要があるよ。
$c =& $b;
$c = 222;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
xdebug_debug_zval( 'c' );
echo PHP_EOL;

実行結果

C:\Users\bravewood\Desktop>php p1.php
a: (refcount=2, is_ref=1)=1
b: (refcount=2, is_ref=1)=1

a: (refcount=2, is_ref=1)=1
b: (refcount=2, is_ref=1)=1
c: (refcount=1, is_ref=0)=111

a: (refcount=3, is_ref=1)=222
b: (refcount=3, is_ref=1)=222
c: (refcount=3, is_ref=1)=222

p2.php

<?php

$a = 1;
$b =& $a;
$c =& $b;

xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
xdebug_debug_zval( 'c' );
echo PHP_EOL;

//参照でくっつけた $a $b $c はどれに何を代入してもすべて同じデータを示す。一心同体。
$a = 111;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
xdebug_debug_zval( 'c' );
echo PHP_EOL;

$b = 222;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
xdebug_debug_zval( 'c' );
echo PHP_EOL;

$c = 333;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
xdebug_debug_zval( 'c' );
echo PHP_EOL;

実行結果

C:\Users\bravewood\Desktop>php p2.php
a: (refcount=3, is_ref=1)=1
b: (refcount=3, is_ref=1)=1
c: (refcount=3, is_ref=1)=1

a: (refcount=3, is_ref=1)=111
b: (refcount=3, is_ref=1)=111
c: (refcount=3, is_ref=1)=111

a: (refcount=3, is_ref=1)=222
b: (refcount=3, is_ref=1)=222
c: (refcount=3, is_ref=1)=222

a: (refcount=3, is_ref=1)=333
b: (refcount=3, is_ref=1)=333
c: (refcount=3, is_ref=1)=333


p3.php

<?php

$a = 1;
$b =& $a;
$c =& $b;

xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
xdebug_debug_zval( 'c' );
echo PHP_EOL;

//参照でくっつけた $a $b $c はどれに何を代入してもすべて同じデータを示す。一心同体。
//でも一つだけ一心同体から引きはがす方法がある。それは、別の参照を代入することだよ。

$d = 4;
$c =& $d; //別の参照を代入
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
xdebug_debug_zval( 'c' );
xdebug_debug_zval( 'd' );
var_dump( $a );
var_dump( $b );
var_dump( $c );
var_dump( $d );
echo PHP_EOL;

実行結果

C:\Users\bravewood\Desktop>php p3.php
a: (refcount=3, is_ref=1)=1
b: (refcount=3, is_ref=1)=1
c: (refcount=3, is_ref=1)=1

a: (refcount=2, is_ref=1)=1
b: (refcount=2, is_ref=1)=1
c: (refcount=2, is_ref=1)=4
d: (refcount=2, is_ref=1)=4
int(1)
int(1)
int(4)
int(4)

p4.php
(オブジェクトだけちょっと特殊)

<?php

$a = new stdClass;
$b = $a;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
echo PHP_EOL;

//オブジェクトの場合、実はデータコンテナにはオブジェクトそのものではなくオブジェクトIDなるものが入っているんだよ。
var_dump( $a );
var_dump( $b );
echo PHP_EOL;

//オブジェクトIDを通してオブジェクトを操作したら、そのオブジェクトを指しているすべての変数に影響するよ
$a->hoge = 111;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
echo PHP_EOL;

//でも $b と $a は参照でつながれたわけじゃないから $b に何か別の値を入れたら、 $a とは縁が切れるよ。
$b = 222;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
echo PHP_EOL;

//参照代入演算子で結び付けると、オブジェクト以外の時と同じ動作になるよ
$b =& $a;
$a->hoge = 333;
xdebug_debug_zval( 'a' );
xdebug_debug_zval( 'b' );
echo PHP_EOL;

実行結果

C:\Users\bravewood\Desktop>php p4.php
a: (refcount=2, is_ref=0)=class stdClass {  }
b: (refcount=2, is_ref=0)=class stdClass {  }

class stdClass#1 (0) {
}
class stdClass#1 (0) {
}

a: (refcount=2, is_ref=0)=class stdClass { public $hoge = (refcount=1, is_ref=0)=111 }
b: (refcount=2, is_ref=0)=class stdClass { public $hoge = (refcount=1, is_ref=0)=111 }

a: (refcount=1, is_ref=0)=class stdClass { public $hoge = (refcount=1, is_ref=0)=111 }
b: (refcount=1, is_ref=0)=222

a: (refcount=2, is_ref=1)=class stdClass { public $hoge = (refcount=1, is_ref=0)=333 }
b: (refcount=2, is_ref=1)=class stdClass { public $hoge = (refcount=1, is_ref=0)=333 }

ここまで書けばOKでしょ。
さて、パズドラやるか。