3.

PHP の変数|$ の付け方・スコープ・参照と値渡しの違い

編集
この記事の要点
  • 変数は先頭に $。宣言は不要で、代入した時点で使えるようになる
  • 変数名は大文字小文字を区別する$name$Name は別物
  • 関数の中から外の変数は見えない。PHP のスコープは他言語より厳しい
  • 未定義の変数を読むと PHP 8 では Warning?? で既定値を用意する
  • ダブルクォートとヒアドキュメントの中では変数が展開される。シングルではされない

変数の作り方

$count = 10;             // 型は書かない
$name  = "田中";
$price = 1980.5;
$isActive = true;
$items = ['a', 'b'];
$nothing = null;

$count = "十";           // 別の型を入れ直せる(推奨はしない)

// 複数まとめて
[$x, $y] = [1, 2];       // 分割代入
[$x, $y] = [$y, $x];     // 入れ替え

$a = $b = 0;             // 同じ値を 2 つに

変数名のルール

ルール
先頭は $ + 英字かアンダースコア$name $_temp
2 文字目以降は英数字とアンダースコア$user_id $item2
数字で始められない$2nd は構文エラー
大文字小文字を区別する$name$Name
日本語も使えるが避ける$名前 は動くが可読性・互換性で不利

命名は PSR-12 に従い $camelCase にするのが主流です(Laravel など主要フレームワークもこれ)。プロジェクト内で $snake_case と混ぜないことのほうが重要です。

未定義の変数

echo $undefined;
// PHP 8: Warning: Undefined variable $undefined
// PHP 7: Notice: Undefined variable: undefined

// 既定値を用意する(?? は未定義でも警告を出さない)
$page = $_GET['page'] ?? 1;
$name = $user['name'] ?? '名無し';

// 存在確認
var_dump(isset($undefined));   // false
var_dump(empty($undefined));   // true   未定義でも警告が出ない

$x = null;
var_dump(isset($x));           // false  ← null は「未設定」扱い
var_dump(array_key_exists('x', ['x' => null]));   // true  キー自体はある

isset() は「定義されていて、かつ null でない」を判定します。null が入っているのかキーが無いのかを区別したいときは array_key_exists() を使います。判定関数の違いは empty, is_null. issetの判定比較表 にまとめています。

スコープ

$total = 100;

function show()
{
    echo $total;      // Warning: Undefined variable $total
}                     // ← 外の変数は「見えない」

function withGlobal()
{
    global $total;    // 明示的に持ち込む
    echo $total;      // 100
}

function withParam(int $total)
{
    echo $total;      // 引数で渡す(これが基本)
}

$fn = function () use ($total) {   // 無名関数は use で取り込む
    echo $total;
};

$arrow = fn() => $total * 2;       // アロー関数は自動で取り込む(読み取りのみ)

PHP の関数は外側の変数を自動では見ません。Python や JavaScript のように「外の変数が読める」つもりで書くと動きません。global は依存関係が見えなくなるので、引数で渡すのが原則です。詳細は スコープ を参照してください。

値のコピーと参照

// 配列は「値」として渡される(コピーされる)
$a = [1, 2, 3];
$b = $a;
$b[] = 4;
print_r($a);          // [1, 2, 3]        影響なし

// オブジェクトは「同じ実体」を指す
class Box { public int $n = 1; }
$x = new Box();
$y = $x;
$y->n = 99;
echo $x->n;          // 99               影響が出る
$z = clone $x;        // 複製したいときは clone

