- カテゴリ: PHP
- 掲載バージョン: PHP 8.4
- 名前空間 / FQCN / コマンド:
extract(グローバル関数) - 関連: compact / parse_str / array_merge / get_defined_vars / list
- 変更履歴: なし(PHP7以降は配列以外で
TypeError)
要点(TL;DR)
- 連想配列のキーを、そのまま変数として現在のスコープへ取り込む
- 最低限の使い方:
extract($data, EXTR_SKIP); - よくある罠
- 既存変数の上書き(既定は
EXTR_OVERWRITE) - 外部入力(
$_GET等)に対して使うと予期せぬ変数注入 EXTR_REFSによる参照の副作用
- 既存変数の上書き(既定は
概要
extract() は連想配列のキーを変数名、値を変数値として現在のシンボルテーブルに取り込みます。テンプレート表示や設定配列の展開に便利ですが、既存変数の上書きや外部入力の混入に注意が必要です。実務ではフラグ指定とプレフィックスで衝突を避けるのが定石です。
構文 / シグネチャ
int extract(array &$array, int $flags = EXTR_OVERWRITE, string $prefix = "");
- 引数(表)
| 引数 | 型 | 必須 | 既定値 | 説明 |
|---|---|---|---|---|
| array | array(参照渡し) | ✓ | — | 展開する連想配列。EXTR_REFS 時は参照が張られる |
| flags | int | EXTR_OVERWRITE | 取り込みルール(下記) | |
| prefix | string | "" | プレフィックス(EXTR_PREFIX_* 指定時に使用) |
主なフラグ
EXTR_OVERWRITE(既定): 既存変数を上書きEXTR_SKIP: 既存変数は保持(新規のみ作成)EXTR_IF_EXISTS: 既にある変数だけ上書き(新規は作らない)EXTR_PREFIX_SAME/EXTR_PREFIX_ALL/EXTR_PREFIX_INVALID/EXTR_PREFIX_IF_EXISTS: 衝突・無効名時などに"$prefix_{$key}"で作成EXTR_REFS: 参照として取り込み(変数と配列の値が連動)
- 戻り値:作成・更新された変数の個数(int)
- 例外/副作用:
TypeError:第1引数が配列でないE_WARNING:無効なプレフィックスなどEXTR_REFS:配列要素と展開先変数が参照で結び付く(相互に影響)
使用例
最小例
<?php
// 最小・安全寄り(既存変数を守る)
$data = ['name' => 'Alice', 'age' => 20];
$age = 99; // 既存
$count = extract($data, EXTR_SKIP);
echo $name; // Alice
echo $age; // 99(上書きされない)
echo $count; // 2(展開できた数)
実務例
<?php
// 1) 設定配列をプレフィックス付きで展開(衝突回避)
$cfg = ['name' => 'myapp', 'debug' => true, 'timezone' => 'Asia/Tokyo'];
extract($cfg, EXTR_PREFIX_ALL, 'app'); // $app_name, $app_debug, $app_timezone
// 2) DB行を「既存変数のみ」上書き(意図しない新規変数を作らない)
$pdo = new PDO('sqlite::memory:');
$pdo->exec("CREATE TABLE posts(id INTEGER, title TEXT)");
$pdo->exec("INSERT INTO posts VALUES(1, 'Hello')");
$row = $pdo->query("SELECT id, title FROM posts LIMIT 1")->fetch(PDO::FETCH_ASSOC);
$id = null; $title = ''; // 期待する変数だけ事前定義
extract($row, EXTR_IF_EXISTS); // $id と $title のみ上書き
// 3) 参照で展開(更新が元配列へ反映)
$state = ['count' => 1];
extract($state, EXTR_REFS); // $count は $state['count'] への参照
$count++;
assert($state['count'] === 2);
よくある落とし穴・注意
- 既定の
EXTR_OVERWRITEは破壊的(既存変数を上書き)。実務はEXTR_SKIP/EXTR_IF_EXISTSが無難。 - 外部入力(
$_GET/$_POST等)には使わない。安全に扱うなら明示的に代入・バリデーション。 - 無効なキー(数値開始・空白/記号)は変数化されない。
EXTR_PREFIX_INVALIDかEXTR_PREFIX_ALLを使っても、名前として無効ならスキップされ得る。 EXTR_REFSは副作用大。テストや読みやすさの観点で慎重に。- 計算量は O(n)。巨大配列で乱用するより、必要なキーのみ取り出す方が明瞭で高速。
代替・関連APIとの比較
- 手動代入(
$x = $arr['x'] ?? ...;):冗長だが安全・明示的。外部入力や重要コードで推奨。 - 配列分割(構造的代入):
['x' => $x, 'y' => $y] = $arr;(キーが確定している時に型安全・読みやすい) compact():逆方向(変数 → 連想配列)。テンプレート/返却値の構築に。array_merge():配列同士の結合。変数展開は不要な場面でこちらが適切。
テスト例(Pest)
<?php
it('skips overwrite and counts imported vars', function () {
$data = ['a' => 1, 'b' => 2];
$a = 9;
$n = extract($data, EXTR_SKIP);
expect($n)->toBe(2)
->and($a)->toBe(9)
->and($b)->toBe(2);
});
it('links by reference with EXTR_REFS', function () {
$state = ['count' => 1];
extract($state, EXTR_REFS);
$count++;
expect($state['count'])->toBe(2);
});
トラブルシュート(エラー別)
| 症状/エラー | 原因 | 対処 |
|---|---|---|
TypeError: extract(): Argument #1 ($array) must be of type array | 配列以外を渡している | 事前に is_array で確認、または適切な変換 |
extract(): prefix is not a valid identifier | プレフィックスが無効(数字開始・記号等) | 英字 or _ で開始する有効な識別子に修正 |
| 変数が思わぬ値に置き換わる | 既定 EXTR_OVERWRITE による上書き | EXTR_SKIP / EXTR_IF_EXISTS / EXTR_PREFIX_* を使用 |
| 期待する変数が作成されない | キーが存在しない/無効、または EXTR_IF_EXISTS のため新規作成されない | キーを確認、必要なら事前に変数を定義またはフラグ見直し |
| 値の更新が元配列に波及する | EXTR_REFS による参照取り込み | 参照を避けたい場合は EXTR_REFS を外す、または配列をコピー |
PHPカテゴリの補足
- 必要拡張:なし(標準)
- エラー形態:
TypeError(非配列)、E_WARNING(無効プレフィックスなど) - エンコーディング注意:変数名は英数字とアンダースコアのみ。多バイト文字キーはそのままでは変数化できない場合がある
- 8.1/8.2/8.3/8.4差分:実用上の仕様差分はほぼなし(型厳格化は7系から)
参考リンク
- PHP Manual — extract: https://www.php.net/manual/function.extract.php
- PHP Manual — compact: https://www.php.net/manual/function.compact.php
- PHP Manual — list(配列分割): https://www.php.net/manual/function.list.php
- PHP Manual — 変数の変数: https://www.php.net/manual/language.variables.variable.php

