【PHP8.x】scandir関数の使い方

scandir関数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

scandir関数は、指定されたディレクトリ内のファイルとディレクトリの一覧を取得し、配列として返す関数です。この関数は、ファイルシステムを操作する上で非常に重要な役割を果たします。具体的には、ディレクトリパスを引数として受け取り、そのディレクトリに含まれるすべてのファイルとサブディレクトリの名前を文字列の配列として返します。

scandir関数には、ソート順を指定するためのオプションの引数も用意されています。デフォルトでは、結果はアルファベット順にソートされますが、SCANDIR_SORT_DESCENDING フラグを使用することで降順にソートしたり、SCANDIR_SORT_NONE フラグを使用することでソートを行わないようにすることも可能です。ソートを行わない場合、ファイルシステムが返す順序で結果が返されます。

また、scandir関数は、カレントディレクトリを示す「.」と親ディレクトリを示す「..」をデフォルトで結果に含みます。これらを含めたくない場合は、返された配列から手動で削除する必要があります。

エラーが発生した場合、scandir関数はfalseを返します。例えば、指定されたディレクトリが存在しない場合や、アクセス権がない場合などが考えられます。そのため、戻り値をチェックして適切にエラー処理を行うことが重要です。

scandir関数は、ファイルマネージャーのようなアプリケーションや、ディレクトリ内のファイルを処理するスクリプトなど、様々な場面で利用されます。ファイルシステムを操作する上で基本となる関数の一つと言えるでしょう。

構文(syntax)

1scandir(string $directory, int $sorting_order = SCANDIR_SORT_ASCENDING, ?resource $context = null): array|false

引数(parameters)

string $directory, int $sorting_order = SCANDIR_SORT_ASCENDING, $context = null

  • string $directory: ファイルやディレクトリをスキャンする対象のディレクトリパス
  • int $sorting_order = SCANDIR_SORT_ASCENDING: ファイルやディレクトリの並び順を指定する定数(デフォルトは昇順)
  • $context = null: ストリームコンテキストを指定する(通常はnull)

戻り値(return)

array|false

指定されたディレクトリ内のファイルおよびディレクトリ名を配列として返します。ディレクトリの読み取りに失敗した場合は false を返します。

サンプルコード

PHP scandirでファイルのみを取得する

1<?php
2
3/**
4 * 指定されたディレクトリからファイルのみを一覧で取得します。
5 *
6 * `scandir` はディレクトリも含むため `is_file` を使ってフィルタリングします。
7 * '.' や '..' といった特殊なディレクトリは `is_file` の判定で偽になるため、
8 * 結果的に除外されます。
9 *
10 * @param string $directory 調査するディレクトリのパス
11 * @return array|false ファイル名の配列。ディレクトリが存在しない等のエラー時は false
12 */
13function getFilesOnly(string $directory): array|false
14{
15    // scandirでディレクトリ内の要素(ファイルとサブディレクトリ)をすべて取得します。
16    // ディレクトリが存在しない、または読み取れない場合は false が返ります。
17    $items = scandir($directory);
18
19    // scandirが失敗した場合は、何もせず false を返します。
20    if ($items === false) {
21        return false;
22    }
23
24    // array_filterを使い、配列からファイルだけを抽出します。
25    // アロー関数 (fn) を使って簡潔に記述しています (PHP 7.4+)。
26    // is_fileには完全なパスが必要なため、ディレクトリ名とファイル名を連結します。
27    $files = array_filter(
28        $items,
29        fn($item) => is_file($directory . DIRECTORY_SEPARATOR . $item)
30    );
31
32    // array_filterは元の配列のキーを維持するため、キーを0から振り直して返します。
33    return array_values($files);
34}
35
36
37// --- 以下、この関数を実際に使うための、単体で動作するサンプルコードです ---
38
39// 1. テスト用のディレクトリとファイル環境を準備します。
40$testDir = 'scandir_test_dir';
41if (!is_dir($testDir)) {
42    mkdir($testDir);
43}
44file_put_contents($testDir . '/file1.txt', 'test data 1');
45file_put_contents($testDir . '/file2.log', 'test data 2');
46if (!is_dir($testDir . '/sub_dir')) {
47    mkdir($testDir . '/sub_dir'); // 比較用のサブディレクトリ
48}
49
50echo "--- ディレクトリ '{$testDir}' の中身 (scandirの素の結果) ---\n";
51print_r(scandir($testDir));
52echo "\n";
53
54
55// 2. 作成した関数を実行して、ファイルのみを取得します。
56$fileList = getFilesOnly($testDir);
57
58
59// 3. 結果を表示します。
60echo "--- ファイルのみを抽出した結果 ---\n";
61if ($fileList !== false) {
62    print_r($fileList);
63} else {
64    echo "ディレクトリの読み込みに失敗しました。\n";
65}
66
67
68// 4. テストで作成したファイルとディレクトリを後片付けします。
69unlink($testDir . '/file1.txt');
70unlink($testDir . '/file2.log');
71rmdir($testDir . '/sub_dir');
72rmdir($testDir);
73