// & を付けると配列でも参照になる
$p = [1, 2];
$q = &$p;
$q[] = 3;
print_r($p);          // [1, 2, 3]
種類代入したとき
スカラー(int string float boolコピーされる
配列コピーされる(他言語と違う点)
オブジェクト同じ実体を共有する
& 付き種類によらず同じ実体を共有する

foreach の参照に注意

$items = [1, 2, 3];

foreach ($items as &$v) {
    $v *= 2;
}
// ここで $v はまだ最後の要素への参照のまま
foreach ($items as $v) {      // 2 週目で $items[2] を上書きしてしまう
    // ...
}
print_r($items);              // [2, 4, 4]  ← 最後が壊れる

// 対処: 使い終わったら unset する
foreach ($items as &$v) { $v *= 2; }
unset($v);

PHP で最も有名な落とし穴の 1 つです。foreach& を使ったら直後に unset() と覚えてください。

文字列の中に変数を埋める

$name = "田中";
$user = ['name' => '鈴木'];

echo "こんにちは $name さん";        // 展開される
echo "こんにちは {$name} さん";      // 波かっこ付き(推奨・境界が明確)
echo 'こんにちは $name さん';        // 展開されない(そのまま出る)

echo "名前は {$user['name']} です";  // 配列は波かっこが必要
echo "合計は {$obj->total} 円";

$text = <<<EOT
{$name} さんへ
金額: {$user['name']}
EOT;                                 // ヒアドキュメント(展開あり)

$raw = <<<'EOT'
$name はそのまま出る
EOT;                                 // ナウドキュメント(展開なし)

直後に英数字が続くと変数名の一部と解釈されるため、迷ったら波かっこで囲むのが安全です。

定数との違い

define('MAX_RETRY', 3);       // 実行時に定義
const TIMEOUT = 30;           // コンパイル時に定義(トップレベル or クラス内)

echo MAX_RETRY;               // $ を付けない
// MAX_RETRY = 5;             // Error: 再代入できない

echo PHP_EOL;                 // 組み込み定数
echo __DIR__;                 // マジック定数(ファイルの場所)

定数は $ を付けず、再代入できません。この点が変数との最大の違いです(定数)。

参照渡しの引数

function addOne(int $n): void      { $n++; }
function addOneRef(int &$n): void  { $n++; }

$x = 1;
addOne($x);     echo $x;   // 1   変わらない
addOneRef($x);  echo $x;   // 2   呼び出し側の変数が変わる

// 標準関数にも参照を受け取るものがある
$a = [3, 1, 2];
sort($a);            // $a 自体が並べ替えられる(戻り値は bool)
$sorted = sort($a);  // ← よくある間違い。$sorted には true が入る

preg_match('/(\d+)/', 'abc123', $m);   // $m に結果が入る
echo $m[1];                            // 123

「戻り値を返す関数」と「引数を書き換える関数」が混在しているのが PHP の標準関数の特徴です。sort() shuffle() array_push() は元の配列を変え、array_merge() array_map() は新しい配列を返します。

$GLOBALS と定義済み変数

$total = 100;

function show(): void
{
    echo $GLOBALS['total'];     // global 宣言なしでも読める
}

// スーパーグローバルはどこからでも見える
function req(): void
{
    echo $_GET['id'] ?? '';
    echo $_SERVER['REQUEST_URI'];
}

$_GET $_POST $_SERVER $_SESSION などはスーパーグローバルで、global 宣言なしにどこからでも参照できます(スーパーグローバル変数)。

ただし関数の中で $_POST を直接読むのは避けてください。テストで値を差し替えられず、その関数がどの入力に依存しているかも分からなくなります。受け取りは入口で済ませ、以降は引数で渡します。

型付き変数(プロパティ)

class User
{
    public string $name = '';       // 型を書ける(PHP 7.4 以降)
    public ?int $age = null;        // null を許す
    public int|string $id = 0;      // 複数の型を許す(PHP 8.0 以降)
    public readonly string $code;   // 一度だけ代入できる(PHP 8.1 以降)

    public function __construct()
    {
        $this->code = 'A-1';
    }
}

$u = new User();
$u->name = 123;      // TypeError にはならず "123" に変換される
$u->age = 'abc';     // TypeError: Cannot assign string to property

ローカル変数には型を書けません。型を付けられるのはプロパティ・引数・戻り値だけです。ローカル変数の型を静的解析に伝えたいときは PHPDoc を使います。

/** @var array<int, string> $names */
$names = [];

/** @var User $user */
$user = $repository->find(1);

関連

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本事項
  2. HTMLへの埋め込み
  3. 変数
  4. 可変変数
  5. 定数
  6. データ型
  7. キャスト
  8. エスケープ文字
  9. 配列
  10. 演算子
  11. 代入の際の注意点
  12. 条件分岐
  13. 繰り返し処理
  14. クラスとインスタンス
  15. コンストラクタ
  16. 関数
  17. スーパーグローバル変数
  18. スコープ
  19. staticについて
  20. yieldについて
  21. ファイルのアップロード方法
  22. DB接続方法
  23. SQL実行方法
  24. カプセル化の具体例
  25. 継承の構文
  26. オーバーライド
  27. ポリモーフィズム(多様性)の具体例
  28. 抽象クラス・メソッドの構文と具体例
  29. GET通信
  30. try catchで全てのエラーを拾う方法

最近更新/作成されたページ