【PHP8.x】RecursiveRegexIterator::hasChildren()メソッドの使い方
hasChildrenメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『hasChildrenメソッドは、イテレータが指す現在の要素が、さらに子要素を持つかどうかを判定するメソッドです。このメソッドは RecursiveIterator インターフェースで定義されており、多次元配列やディレクトリ構造のような階層的なデータを再帰的に処理する際に不可欠な役割を果たします。RecursiveRegexIteratorのインスタンスに対してこのメソッドを呼び出すと、内部的にラップしているイテレータの hasChildren メソッドが実行されます。もし現在の要素が子要素(例えば、配列内の別の配列や、ディレクトリ内のサブディレクトリなど)を持っている場合は true を、持っていない場合は false を返します。この戻り値は、多くの場合 RecursiveIteratorIterator によって内部的に利用され、再帰処理を続行するか、次の兄弟要素へ進むかを決定するために使われます。hasChildren メソッド自体は正規表現によるフィルタリングを行わず、あくまで構造としての子の有無を確認することに特化しています。このメソッドに引数はありません。
構文(syntax)
1<?php 2 3$array = [ 4 'non-match', 5 'match-1', 6 'parent' => [ 7 'child-match-1', 8 'child-non-match', 9 'child-match-2' 10 ], 11 'match-2' 12]; 13 14$arrayIterator = new RecursiveArrayIterator($array); 15 16// 'match'を含む要素のみを対象とするイテレータ 17$regexIterator = new RecursiveRegexIterator($arrayIterator, '/match/', RecursiveRegexIterator::GET_MATCH); 18 19// 親ノードを先に処理するように設定 20$iterator = new RecursiveIteratorIterator($regexIterator, RecursiveIteratorIterator::SELF_FIRST); 21 22foreach ($iterator as $key => $value) { 23 // 現在の要素が子を持つ(=イテレート可能である)かを確認する 24 if ($iterator->hasChildren()) { 25 echo "Key '$key' is a parent node and has children." . PHP_EOL; 26 } else { 27 echo " - Key '$key' is a leaf node with value: " . $value[0] . PHP_EOL; 28 } 29}
引数(parameters)
引数なし
引数はありません
戻り値(return)
bool
このメソッドは、現在のイテレータ位置に子要素が存在するかどうかを示す論理値(trueまたはfalse)を返します。
サンプルコード
PHP RecursiveRegexIterator hasChildren()の動作確認
1<?php 2 3/** 4 * RecursiveRegexIterator::hasChildren() の使用例を示します。 5 * このメソッドは、現在のイテレータ要素が、正規表現にマッチする子要素を持っているかどうかを判断します。 6 */ 7function demonstrateRecursiveRegexIteratorHasChildren(): void 8{ 9 // テスト用のディレクトリ構造を作成します。 10 $testDirPath = createTestDirectory(); 11 12 try { 13 // RecursiveDirectoryIterator を使用してディレクトリを再帰的に走査します。 14 // SKIP_DOTS フラグは、"." と ".." ディレクトリをスキップします。 15 $directoryIterator = new RecursiveDirectoryIterator( 16 $testDirPath, 17 RecursiveDirectoryIterator::SKIP_DOTS 18 ); 19 20 // RecursiveRegexIterator を使用して、特定のパターンにマッチする要素のみをフィルタリングします。 21 // ここでは、名前に "dir" を含むディレクトリまたはファイルにマッチさせます。 22 // RECURSIVE フラグは、子イテレータもこの正規表現でフィルタリングすることを意味します。 23 $regexIterator = new RecursiveRegexIterator( 24 $directoryIterator, 25 '/.*dir.*/i', // 名前に 'dir' を含むものにマッチ (大文字小文字を区別しない) 26 RecursiveRegexIterator::GET_MATCH, 27 RecursiveRegexIterator::RECURSIVE 28 ); 29 30 // RecursiveIteratorIterator を使用して、フィルタリングされた要素をツリー構造で走査します。 31 // SELF_FIRST フラグは、親要素を最初に訪問し、その後に子要素を訪問します。 32 $iterator = new RecursiveIteratorIterator( 33 $regexIterator, 34 RecursiveIteratorIterator::SELF_FIRST 35 ); 36 37 echo "RecursiveRegexIterator::hasChildren() の使用例:\n"; 38 echo "---------------------------------------------------\n"; 39 40 // イテレータを走査し、各要素に対して hasChildren() を呼び出します。 41 foreach ($iterator as $name => $fileInfo) { 42 // RecursiveIteratorIterator は内部で RecursiveRegexIterator をラップしているため、 43 // current() メソッドで現在の RecursiveRegexIterator インスタンスを取得します。 44 $currentRecursiveRegexIterator = $iterator->getInnerIterator(); 45 46 // hasChildren() メソッドを呼び出し、現在の要素が正規表現にマッチする子要素を持つか確認します。 47 $hasChildren = $currentRecursiveRegexIterator->hasChildren(); 48 49 // 階層に応じたインデントを作成します。 50 $prefix = str_repeat(" ", $iterator->getDepth()); 51 52 // 現在の要素のパス、タイプ、および子要素の有無を表示します。 53 echo sprintf( 54 "%s- %s (%s): %s\n", 55 $prefix, 56 $fileInfo->getFilename(), 57 $fileInfo->isDir() ? 'Dir' : 'File', 58 $hasChildren ? '子あり (hasChildren() == true)' : '子なし (hasChildren() == false)' 59 ); 60 } 61 echo "---------------------------------------------------\n"; 62 63 } finally { 64 // 後処理として、作成したテストディレクトリを削除します。 65 removeTestDirectory($testDirPath); 66 } 67} 68 69/** 70 * テスト用のディレクトリ構造を作成するヘルパー関数。 71 * 72 * @return string 作成されたテストディレクトリのパス。 73 */ 74function createTestDirectory(): string 75{ 76 $basePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'recursive_regex_test_' . uniqid(); 77 mkdir($basePath); 78 79 mkdir($basePath . DIRECTORY_SEPARATOR . 'my_dir1'); 80 file_put_contents($basePath . DIRECTORY_SEPARATOR . 'my_dir1' . DIRECTORY_SEPARATOR . 'file_a.txt', 'content A'); 81 82 mkdir($basePath . DIRECTORY_SEPARATOR . 'my_dir2'); 83 mkdir($basePath . DIRECTORY_SEPARATOR . 'my_dir2' . DIRECTORY_SEPARATOR . 'sub_dir3'); 84 file_put_contents($basePath . DIRECTORY_SEPARATOR . 'my_dir2' . DIRECTORY_SEPARATOR . 'sub_dir3' . DIRECTORY_SEPARATOR . 'file_b.txt', 'content B'); 85 86 file_put_contents($basePath . DIRECTORY_SEPARATOR . 'some_file.log', 'content Log'); 87 88 echo "テストディレクトリを作成しました: " . $basePath . "\n"; 89 return $basePath; 90} 91 92/** 93 * テスト用のディレクトリ構造を削除するヘルパー関数。 94 * 95 * @param string $path 削除するディレクトリのパス。 96 */ 97function removeTestDirectory(string $path): void 98{ 99 if (!is_dir($path)) { 100 return; 101 } 102 $it = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS); 103 $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); 104 foreach ($files as $file) { 105 if ($file->isDir()) { 106 rmdir($file->getRealPath()); 107 } else { 108 unlink($file->getRealPath()); 109 } 110 } 111 rmdir($path); 112 echo "テストディレクトリを削除しました: " . $path . "\n"; 113} 114 115// 関数の実行 116demonstrateRecursiveRegexIteratorHasChildren();
PHP 8のRecursiveRegexIterator::hasChildren()メソッドは、ファイルシステムなどを再帰的に走査し、特定の正規表現でフィルタリングされた現在の要素が、さらに正規表現にマッチする子要素を持っているかどうかを判断するメソッドです。このメソッドは引数を取らず、戻り値として真偽値(bool)を返します。現在の要素が正規表現にマッチする子要素を持つ場合はtrueを、持たない場合はfalseを返します。
この機能は、例えばディレクトリ構造を走査する際に、名前に特定のパターン(例:「dir」という文字列)を含むディレクトリやファイルだけを抽出し、さらにその抽出された要素が、同様にパターンに合致する子要素を内部に持っているかを効率的に確認したい場合に利用されます。
サンプルコードでは、まずRecursiveDirectoryIteratorでディレクトリを再帰的に走査し、次にRecursiveRegexIteratorで名前に「dir」を含む要素のみを正規表現でフィルタリングしています。そして、RecursiveIteratorIteratorを使ってフィルタリングされた要素を順に処理する中で、各要素についてhasChildren()を呼び出しています。これにより、現在の要素が正規表現にマッチする子要素を持つかどうかを判定し、その結果を表示しています。ファイルやディレクトリの階層的な探索やフィルタリングにおいて、特定の条件を満たす子要素の有無を簡単にチェックできるため、複雑なデータ構造の処理に役立ちます。
RecursiveRegexIterator::hasChildren()は、現在のイテレータ要素が、コンストラクタで指定された正規表現に合致する子要素を持っている場合にtrueを返します。単に子要素が存在するだけでなく、それらも正規表現のフィルタリング条件を満たす必要がある点が重要です。RecursiveIteratorIteratorでイテレーションする際は、getInnerIterator()を使って内部のRecursiveRegexIteratorインスタンスを取得し、その上でhasChildren()を呼び出す必要があります。また、RecursiveRegexIterator::RECURSIVEフラグが設定されている場合、子要素にも正規表現フィルタが適用されるため、このフラグの有無がhasChildren()の結果に影響することを理解しておきましょう。想定通りの動作をしない場合は、正規表現パターンとフラグ設定を見直してください。
RecursiveRegexIterator::hasChildren()で子要素を判定する
1<?php 2 3/** 4 * RecursiveRegexIterator::hasChildren() メソッドのサンプルコード。 5 * 6 * この関数は、指定されたディレクトリを再帰的に走査し、 7 * RecursiveRegexIterator を使用して特定のパターンにマッチする要素をフィルタリングします。 8 * その後、各要素が子要素を持っているか (hasChildren()) を確認し、結果を出力します。 9 * 10 * 「php hasproperty」というキーワードに関連して、このhasChildren()メソッドは、 11 * 現在のイテレータの要素が「子要素を持つ」というプロパティ(特性)を持っているかを 12 * 確認するために使用されます。 13 */ 14function demonstrateRecursiveRegexIteratorHasChildren(): void 15{ 16 // 一時ディレクトリを作成し、サンプルファイルとディレクトリを準備 17 $tempDir = sys_get_temp_dir() . '/recursive_regex_example_' . uniqid(); 18 if (!is_dir($tempDir)) { 19 mkdir($tempDir, 0777, true); 20 } 21 file_put_contents($tempDir . '/file1.txt', 'Content A'); 22 mkdir($tempDir . '/subdir1', 0777, true); 23 file_put_contents($tempDir . '/subdir1/file2.txt', 'Content B'); 24 mkdir($tempDir . '/subdir2', 0777, true); 25 mkdir($tempDir . '/subdir2/subsubdir1', 0777, true); 26 file_put_contents($tempDir . '/subdir2/subsubdir1/file3.txt', 'Content C'); 27 file_put_contents($tempDir . '/file4.log', 'Log entry'); 28 29 echo "サンプルディレクトリ構造を生成しました: " . realpath($tempDir) . "\n\n"; 30 31 try { 32 // 1. RecursiveDirectoryIterator を使用して、指定されたディレクトリを再帰的に走査します。 33 // SKIP_DOTS フラグは '.' と '..' をスキップします。 34 $directoryIterator = new RecursiveDirectoryIterator( 35 $tempDir, 36 RecursiveDirectoryIterator::SKIP_DOTS 37 ); 38 39 // 2. RecursiveRegexIterator を使用して、正規表現で要素をフィルタリングします。 40 // ここでは、すべてのファイル・ディレクトリ名にマッチするパターン `/.*/` を使用します。 41 // mode に RecursiveRegexIterator::GET_MATCH を指定すると、current() はマッチ結果の配列を返します。 42 $regexIterator = new RecursiveRegexIterator( 43 $directoryIterator, 44 '/.*/', // すべてのファイル・ディレクトリ名にマッチ 45 RecursiveRegexIterator::GET_MATCH 46 ); 47 48 // 3. RecursiveIteratorIterator を使用して、再帰的にイテレータを処理します。 49 // SELF_FIRST フラグは、親要素を先に、その後に子要素を処理することを意味します。 50 $recursiveIterator = new RecursiveIteratorIterator( 51 $regexIterator, 52 RecursiveIteratorIterator::SELF_FIRST 53 ); 54 55 echo "--- RecursiveRegexIterator::hasChildren() のデモンストレーション ---\n"; 56 echo "指定された正規表現にマッチした各要素と、その子要素の有無を表示します。\n\n"; 57 58 // イテレータをループして各要素を処理します。 59 foreach ($recursiveIterator as $path => $matchResultArray) { 60 // 現在の要素の深さに応じてインデントを設定 61 $indent = str_repeat(' ', $recursiveIterator->getDepth()); 62 63 // RecursiveRegexIterator::current() (そしてそれをラップする RecursiveIteratorIterator::current()) は、 64 // RecursiveRegexIterator::GET_MATCH モードのため、マッチ結果の配列を返します。 65 // $matchResultArray[0] には、マッチした要素のパス(ファイル名またはディレクトリ名)が含まれます。 66 $itemName = $matchResultArray[0]; 67 68 // hasChildren() メソッドは、RecursiveIteratorIterator (そしてその内部の RecursiveRegexIterator) が、 69 // 現在の位置で子イテレータを持っているかどうかを判断します。 70 // これは、現在の要素がディレクトリであり、かつそのディレクトリ内に(フィルタリング後も)子要素が存在する場合に true を返します。 71 $hasChildren = $recursiveIterator->hasChildren() ? 'はい' : 'いいえ'; 72 73 // 現在のイテレータの基となる SplFileInfo オブジェクトを取得し、 74 // それがディレクトリかどうかを判断します。 75 // RecursiveIteratorIterator -> RecursiveRegexIterator -> RecursiveDirectoryIterator とたどって、 76 // 最終的な SplFileInfo オブジェクト(元のファイルまたはディレクトリの情報)を取得します。 77 $splFileInfo = $recursiveIterator->getInnerIterator()->getInnerIterator()->current(); 78 $itemType = $splFileInfo->isDir() ? 'ディレクトリ' : 'ファイル'; 79 80 echo sprintf("%s- %s (%s)\n", $indent, $itemName, $itemType); 81 echo sprintf("%s 子要素を持っていますか?: %s\n", $indent, $hasChildren); 82 } 83 } finally { 84 // 後処理: サンプルで作成したディレクトリとファイルをすべて削除します。 85 if (is_dir($tempDir)) { 86 $files = new RecursiveIteratorIterator( 87 new RecursiveDirectoryIterator($tempDir, RecursiveDirectoryIterator::SKIP_DOTS), 88 RecursiveIteratorIterator::CHILD_FIRST 89 ); 90 foreach ($files as $fileinfo) { 91 if ($fileinfo->isDir()) { 92 rmdir($fileinfo->getRealPath()); 93 } else { 94 unlink($fileinfo->getRealPath()); 95 } 96 } 97 rmdir($tempDir); 98 echo "\nサンプルディレクトリ構造をクリーンアップしました。\n"; 99 } 100 } 101} 102 103// 関数の実行 104demonstrateRecursiveRegexIteratorHasChildren();
PHP 8のRecursiveRegexIteratorクラスに属するhasChildren()メソッドは、現在のイテレータの要素が子要素を持っているかを判断します。このメソッドは引数を取らず、戻り値として真偽値(trueまたはfalse)を返します。現在の要素が子要素を持つディレクトリである場合にtrueを返し、ファイルである場合や子要素を持たない(フィルタリングされた結果としての)ディレクトリである場合にはfalseを返します。
「php hasproperty」というキーワードに関連して、このhasChildren()メソッドは、イテレータが現在指し示している要素が「子要素を持つ」というプロパティ(特性)を備えているかを確認するために利用されます。
サンプルコードでは、一時的なディレクトリ構造を作成し、RecursiveDirectoryIteratorとRecursiveRegexIteratorを組み合わせてディレクトリを再帰的に走査し、正規表現でフィルタリングを行っています。その後、RecursiveIteratorIteratorを使って各要素を順に処理する際、hasChildren()メソッドを呼び出すことで、現在の要素がさらに深い階層の子要素を持つかを確認し、その結果を出力しています。これにより、ディレクトリとファイルの区別や、ディレクトリが子要素を持つかどうかを視覚的に理解することができます。このメソッドは、再帰的なデータ構造を扱う際に、探索を継続するかどうかを決定する上で非常に有用です。
RecursiveRegexIterator::hasChildren()は、単にディレクトリであるかだけでなく、正規表現フィルタリング後に子要素が存在するかを判断します。このため、フィルタリング条件に合致しない子要素は考慮されません。RecursiveRegexIteratorをGET_MATCHモードで初期化した場合、current()メソッドはSplFileInfoオブジェクトではなく、正規表現のマッチ結果の配列を返します。元のSplFileInfoオブジェクトを取得したい場合は、getInnerIterator()を複数回辿る必要があります。また、サンプルコードのように一時ディレクトリを作成する際は、finallyブロックを用いて処理が中断されても確実にリソースがクリーンアップされるよう実装することが、安全なコード運用に不可欠です。