PHPのscandir関数は、引数に指定したディレクトリパス内のファイルやサブディレクトリの名前を配列として取得する関数です。処理に成功するとファイル名などの配列を、ディレクトリが存在しないなどのエラー時にはfalseを返します。この関数が返す配列には、ファイルだけでなく、.(カレントディレクトリ)、..(親ディレクトリ)、そしてサブディレクトリもすべて含まれるという特徴があります。

このサンプルコードは、scandirの結果からファイルのみを抽出する実践的な方法を示しています。まずscandirでディレクトリ内のすべての要素を取得した後、array_filter関数を使って配列から条件に合う要素だけを絞り込みます。その条件判定にis_file関数を利用します。is_fileは指定されたパスが通常のファイルである場合にtrueを返すため、ディレクトリや...は自動的に除外されます。is_fileには完全なパスが必要なため、scandirで得られた要素名に元のディレクトリパスを連結して渡している点が重要です。最後に、array_filterによって歯抜けになる可能性がある配列のキー(インデックス)をarray_valuesで0から始まる連番に整え、扱いやすい形式でファイル名の配列を返します。

scandir関数は、ファイルだけでなくサブディレクトリや.(カレントディレクトリ)、..(親ディレクトリ)も含むすべての一覧を返します。このため、ファイルのみを抽出するには追加の処理が必要です。注意点として、is_file関数はファイル名を単体で渡しても正しく動作しません。サンプルコードのように、調査対象のディレクトリパスと連結し、完全なファイルパスを渡す必要があります。このとき、OS環境の違いを吸収するために、パスの区切り文字としてDIRECTORY_SEPARATOR定数を利用するのが安全です。また、scandirはディレクトリが存在しない等のエラー時にfalseを返すため、必ずその戻り値を確認する処理を記述しましょう。

PHP scandir で「.」と「..」を除外する

