はじめに (対象読者・この記事でわかること)
この記事は、PHPで多次元配列を扱う実務経験があるエンジニア、あるいは配列操作に不安を抱えている初心者・中級者向けに書かれています。配列の階層やキー名が毎回変わっても、数値または文字列といったスカラー値だけを抽出・加工したいケースは多く、手間のかかる条件分岐を書きたくないという悩みが頻出します。本記事を読むことで、再帰関数や組み込みイテレータを使って、キーや深さに依存しない「スカラーのみ」を安全に取得し、必要な処理を一括で適用できるようになります。また、パフォーマンスやエラー回避のポイントも併せて解説します。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- PHPの基本文法(変数、配列、関数定義など)
- 再帰処理やコールバック関数の概念
- Composer が利用できる環境(任意)
多次元配列でスカラーだけを扱う背景と課題
PHP の配列はキーが数値でも文字列でも自由に組み合わせられ、さらに配列の要素として別の配列を入れ子にできるため、多次元配列は非常に柔軟です。その一方で、実務で扱うデータは外部 API のレスポンスや JSON デコード結果など、階層構造が不規則でキー名が毎回異なることが普通です。たとえば次のような配列があります。
Php$data = [ 'user' => [ 'id' => 123, 'name' => 'Taro', 'profile' => [ 'age' => 28, 'email' => 'taro@example.com', 'tags' => ['php', 'backend'] ], 'settings' => null, ], 'meta' => [ 'request_id' => 'abcd-1234', 'timestamp' => 1693400000, 'debug' => false, ], ];
この配列では、null や false も含まれていますが、数値または文字列だけを対象に何らかの加工(例:エスケープ、HTML エンコード)を行いたいことがあります。課題は次の三点です。
- 階層が不定:
$data['user']['profile']['tags'][0]のように深さが変わる。 - キー名が毎回異なる:
user,meta,request_idなど固定ではない。 - スカラーの判定が必要:配列、オブジェクト、
null、falseなどは除外したい。
これらを手作業で if 文を多数書くのは保守性が低く、エラーも起きやすいです。そこで、再帰的に配列を走査し、is_scalar() が true になる要素だけをピックアップする汎用的な手法を提案します。
具体的な手順や実装方法
以下のセクションでは、実際に使えるコード例とともに、ステップごとに解説します。主に 2 つのアプローチを紹介します。
- 再帰関数を自作して走査する方法
RecursiveIteratorIteratorとRecursiveArrayIteratorを組み合わせた標準イテレータ活用法
どちらも キーや深さに依存しない 形でスカラーだけを抽出でき、コールバックで任意の処理を付加可能です。
ステップ1:再帰関数でスカラー要素だけを抽出
まずは最もシンプルな実装です。再帰的に配列を走査し、スカラーであれば結果配列に格納します。
Php/** * 多次元配列からスカラー値だけを抽出する再帰関数 * * @param array $input 元配列 * @param array $result 参照渡しで結果を格納 */ function extractScalars(array $input, array &$result = []) { foreach ($input as $key => $value) { if (is_array($value)) { // 配列なら再帰呼び出し extractScalars($value, $result); } elseif (is_scalar($value)) { // スカラーなら結果に格納(キーは保持したい場合は $key も保存) $result[$key] = $value; } // null, object, resource などは無視 } } // 使用例 $scalars = []; extractScalars($data, $scalars); print_r($scalars);
ポイント解説
is_scalar()は 整数・浮動小数点数・文字列・ブール を判定し、配列・オブジェクト・リソースは除外します。nullはスカラーとみなされないので自動的に除外されます。- 結果配列
$resultに対して参照渡し (&$result) を使うことで、再帰呼び出しごとに同一の配列へ追加できます。 - キーは元の配列のキーをそのまま使用しています。キーが重複した場合は後に出現したものが上書きされます。キーの衝突を防ぎたい場合は、
$result[] = $value;のようにインデックス配列に変えるか、$result[$key] = $value;の前に重複チェックを入れます。
応用:コールバックで「加工」も同時に行う
抽出したスカラーに対して何らかの処理(例:HTML エスケープ)を同時に行いたいケースがあります。その場合はコールバック関数を受け取る形にします。
Phpfunction walkScalars(array $input, callable $callback, array &$result = []) { foreach ($input as $key => $value) { if (is_array($value)) { walkScalars($value, $callback, $result); } elseif (is_scalar($value)) { $result[$key] = $callback($value, $key); } } } // エスケープ例 $escaped = []; walkScalars($data, function ($v, $k) { return is_string($v) ? htmlspecialchars($v, ENT_QUOTES, 'UTF-8') : $v; }, $escaped); print_r($escaped);
ステップ2:標準イテレータで同等の処理を実装
PHP 標準ライブラリの RecursiveIteratorIterator と RecursiveArrayIterator を組み合わせれば、自前の再帰ロジックを書かずに 同様の走査が可能です。イテレータは内部的にスタックを管理するため、深い階層でも安全に処理できます。
Phpfunction extractScalarsWithIterator(array $input): array { $iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator($input), RecursiveIteratorIterator::SELF_FIRST ); $result = []; foreach ($iterator as $key => $value) { // 配列の場合はスキップ(RecursiveIterator が自動で子要素に入るため) if (is_array($value)) { continue; } if (is_scalar($value)) { // キーは「フラット化」したものを使用 $flatKey = implode('.', iterator_to_array($iterator->getDepth())); // ここでは単純に元キーで保存 $result[$key] = $value; } } return $result; } // 使用例 $scalarsIter = extractScalarsWithIterator($data); print_r($scalarsIter);
ポイント解説
RecursiveArrayIteratorは配列に対して再帰的にイテレートできるラッパーです。RecursiveIteratorIteratorがその上に乗り、深さに応じた走査を行います。SELF_FIRSTオプションにすると、親要素も先に取得できるため、キーの階層情報を取得しやすくなります(上記例では簡易的にキーだけ保存しています)。iterator_to_array($iterator->getDepth())のように深さ情報を取得すれば、user.profile.ageのような「ドット区切り」キーを生成して保存でき、後続の検索やマッピングに便利です。
コールバック版イテレータ
イテレータ版でもコールバックで加工できます。array_map のように書くと見た目がすっきりします。
Phpfunction walkScalarsWithIterator(array $input, callable $callback): array { $iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator($input), RecursiveIteratorIterator::SELF_FIRST ); $result = []; foreach ($iterator as $key => $value) { if (is_array($value)) { continue; } if (is_scalar($value)) { $result[$key] = $callback($value, $key); } } return $result; } // 例: 文字列だけを upper case に変換 $uppercased = walkScalarsWithIterator($data, function ($v) { return is_string($v) ? strtoupper($v) : $v; }); print_r($uppercased);
ハマった点やエラー解決
| 現象 | 原因 | 解決策 |
|---|---|---|
Invalid argument supplied for foreach() |
再帰関数に配列でない値が渡された | 関数冒頭で if (!is_array($input)) return; を追加 |
| キーが重複して上書きされる | フラット化した結果、同名キーが複数階層に出現 | 結果配列をインデックス配列にしたり、$result[] = ['key' => $keyPath, 'value' => $value]; のようにパス情報も保存 |
| 深い階層でスタックオーバーフロー | 再帰呼び出しが極端に深い(>1000) | RecursiveIteratorIterator に切り替えるか、ini_set('xdebug.max_nesting_level', 2000); で一時的に対策 |
null が結果に混入 |
is_scalar(null) が false だが、カスタムロジックで null 判定を外した |
if (is_scalar($value) || $value === null) のように条件を明示的に書く |
解決策まとめ
- シンプルな再帰関数は可読性が高く、柔軟にコールバックを組み込める。小規模・中規模のデータに最適。
- 標準イテレータは深い階層や大量データでも安定して動作し、キーのフラット化が簡単。パフォーマンスが要求される場面で有効。
- キー衝突を防ぐために「パス情報(ドット区切り)」を保存すると、後続の検索やマッピングが楽になる。
- エラーハンドリングは
is_arrayのチェックやnull判定を明示的に入れるだけで、ほとんどのケースで対応できる。
まとめ
本記事では、階層やキーが不定な多次元配列から、数値・文字列といったスカラー値だけを抽出し、任意の処理を安全に適用する方法を解説しました。
- 再帰関数
extractScalars/walkScalarsにより、シンプルかつコールバック対応の実装が可能。 RecursiveIteratorIteratorとRecursiveArrayIteratorを組み合わせると、深い階層でもスタックオーバーフローを回避しつつフラットキー取得が容易になる。- キー衝突や
null、falseの扱いなど、実務で遭遇しやすい落とし穴とその対策をまとめました。
これらのテクニックを活用すれば、外部 API のレスポンスや JSON デコード結果といった構造が不安定なデータでも、安定してスカラーだけを処理できるようになります。次回は、取得したスカラーを データベース一括更新や CSV エクスポートに応用する方法を紹介する予定です。
参考資料
- PHP: is_scalar - Manual
- PHP: RecursiveIteratorIterator - Manual
- PHP: RecursiveArrayIterator - Manual
- 書籍: 『PHP逆引きレシピブック』(技術評論社)
