この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:3
ページ更新者:guest
更新日時:2026-06-11 07:07:02

タイトル: 代入の際の注意点
SEOタイトル: PHP 代入の注意点完全ガイド

この記事の要点
  • PHP のデフォルトは値渡し= は値のコピー。配列も自動でコピーされる
  • オブジェクトだけは参照のように扱われる(実体はオブジェクト ID のコピー)。深いコピーは clone が必要
  • 参照渡し: $b = &$a または関数引数 function f(&$x)
  • 厳密比較 === 必須。0 == "abc" は PHP 7 までは true、PHP 8 で false に修正された
  • 未定義変数は警告: PHP 7 で notice、PHP 8 で warning。$x ?? nullisset() で安全化

値渡しと参照渡し

PHP の代入はデフォルトで値のコピーです。元の変数を書き換えても、コピー先には影響しません。

// スカラ値: 値渡し
$a = 10;
$b = $a;       // ★ 値のコピー
$b = 20;
echo $a;       // 10(変わらない)

// 配列: 値渡し(PHP の特徴)
$arr1 = [1, 2, 3];
$arr2 = $arr1;    // ★ 自動でコピー(書込み時にコピーされる Copy-on-Write)
$arr2[] = 4;
print_r($arr1);   // [1, 2, 3]
print_r($arr2);   // [1, 2, 3, 4]

// 参照渡し: & 演算子で明示的に
$x = 10;
$y = &$x;       // ★ $y は $x のエイリアス
$y = 20;
echo $x;        // 20($x も変わる)

// 解除
unset($y);
echo $x;        // 20($x は残る)

オブジェクトの代入 (大きな罠)

オブジェクトは内部的にオブジェクト ID(ハンドル)のコピーで動きます。実質的に参照のように振る舞います。

class User {
    public string $name;
    public function __construct(string $name) {
        $this->name = $name;
    }
}

$u1 = new User('Taro');
$u2 = $u1;           // ★ オブジェクト ID のコピー(実体は同じ)
$u2->name = 'Jiro';

echo $u1->name;      // Jiro ★ $u1 も変わってしまう!
echo $u2->name;      // Jiro

// 解決: clone で浅いコピー
$u3 = clone $u1;
$u3->name = 'Saburo';
echo $u1->name;      // Jiro(影響なし)
echo $u3->name;      // Saburo

clone と __clone マジックメソッド

class Order {
    public string $id;
    public array $items;
    public Customer $customer;    // 別オブジェクト

    public function __clone() {
        // ★ 深いコピーをカスタマイズ
        $this->id = uniqid('order_');         // 新 ID
        $this->customer = clone $this->customer;
        // items は配列(プリミティブ)なので自動でコピー
    }
}

$o1 = new Order();
$o2 = clone $o1;    // __clone が呼ばれる
代入の挙動深いコピー
int / float / bool / string値渡し不要
array値渡し (Copy-on-Write)不要(要素もコピーされる)
object参照のような振る舞いclone + __clone()
resource (リソース)参照言語標準ではコピー不可
null値渡し不要

null / 0 / '' / false の取り扱い

PHP は暗黙の型変換 (Type Juggling) を行うため、緩い比較 == は事故の温床です。

// ❌ PHP 7 までの罠(PHP 8 で挙動修正)
var_dump(0 == 'abc');        // PHP 7: true / PHP 8: false
var_dump('0' == false);      // true(両バージョン)
var_dump(null == 0);         // true(両バージョン)
var_dump([] == false);       // true

// ✅ 厳密比較を使う
var_dump(0 === 'abc');       // false
var_dump(null === 0);        // false
var_dump('' === null);       // false

// null と空文字を区別したいケース
$name = $request->input('name');
if ($name === null) {
    // フィールド未送信
} elseif ($name === '') {
    // 空欄で送信された
} else {
    // 何か入力された
}

未定義変数 / 未定義キー

// ❌ PHP 8 で Warning
$user = $data['user'];        // Warning: Undefined array key "user"
$value = $undefined;          // Warning: Undefined variable $undefined

// ✅ Null Coalescing Operator (??)
$user = $data['user'] ?? null;
$value = $undefined ?? 'default';

// ✅ Null Coalescing Assignment (??=)
$config['timeout'] ??= 30;    // 未設定なら 30

// ✅ isset / array_key_exists
if (isset($data['user'])) {
    $user = $data['user'];
}

// ★ isset vs array_key_exists
$a = ['x' => null];
isset($a['x']);                // false(null 扱い)
array_key_exists('x', $a);     // true(キーは存在する)

配列の初期化

// ❌ 初期化忘れ
function collect() {
    foreach ($items as $i) {
        $result[] = $i;    // 未初期化 → PHP 8 で Warning
    }
    return $result;
}

// ✅ 明示的に初期化
function collect(array $items): array {
    $result = [];
    foreach ($items as $i) {
        $result[] = $i;
    }
    return $result;
}

// 型宣言(PHP 7.4+ プロパティ型)
class Cart {
    public array $items = [];      // 初期値 [] で安全
    public ?User $user = null;
}

関数引数の参照渡し

// 値渡し(デフォルト)
function increment(int $n): int {
    return $n + 1;
}

$x = 10;
$y = increment($x);    // $x は変わらず 10、$y = 11

// 参照渡し
function incrementByRef(int &$n): void {
    $n++;
}

$x = 10;
incrementByRef($x);
echo $x;    // 11(破壊的変更)

// 配列ソートは標準関数で参照渡し
$arr = [3, 1, 2];
sort($arr);           // ★ $arr 自体が変わる
print_r($arr);        // [1, 2, 3]

PHP 8 の Named Arguments と代入

function createUser(string $name, int $age = 0, bool $active = true) {
    return compact('name', 'age', 'active');
}

// 名前付き引数で読みやすく
$user = createUser(
    name: 'Taro',
    active: false,      // age はデフォルト
);

// readonly プロパティ(一度だけ代入可)
class Coordinate {
    public function __construct(
        public readonly float $x,
        public readonly float $y,
    ) {}
}

$p = new Coordinate(1.0, 2.0);
// $p->x = 3.0;    ★ Error: Cannot modify readonly property

Type Juggling の例外

PHP 7PHP 8
0 == "abc"truefalse ★
0 == ""truefalse ★
0 == "0"truetrue
"abc" + 11 (notice)TypeError ★
(int)"abc"00

FAQ

Q: なぜ PHP 8 で 0 == "abc" の結果が変わった?
A: 旧仕様では文字列が数値に変換できないとき 0 として比較していました。これが多数のバグの原因になり、PHP 8 で文字列⇔数値比較ルールが見直されました(RFC: Saner string to number comparisons)。

Q: Laravel の null safety はどう書く?
A: Null Coalescing ??、Optional ヘルパー optional($user)->name、PHP 8+ の Null Safe Operator $user?->name など。

Q: $a = $b = $c = 0 はどう評価される?
A: 右結合で $a = ($b = ($c = 0))。代入式の戻り値は代入された値なので、3 変数とも 0 になります。