【2019年07月21日】

変数には実は2種類存在する。
値型と参照型である。

そもそもプログラムというものはメモリ上で動く。
変数に値を代入すると、代入した値がメモリに記憶される。
新たな変数に値を代入すると、またメモリ上の別の場所に値が記憶される。

<?php

$a = 1;
$b = $a;
$b = 2;
var_dump($a);

?>
int(1) 

これは値渡しの例。
概念的には、$bに$aと同じ「値」を格納し、$aと$bの間にはかかわりがないことになる。
つまり、後で$bに何か別の値を代入しても、$aには変化はなく、元の値のまま。

「値型」って一体なんなのか?
変数の値はメモリ上に記憶されているわけだから、変数というのは当然、メモリの番地を保持していると考えられる。
それは値型の変数でも同じ。

$b = $aの瞬間に起こることは、実は「値のコピー」である。
$aが参照しているのとは別のメモリ上の番地に領域を新たに確保し、そこに値をコピーしているのだ。
そして$bという変数には新たに確保された番地が保持される。

もう一つの例。参照渡しの例を見てみよう。

<?php

$a = 1;
$b = &$a;
$b = 2;
var_dump($a);

?>
int(2) 

このように「&」をつけて変数の代入を行うことで
$bに、$aと同じ「番地をコピー」する。
つまり、$aと$bがメモリ上の同じ番地を参照する事になる。
値のコピーとは違う。

更にもう一つ、

<?php

$a = new TestClass();
$a->var = 1;
$b = $a;
$b->var = 2;
var_dump($a->var);

?>
int(2) 

$aにクラスのインスタンスを代入する。
クラスインスタンスは常に参照型の変数なので、
「$b = $a」とすると参照渡しになり、$bには$aと同じ番地がコピーされる。
つまり、同じインスタンスを参照することになる。

ちなみにインスタンスを値渡し(という表現でいいのかは分からないが)することも出来る。

<?php

$a = new TestClass();
$a->var = 1;
$b = clone $a;
$b->var = 2;
var_dump($a->var);

?>
int(1) 

「$b = clone $a」というように、cloneというキーワードをつけて代入すれば、
参照渡しではなく、インスタンスのコピーを作成できる。

ちなみにこれはPHP5以降の話。
有名な話だが、PHP4では普通に「$b = $a」とやるだけでコピーになっていた。
逆に参照私にする場合は「$b = &$a」とやる。

関数定義においては、引数は値型が基本となる。
しかし、引数の頭に「&」を付けることで参照型とすることが出来る。

<?php

class TestClass
{
    // 引数値渡し
    public static function incrementVal($var)
    {
        $var++;
        return $var;
    }    

    // 引数参照渡し
    public static function incrementRef(&$var)
    {
        $var++;
    }
}

// $iの初期値は1
$i = 1;
// 値渡しの関数の引数に$iを指定。$retに戻り値を代入
$ret = TestClass::incrementVal($i);
var_dump($i);
var_dump($ret);
// 参照渡しの関数の引数に$iを指定。参照なので$iは関数の処理内容が反映される
TestClass::incrementRef($i);
var_dump($i);

?>
int(1) 
int(2) 
int(2) 

このように引数が参照渡しの場合は、
関数内での変数内容の書き換えが関数の呼び出し元へも影響する。
呼び出し元と関数内とで同じ「番地」を参照する事になるので当然である。

また引数にクラスインスタンスを渡す場合、「&」をつけないでも常に参照渡しとなるので、この事は意識しておく必要がある。