タイトル: 代入の際の注意点
SEOタイトル: PHP 代入の注意点完全ガイド
| この記事の要点 |
|
値渡しと参照渡し
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 7 | PHP 8 |
|---|---|---|
0 == "abc" | true | false ★ |
0 == "" | true | false ★ |
0 == "0" | true | true |
"abc" + 1 | 1 (notice) | TypeError ★ |
(int)"abc" | 0 | 0 |
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 になります。