はじめに (対象読者・この記事でわかること)

この記事は、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, ], ];

この配列では、nullfalse も含まれていますが、数値または文字列だけを対象に何らかの加工(例:エスケープ、HTML エンコード)を行いたいことがあります。課題は次の三点です。

  1. 階層が不定$data['user']['profile']['tags'][0] のように深さが変わる。
  2. キー名が毎回異なるuser, meta, request_id など固定ではない。
  3. スカラーの判定が必要:配列、オブジェクト、nullfalse などは除外したい。

これらを手作業で if 文を多数書くのは保守性が低く、エラーも起きやすいです。そこで、再帰的に配列を走査し、is_scalar() が true になる要素だけをピックアップする汎用的な手法を提案します。

具体的な手順や実装方法

以下のセクションでは、実際に使えるコード例とともに、ステップごとに解説します。主に 2 つのアプローチを紹介します。

  • 再帰関数を自作して走査する方法
  • RecursiveIteratorIteratorRecursiveArrayIterator を組み合わせた標準イテレータ活用法

どちらも キーや深さに依存しない 形でスカラーだけを抽出でき、コールバックで任意の処理を付加可能です。

ステップ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);

ポイント解説

  1. is_scalar()整数・浮動小数点数・文字列・ブール を判定し、配列・オブジェクト・リソースは除外します。null はスカラーとみなされないので自動的に除外されます。
  2. 結果配列 $result に対して参照渡し (&$result) を使うことで、再帰呼び出しごとに同一の配列へ追加できます。
  3. キーは元の配列のキーをそのまま使用しています。キーが重複した場合は後に出現したものが上書きされます。キーの衝突を防ぎたい場合は、$result[] = $value; のようにインデックス配列に変えるか、$result[$key] = $value; の前に重複チェックを入れます。

応用:コールバックで「加工」も同時に行う

抽出したスカラーに対して何らかの処理(例:HTML エスケープ)を同時に行いたいケースがあります。その場合はコールバック関数を受け取る形にします。

Php
function 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 標準ライブラリの RecursiveIteratorIteratorRecursiveArrayIterator を組み合わせれば、自前の再帰ロジックを書かずに 同様の走査が可能です。イテレータは内部的にスタックを管理するため、深い階層でも安全に処理できます。

Php
function 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);

ポイント解説

  1. RecursiveArrayIterator は配列に対して再帰的にイテレートできるラッパーです。RecursiveIteratorIterator がその上に乗り、深さに応じた走査を行います。
  2. SELF_FIRST オプションにすると、親要素も先に取得できるため、キーの階層情報を取得しやすくなります(上記例では簡易的にキーだけ保存しています)。
  3. iterator_to_array($iterator->getDepth()) のように深さ情報を取得すれば、user.profile.age のような「ドット区切り」キーを生成して保存でき、後続の検索やマッピングに便利です。

コールバック版イテレータ

イテレータ版でもコールバックで加工できます。array_map のように書くと見た目がすっきりします。

Php
function 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 により、シンプルかつコールバック対応の実装が可能。
  • RecursiveIteratorIteratorRecursiveArrayIterator を組み合わせると、深い階層でもスタックオーバーフローを回避しつつフラットキー取得が容易になる。
  • キー衝突や nullfalse の扱いなど、実務で遭遇しやすい落とし穴とその対策をまとめました。

これらのテクニックを活用すれば、外部 API のレスポンスや JSON デコード結果といった構造が不安定なデータでも、安定してスカラーだけを処理できるようになります。次回は、取得したスカラーを データベース一括更新CSV エクスポートに応用する方法を紹介する予定です。

参考資料