こんにちは!今回は、PHPの標準関数であるscandir()について詳しく解説していきます。指定したディレクトリ内のファイルやサブディレクトリの一覧を取得できる便利な関数です!
scandir関数とは?
scandir()関数は、指定したディレクトリ内のファイルとディレクトリの一覧を配列として取得する関数です。
ファイル管理、ファイル一覧の表示、バックアップシステムなど、ディレクトリを操作する様々な場面で使用されます!
基本的な構文
scandir(string $directory, int $sorting_order = SCANDIR_SORT_ASCENDING,
resource $context = null): array|false
- $directory: スキャンするディレクトリのパス
- $sorting_order: ソート順序(
SCANDIR_SORT_ASCENDING、SCANDIR_SORT_DESCENDING、SCANDIR_SORT_NONE) - $context: ストリームコンテキスト(オプション)
- 戻り値: ファイル名の配列、失敗時は
false
基本的な使用例
シンプルなディレクトリスキャン
// カレントディレクトリをスキャン
$files = scandir('.');
print_r($files);
/*
Array (
[0] => .
[1] => ..
[2] => file1.txt
[3] => file2.php
[4] => folder1
)
*/
ソート順序の指定
// 昇順(デフォルト)
$files_asc = scandir('.', SCANDIR_SORT_ASCENDING);
print_r($files_asc);
// 降順
$files_desc = scandir('.', SCANDIR_SORT_DESCENDING);
print_r($files_desc);
// ソートなし
$files_none = scandir('.', SCANDIR_SORT_NONE);
print_r($files_none);
. と .. を除外
$files = scandir('.');
// . と .. を除外
$filtered = array_diff($files, ['.', '..']);
print_r($filtered);
/*
Array (
[2] => file1.txt
[3] => file2.php
[4] => folder1
)
*/
// またはarray_filterを使用
$filtered = array_filter($files, function($file) {
return !in_array($file, ['.', '..']);
});
エラーハンドリング
$directory = '/nonexistent/path';
$files = scandir($directory);
if ($files === false) {
echo "ディレクトリの読み取りに失敗しました\n";
} else {
print_r($files);
}
// またはis_dirでチェック
if (is_dir($directory)) {
$files = scandir($directory);
print_r($files);
} else {
echo "ディレクトリが存在しません\n";
}
実践的な使用例
例1: ファイルリストマネージャー
class FileListManager {
/**
* ディレクトリ内のファイル一覧を取得(.と..を除外)
*/
public static function getFiles($directory) {
if (!is_dir($directory)) {
return [];
}
$items = scandir($directory);
return array_diff($items, ['.', '..']);
}
/**
* ファイルのみを取得(ディレクトリを除外)
*/
public static function getFilesOnly($directory) {
$items = self::getFiles($directory);
$files = [];
foreach ($items as $item) {
$path = $directory . DIRECTORY_SEPARATOR . $item;
if (is_file($path)) {
$files[] = $item;
}
}
return $files;
}
/**
* ディレクトリのみを取得
*/
public static function getDirectoriesOnly($directory) {
$items = self::getFiles($directory);
$directories = [];
foreach ($items as $item) {
$path = $directory . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
$directories[] = $item;
}
}
return $directories;
}
/**
* 特定の拡張子のファイルを取得
*/
public static function getFilesByExtension($directory, $extension) {
$files = self::getFilesOnly($directory);
return array_filter($files, function($file) use ($extension) {
return pathinfo($file, PATHINFO_EXTENSION) === ltrim($extension, '.');
});
}
/**
* パターンマッチでファイルを取得
*/
public static function getFilesByPattern($directory, $pattern) {
$files = self::getFilesOnly($directory);
return array_filter($files, function($file) use ($pattern) {
return preg_match($pattern, $file);
});
}
/**
* ファイル情報を含む詳細リストを取得
*/
public static function getDetailedList($directory) {
$items = self::getFiles($directory);
$details = [];
foreach ($items as $item) {
$path = $directory . DIRECTORY_SEPARATOR . $item;
$details[] = [
'name' => $item,
'path' => $path,
'type' => is_dir($path) ? 'directory' : 'file',
'size' => is_file($path) ? filesize($path) : null,
'modified' => filemtime($path),
'extension' => is_file($path) ? pathinfo($item, PATHINFO_EXTENSION) : null
];
}
return $details;
}
/**
* ファイルをサイズでソート
*/
public static function sortBySize($directory, $descending = false) {
$details = self::getDetailedList($directory);
usort($details, function($a, $b) use ($descending) {
$aSize = $a['size'] ?? 0;
$bSize = $b['size'] ?? 0;
return $descending ? $bSize <=> $aSize : $aSize <=> $bSize;
});
return $details;
}
/**
* ファイルを更新日時でソート
*/
public static function sortByDate($directory, $descending = true) {
$details = self::getDetailedList($directory);
usort($details, function($a, $b) use ($descending) {
return $descending ?
$b['modified'] <=> $a['modified'] :
$a['modified'] <=> $b['modified'];
});
return $details;
}
}
// 使用例
echo "=== ファイルリストマネージャー ===\n";
$directory = '/tmp';
// すべてのファイルとディレクトリ
$all = FileListManager::getFiles($directory);
echo "全アイテム数: " . count($all) . "\n";
// ファイルのみ
$files = FileListManager::getFilesOnly($directory);
echo "ファイル数: " . count($files) . "\n";
// ディレクトリのみ
$dirs = FileListManager::getDirectoriesOnly($directory);
echo "ディレクトリ数: " . count($dirs) . "\n";
// 特定の拡張子(.txt)
$txtFiles = FileListManager::getFilesByExtension($directory, '.txt');
echo "\n.txtファイル:\n";
foreach (array_slice($txtFiles, 0, 5) as $file) {
echo " - {$file}\n";
}
// パターンマッチ(testで始まるファイル)
$testFiles = FileListManager::getFilesByPattern($directory, '/^test/');
echo "\n'test'で始まるファイル:\n";
foreach (array_slice($testFiles, 0, 5) as $file) {
echo " - {$file}\n";
}
// 詳細リスト
echo "\n=== 詳細リスト(最初の5件) ===\n";
$details = FileListManager::getDetailedList($directory);
foreach (array_slice($details, 0, 5) as $item) {
echo "{$item['name']} ({$item['type']})";
if ($item['size'] !== null) {
echo " - " . number_format($item['size']) . " bytes";
}
echo "\n";
}
// サイズでソート
echo "\n=== サイズの大きい順(上位5件) ===\n";
$bySize = FileListManager::sortBySize($directory, true);
foreach (array_slice($bySize, 0, 5) as $item) {
if ($item['type'] === 'file') {
echo sprintf(
"%s - %s\n",
$item['name'],
number_format($item['size']) . ' bytes'
);
}
}
例2: 再帰的ディレクトリスキャナー
class RecursiveDirectoryScanner {
/**
* 再帰的にすべてのファイルを取得
*/
public static function scanRecursive($directory, $maxDepth = null, $currentDepth = 0) {
$result = [];
if (!is_dir($directory)) {
return $result;
}
if ($maxDepth !== null && $currentDepth >= $maxDepth) {
return $result;
}
$items = scandir($directory);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $directory . DIRECTORY_SEPARATOR . $item;
$result[] = [
'name' => $item,
'path' => $path,
'depth' => $currentDepth,
'type' => is_dir($path) ? 'directory' : 'file'
];
if (is_dir($path)) {
$subItems = self::scanRecursive($path, $maxDepth, $currentDepth + 1);
$result = array_merge($result, $subItems);
}
}
return $result;
}
/**
* ディレクトリツリーを表示
*/
public static function displayTree($directory, $prefix = '', $isLast = true) {
$items = scandir($directory);
$items = array_diff($items, ['.', '..']);
$items = array_values($items);
foreach ($items as $index => $item) {
$path = $directory . DIRECTORY_SEPARATOR . $item;
$isLastItem = ($index === count($items) - 1);
$connector = $isLastItem ? '└── ' : '├── ';
echo $prefix . $connector . $item;
if (is_dir($path)) {
echo " [DIR]\n";
$newPrefix = $prefix . ($isLastItem ? ' ' : '│ ');
self::displayTree($path, $newPrefix, $isLastItem);
} else {
echo "\n";
}
}
}
/**
* ファイル総数とディレクトリ総数を取得
*/
public static function getCounts($directory) {
$items = self::scanRecursive($directory);
$fileCount = 0;
$dirCount = 0;
foreach ($items as $item) {
if ($item['type'] === 'file') {
$fileCount++;
} else {
$dirCount++;
}
}
return [
'files' => $fileCount,
'directories' => $dirCount,
'total' => $fileCount + $dirCount
];
}
/**
* 総ファイルサイズを計算
*/
public static function getTotalSize($directory) {
$items = self::scanRecursive($directory);
$totalSize = 0;
foreach ($items as $item) {
if ($item['type'] === 'file' && file_exists($item['path'])) {
$totalSize += filesize($item['path']);
}
}
return $totalSize;
}
/**
* 拡張子別の統計を取得
*/
public static function getExtensionStats($directory) {
$items = self::scanRecursive($directory);
$stats = [];
foreach ($items as $item) {
if ($item['type'] === 'file') {
$ext = pathinfo($item['name'], PATHINFO_EXTENSION);
$ext = $ext ?: 'no_extension';
if (!isset($stats[$ext])) {
$stats[$ext] = [
'count' => 0,
'total_size' => 0
];
}
$stats[$ext]['count']++;
if (file_exists($item['path'])) {
$stats[$ext]['total_size'] += filesize($item['path']);
}
}
}
return $stats;
}
}
// 使用例
echo "=== 再帰的ディレクトリスキャン ===\n";
$directory = '/tmp/test';
// ディレクトリツリー表示
echo "ディレクトリツリー:\n";
RecursiveDirectoryScanner::displayTree($directory);
// ファイル・ディレクトリ数
echo "\n=== 統計情報 ===\n";
$counts = RecursiveDirectoryScanner::getCounts($directory);
echo "ファイル数: {$counts['files']}\n";
echo "ディレクトリ数: {$counts['directories']}\n";
echo "合計: {$counts['total']}\n";
// 総サイズ
$totalSize = RecursiveDirectoryScanner::getTotalSize($directory);
echo "総サイズ: " . number_format($totalSize) . " bytes\n";
// 拡張子別統計
echo "\n=== 拡張子別統計 ===\n";
$extStats = RecursiveDirectoryScanner::getExtensionStats($directory);
foreach ($extStats as $ext => $stats) {
echo sprintf(
".%s: %d個 (合計 %s bytes)\n",
$ext,
$stats['count'],
number_format($stats['total_size'])
);
}
例3: ファイル検索システム
class FileSearcher {
/**
* ファイル名で検索
*/
public static function searchByName($directory, $searchTerm, $caseSensitive = false) {
$items = scandir($directory);
$results = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$match = $caseSensitive ?
str_contains($item, $searchTerm) :
str_contains(strtolower($item), strtolower($searchTerm));
if ($match) {
$path = $directory . DIRECTORY_SEPARATOR . $item;
$results[] = [
'name' => $item,
'path' => $path,
'type' => is_dir($path) ? 'directory' : 'file'
];
}
}
return $results;
}
/**
* 正規表現で検索
*/
public static function searchByRegex($directory, $pattern) {
$items = scandir($directory);
$results = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
if (preg_match($pattern, $item)) {
$path = $directory . DIRECTORY_SEPARATOR . $item;
$results[] = [
'name' => $item,
'path' => $path,
'type' => is_dir($path) ? 'directory' : 'file'
];
}
}
return $results;
}
/**
* サイズ範囲で検索
*/
public static function searchBySize($directory, $minSize = 0, $maxSize = null) {
$items = scandir($directory);
$results = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $directory . DIRECTORY_SEPARATOR . $item;
if (is_file($path)) {
$size = filesize($path);
if ($size >= $minSize && ($maxSize === null || $size <= $maxSize)) {
$results[] = [
'name' => $item,
'path' => $path,
'size' => $size
];
}
}
}
return $results;
}
/**
* 更新日時で検索
*/
public static function searchByDate($directory, $startDate = null, $endDate = null) {
$items = scandir($directory);
$results = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $directory . DIRECTORY_SEPARATOR . $item;
$mtime = filemtime($path);
$inRange = true;
if ($startDate !== null && $mtime < $startDate) {
$inRange = false;
}
if ($endDate !== null && $mtime > $endDate) {
$inRange = false;
}
if ($inRange) {
$results[] = [
'name' => $item,
'path' => $path,
'modified' => $mtime,
'modified_date' => date('Y-m-d H:i:s', $mtime)
];
}
}
return $results;
}
/**
* 複数条件で検索
*/
public static function advancedSearch($directory, $criteria) {
$items = scandir($directory);
$results = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $directory . DIRECTORY_SEPARATOR . $item;
$match = true;
// ファイル名検索
if (isset($criteria['name'])) {
if (!str_contains(strtolower($item), strtolower($criteria['name']))) {
$match = false;
}
}
// 拡張子検索
if (isset($criteria['extension']) && is_file($path)) {
$ext = pathinfo($item, PATHINFO_EXTENSION);
if ($ext !== $criteria['extension']) {
$match = false;
}
}
// タイプ検索
if (isset($criteria['type'])) {
$actualType = is_dir($path) ? 'directory' : 'file';
if ($actualType !== $criteria['type']) {
$match = false;
}
}
if ($match) {
$results[] = [
'name' => $item,
'path' => $path,
'type' => is_dir($path) ? 'directory' : 'file'
];
}
}
return $results;
}
}
// 使用例
echo "=== ファイル検索 ===\n";
$directory = '/tmp';
// 名前で検索
$results = FileSearcher::searchByName($directory, 'test');
echo "「test」を含むファイル:\n";
foreach (array_slice($results, 0, 5) as $result) {
echo " - {$result['name']} ({$result['type']})\n";
}
// 正規表現で検索
echo "\n正規表現検索(.txtで終わる):\n";
$results = FileSearcher::searchByRegex($directory, '/\.txt$/');
foreach (array_slice($results, 0, 5) as $result) {
echo " - {$result['name']}\n";
}
// サイズで検索
echo "\n1KB〜10KBのファイル:\n";
$results = FileSearcher::searchBySize($directory, 1024, 10240);
foreach (array_slice($results, 0, 5) as $result) {
echo " - {$result['name']} (" . number_format($result['size']) . " bytes)\n";
}
// 日付で検索(最近1日間)
$yesterday = strtotime('-1 day');
$results = FileSearcher::searchByDate($directory, $yesterday);
echo "\n最近1日間に更新されたファイル:\n";
foreach (array_slice($results, 0, 5) as $result) {
echo " - {$result['name']} ({$result['modified_date']})\n";
}
// 高度な検索
echo "\n=== 高度な検索 ===\n";
$criteria = [
'name' => 'test',
'type' => 'file'
];
$results = FileSearcher::advancedSearch($directory, $criteria);
echo "条件: 名前に'test'を含むファイル:\n";
foreach (array_slice($results, 0, 5) as $result) {
echo " - {$result['name']}\n";
}
例4: ファイルバックアップシステム
class BackupManager {
/**
* ディレクトリをバックアップ
*/
public static function backup($sourceDir, $backupDir) {
if (!is_dir($sourceDir)) {
return false;
}
// バックアップディレクトリを作成
if (!is_dir($backupDir)) {
mkdir($backupDir, 0755, true);
}
$items = scandir($sourceDir);
$copied = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$sourcePath = $sourceDir . DIRECTORY_SEPARATOR . $item;
$backupPath = $backupDir . DIRECTORY_SEPARATOR . $item;
if (is_file($sourcePath)) {
if (copy($sourcePath, $backupPath)) {
$copied[] = $item;
}
} elseif (is_dir($sourcePath)) {
self::backup($sourcePath, $backupPath);
$copied[] = $item . ' [DIR]';
}
}
return $copied;
}
/**
* 増分バックアップ(変更されたファイルのみ)
*/
public static function incrementalBackup($sourceDir, $backupDir) {
if (!is_dir($sourceDir)) {
return [];
}
if (!is_dir($backupDir)) {
mkdir($backupDir, 0755, true);
}
$items = scandir($sourceDir);
$updated = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$sourcePath = $sourceDir . DIRECTORY_SEPARATOR . $item;
$backupPath = $backupDir . DIRECTORY_SEPARATOR . $item;
if (is_file($sourcePath)) {
$needsBackup = false;
if (!file_exists($backupPath)) {
$needsBackup = true;
} else {
// ファイルが変更されているかチェック
$sourceTime = filemtime($sourcePath);
$backupTime = filemtime($backupPath);
if ($sourceTime > $backupTime) {
$needsBackup = true;
}
}
if ($needsBackup && copy($sourcePath, $backupPath)) {
$updated[] = $item;
}
}
}
return $updated;
}
/**
* バックアップファイル一覧を表示
*/
public static function listBackups($backupDir) {
if (!is_dir($backupDir)) {
return [];
}
$items = scandir($backupDir);
$backups = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $backupDir . DIRECTORY_SEPARATOR . $item;
if (is_file($path)) {
$backups[] = [
'name' => $item,
'size' => filesize($path),
'date' => filemtime($path)
];
}
}
// 日付でソート
usort($backups, function($a, $b) {
return $b['date'] <=> $a['date'];
});
return $backups;
}
}
// 使用例
echo "=== バックアップシステム ===\n";
$sourceDir = '/tmp/source';
$backupDir = '/tmp/backup';
// フルバックアップ
$copied = BackupManager::backup($sourceDir, $backupDir);
echo "バックアップ完了: " . count($copied) . "ファイル\n";
// 増分バックアップ
$updated = BackupManager::incrementalBackup($sourceDir, $backupDir);
echo "増分バックアップ: " . count($updated) . "ファイルを更新\n";
// バックアップ一覧
$backups = BackupManager::listBackups($backupDir);
echo "\nバックアップファイル一覧:\n";
foreach (array_slice($backups, 0, 10) as $backup) {
echo sprintf(
" %s - %s (%s bytes)\n",
date('Y-m-d H:i:s', $backup['date']),
$backup['name'],
number_format($backup['size'])
);
}
例5: ディレクトリ比較ツール
class DirectoryComparator {
/**
* 2つのディレクトリを比較
*/
public static function compare($dir1, $dir2) {
$files1 = scandir($dir1);
$files2 = scandir($dir2);
$files1 = array_diff($files1, ['.', '..']);
$files2 = array_diff($files2, ['.', '..']);
return [
'only_in_first' => array_diff($files1, $files2),
'only_in_second' => array_diff($files2, $files1),
'in_both' => array_intersect($files1, $files2)
];
}
/**
* 詳細な差分を取得
*/
public static function detailedDiff($dir1, $dir2) {
$comparison = self::compare($dir1, $dir2);
$diff = [
'added' => [],
'removed' => [],
'modified' => [],
'unchanged' => []
];
// 追加されたファイル
foreach ($comparison['only_in_second'] as $file) {
$diff['added'][] = $file;
}
// 削除されたファイル
foreach ($comparison['only_in_first'] as $file) {
$diff['removed'][] = $file;
}
// 両方に存在するファイルをチェック
foreach ($comparison['in_both'] as $file) {
$path1 = $dir1 . DIRECTORY_SEPARATOR . $file;
$path2 = $dir2 . DIRECTORY_SEPARATOR . $file;
if (is_file($path1) && is_file($path2)) {
$hash1 = md5_file($path1);
$hash2 = md5_file($path2);
if ($hash1 !== $hash2) {
$diff['modified'][] = $file;
} else {
$diff['unchanged'][] = $file;
}
}
}
return $diff;
}
/**
* ディレクトリの同期状況を取得
*/
public static function getSyncStatus($dir1, $dir2) {
$diff = self::detailedDiff($dir1, $dir2);
$total = count($diff['added']) + count($diff['removed']) +
count($diff['modified']) + count($diff['unchanged']);
return [
'in_sync' => empty($diff['added']) && empty($diff['removed']) &&
empty($diff['modified']),
'differences' => count($diff['added']) + count($diff['removed']) +
count($diff['modified']),
'total_files' => $total,
'details' => $diff
];
}
}
// 使用例
echo "=== ディレクトリ比較 ===\n";
$dir1 = '/tmp/dir1';
$dir2 = '/tmp/dir2';
// 基本比較
$comparison = DirectoryComparator::compare($dir1, $dir2);
echo "dir1のみ: " . count($comparison['only_in_first']) . "ファイル\n";
echo "dir2のみ: " . count($comparison['only_in_second']) . "ファイル\n";
echo "両方に存在: " . count($comparison['in_both']) . "ファイル\n";
// 詳細差分
echo "\n=== 詳細差分 ===\n";
$diff = DirectoryComparator::detailedDiff($dir1, $dir2);
echo "追加: " . count($diff['added']) . "ファイル\n";
echo "削除: " . count($diff['removed']) . "ファイル\n";
echo "変更: " . count($diff['modified']) . "ファイル\n";
echo "未変更: " . count($diff['unchanged']) . "ファイル\n";
// 同期状況
echo "\n=== 同期状況 ===\n";
$status = DirectoryComparator::getSyncStatus($dir1, $dir2);
echo "同期状態: " . ($status['in_sync'] ? '同期済み' : '差分あり') . "\n";
echo "差分ファイル数: {$status['differences']}\n";
echo "総ファイル数: {$status['total_files']}\n";
glob()との比較
// scandir() - すべてのファイルを取得
$files_scandir = scandir('/tmp');
$files_scandir = array_diff($files_scandir, ['.', '..']);
// glob() - パターンマッチで取得
$files_glob = glob('/tmp/*');
// glob()は . と .. を含まない
// glob()はフルパスを返す
// scandir()はファイル名のみを返す
echo "scandir()の結果: " . count($files_scandir) . "件\n";
echo "glob()の結果: " . count($files_glob) . "件\n";
// 使い分け:
// - すべてのファイルが必要 → scandir()
// - パターンマッチが必要 → glob()
// - フルパスが必要 → glob()
まとめ
scandir()関数の特徴をまとめると:
できること:
- ディレクトリ内のファイル一覧を取得
- ソート順序の指定
- ファイル・ディレクトリの列挙
戻り値:
- ファイル名の配列
.と..も含まれる- 失敗時は
false
推奨される使用場面:
- ファイル一覧表示
- ファイル検索
- バックアップシステム
- ディレクトリ比較
- ファイル管理
ソート順序:
SCANDIR_SORT_ASCENDING: 昇順(デフォルト)SCANDIR_SORT_DESCENDING: 降順SCANDIR_SORT_NONE: ソートなし
注意点:
.と..が含まれる- ファイル名のみ返す(パスは含まない)
- 再帰的には検索しない
関連関数:
glob(): パターンマッチでファイル検索opendir()/readdir(): ディレクトリハンドルで処理is_dir(): ディレクトリかチェックis_file(): ファイルかチェック
よく使うパターン:
// . と .. を除外
$files = array_diff(scandir($dir), ['.', '..']);
// フルパスを取得
$files = array_map(function($file) use ($dir) {
return $dir . DIRECTORY_SEPARATOR . $file;
}, scandir($dir));
// ファイルのみ
$files = array_filter(scandir($dir), function($file) use ($dir) {
return is_file($dir . '/' . $file);
});
scandir()は、ディレクトリ内のファイルを一覧取得する最もシンプルな方法です。ファイル管理やバックアップシステムなど、様々な場面で活躍します!