1<?php
2
3/**
4 * 指定されたディレクトリから '.' と '..' を除いたファイルとディレクトリの一覧を取得します。
5 *
6 * @param string $directoryPath 調査するディレクトリのパス
7 * @return array ファイルとディレクトリ名の配列。ディレクトリが読み込めない場合は空の配列。
8 */
9function getDirectoryListingWithoutDots(string $directoryPath): array
10{
11    // is_dir() でディレクトリが存在するかを確認します。
12    if (!is_dir($directoryPath)) {
13        // ディレクトリが存在しない場合は空の配列を返します。
14        return [];
15    }
16    
17    // scandir() でディレクトリの内容を取得します。
18    // この時点では結果に '.' (カレントディレクトリ) と '..' (親ディレクトリ) が含まれます。
19    $items = scandir($directoryPath);
20
21    // scandir() は失敗時に false を返すため、その場合も空の配列を返します。
22    if ($items === false) {
23        return [];
24    }
25
26    // array_diff() を使って、取得した配列から '.' と '..' の値を取り除きます。
27    $filteredItems = array_diff($items, ['.', '..']);
28
29    // array_values() で配列のキーを 0 から始まる連番にリセットして返します。
30    // これにより、後のループ処理などが扱いやすくなります。
31    return array_values($filteredItems);
32}
33
34
35// --- 以下、この関数を実際に使用するサンプルコードです ---
36
37// テスト用のディレクトリパスを定義します。
38$testDir = __DIR__ . '/sample_dir';
39
40// 実行前の準備として、テスト用のディレクトリとファイルを一時的に作成します。
41if (!is_dir($testDir)) {
42    mkdir($testDir);
43}
44touch($testDir . '/file1.txt');
45touch($testDir . '/file2.log');
46if (!is_dir($testDir . '/sub_dir')) {
47    mkdir($testDir . '/sub_dir');
48}
49
50try {
51    // 上で定義した関数を呼び出して、ディレクトリの内容を取得します。
52    $fileList = getDirectoryListingWithoutDots($testDir);
53
54    echo "ディレクトリ '{$testDir}' の内容('.' と '..' を除く):\n";
55
56    if (empty($fileList)) {
57        echo "アイテムは見つかりませんでした。\n";
58    } else {
59        // 取得したファイルとディレクトリの一覧を出力します。
60        foreach ($fileList as $file) {
61            echo "- {$file}\n";
62        }
63    }
64} finally {
65    // スクリプト終了時に、作成したテスト用のファイルとディレクトリをクリーンアップします。
66    // これにより、実行環境が汚れるのを防ぎます。
67    unlink($testDir . '/file1.txt');
68    unlink($testDir . '/file2.log');
69    rmdir($testDir . '/sub_dir');
70    rmdir($testDir);
71}

このPHPコードは、指定したディレクトリ内にあるファイルとサブディレクトリの一覧を取得する方法を示しています。特に、scandir関数を使って取得した一覧から、カレントディレクトリを指す . と親ディレクトリを指す .. という特殊なエントリを除外する点が重要です。

scandir関数は、引数にディレクトリのパス文字列を受け取り、その中にある要素の名前を配列として返します。しかし、この関数が返す配列には、デフォルトで ... が含まれてしまいます。

そこで、このサンプルコードではarray_diff関数を活用しています。array_diffは、1つ目の引数で渡された配列から、2つ目以降の引数で渡された配列に含まれる値を取り除いた、新しい配列を生成します。この性質を利用して、scandirで取得した配列から ... だけを正確に除去しています。

さらに、array_diffの実行後、配列のキー(インデックス)は元のまま維持されるため、歯抜けの状態になることがあります。これを整理するため、array_values関数を使ってキーを0から始まる連番に振り直しています。これにより、後のループ処理などで配列を直感的に扱いやすくなります。

また、関数のはじめにis_dirでディレクトリの存在を確認したり、scandirが失敗した場合(falseを返した場合)を考慮したりすることで、予期せぬエラーを防ぐ堅牢な作りになっています。

scandir関数は、指定ディレクトリ内のファイル名一覧を取得しますが、必ずカレントディレクトリを示す.と親ディレクトリを示す..も結果に含みます。これらが不要な場合は、サンプルコードのようにarray_diff関数で明示的に取り除くのが一般的な方法です。また、この関数はディレクトリが存在しない場合や読み取り権限がない場合にfalseを返すため、戻り値を=== falseで厳密に比較し、エラー処理を行うことが重要です。is_dirで事前にディレクトリの存在を確認しておくと、より安全なコードになります。array_diffで要素を削除すると配列のキーが歯抜けになるため、array_valuesでキーを0から振り直すことで、後のループ処理が扱いやすくなります。

関連コンテンツ