こんにちは!今回は、PHPの標準関数であるsha1_file()について詳しく解説していきます。ファイル全体を読み込むことなく、直接SHA-1ハッシュ値を計算できる便利な関数です!
sha1_file関数とは?
sha1_file()関数は、ファイルのSHA-1ハッシュ値を計算する関数です。
大きなファイルでもメモリ効率よくハッシュ値を計算でき、ファイルの整合性確認、重複検出、バージョン管理などで活躍します!
基本的な構文
sha1_file(string $filename, bool $binary = false): string|false
- $filename: ハッシュ化するファイルのパス
- $binary:
trueで生のバイナリ出力(20バイト)、falseで16進数文字列(40文字) - 戻り値: SHA-1ハッシュ値、失敗時は
false
基本的な使用例
シンプルなファイルハッシュ
// テストファイルを作成
file_put_contents('/tmp/test.txt', 'Hello World');
// ファイルのハッシュを計算
$hash = sha1_file('/tmp/test.txt');
echo "ハッシュ: {$hash}\n";
// 出力: 0a4d55a8d778e5022fab701977c5d840bbc486d0
// 同じ内容なら同じハッシュ
file_put_contents('/tmp/test2.txt', 'Hello World');
$hash2 = sha1_file('/tmp/test2.txt');
echo "同一: " . ($hash === $hash2 ? 'Yes' : 'No') . "\n";
// 出力: Yes
バイナリ出力
// 16進数文字列(デフォルト)
$hash = sha1_file('/tmp/test.txt', false);
echo "16進数: {$hash}\n";
echo "長さ: " . strlen($hash) . "文字\n";
// 長さ: 40文字
// バイナリ出力
$hash = sha1_file('/tmp/test.txt', true);
echo "バイナリ長さ: " . strlen($hash) . "バイト\n";
echo "16進数変換: " . bin2hex($hash) . "\n";
// 長さ: 20バイト
エラーハンドリング
// 存在しないファイル
$hash = sha1_file('/nonexistent/file.txt');
if ($hash === false) {
echo "エラー: ファイルが見つかりません\n";
}
// ファイル存在チェック
if (file_exists('/tmp/test.txt')) {
$hash = sha1_file('/tmp/test.txt');
echo "ハッシュ: {$hash}\n";
} else {
echo "ファイルが存在しません\n";
}
大きなファイルの処理
// 大きなファイルを作成
$bigFile = '/tmp/bigfile.bin';
$fp = fopen($bigFile, 'w');
for ($i = 0; $i < 1000000; $i++) {
fwrite($fp, str_repeat('A', 100));
}
fclose($fp);
echo "ファイルサイズ: " . filesize($bigFile) . " バイト\n";
// メモリ効率よくハッシュを計算
$start = microtime(true);
$hash = sha1_file($bigFile);
$time = microtime(true) - $start;
echo "ハッシュ: {$hash}\n";
echo "処理時間: {$time}秒\n";
実践的な使用例
例1: ファイル整合性チェッカー
class FileIntegrityChecker {
private $checksumFile;
public function __construct($checksumFile = 'checksums.json') {
$this->checksumFile = $checksumFile;
}
/**
* ファイルのチェックサムを計算
*/
public function calculateChecksum($filepath) {
if (!file_exists($filepath)) {
return null;
}
return sha1_file($filepath);
}
/**
* チェックサムを保存
*/
public function saveChecksum($filepath, $checksum = null) {
if ($checksum === null) {
$checksum = $this->calculateChecksum($filepath);
if ($checksum === null) {
return false;
}
}
$checksums = $this->loadChecksums();
$checksums[$filepath] = [
'checksum' => $checksum,
'timestamp' => time(),
'filesize' => filesize($filepath)
];
return file_put_contents(
$this->checksumFile,
json_encode($checksums, JSON_PRETTY_PRINT)
);
}
/**
* 保存されたチェックサムを読み込み
*/
private function loadChecksums() {
if (!file_exists($this->checksumFile)) {
return [];
}
$content = file_get_contents($this->checksumFile);
return json_decode($content, true) ?? [];
}
/**
* ファイルの整合性を検証
*/
public function verify($filepath) {
$checksums = $this->loadChecksums();
if (!isset($checksums[$filepath])) {
return [
'status' => 'unknown',
'message' => 'チェックサムが記録されていません'
];
}
$currentChecksum = $this->calculateChecksum($filepath);
if ($currentChecksum === null) {
return [
'status' => 'error',
'message' => 'ファイルが見つかりません'
];
}
$expected = $checksums[$filepath]['checksum'];
if ($currentChecksum === $expected) {
return [
'status' => 'valid',
'message' => 'ファイルは改変されていません',
'checksum' => $currentChecksum
];
}
return [
'status' => 'invalid',
'message' => 'ファイルが改変されています',
'expected' => $expected,
'actual' => $currentChecksum
];
}
/**
* 複数ファイルを一括検証
*/
public function verifyAll($filepaths) {
$results = [];
foreach ($filepaths as $filepath) {
$results[$filepath] = $this->verify($filepath);
}
return $results;
}
/**
* ディレクトリ全体をスキャン
*/
public function scanDirectory($directory, $save = true) {
$results = [];
$files = glob($directory . '/*');
foreach ($files as $file) {
if (is_file($file)) {
$checksum = $this->calculateChecksum($file);
$results[$file] = $checksum;
if ($save) {
$this->saveChecksum($file, $checksum);
}
}
}
return $results;
}
}
// 使用例
echo "=== ファイル整合性チェック ===\n";
// テストファイルを作成
file_put_contents('/tmp/document.txt', 'Original content');
file_put_contents('/tmp/image.jpg', str_repeat('X', 1000));
$checker = new FileIntegrityChecker('/tmp/checksums.json');
// チェックサムを保存
$checker->saveChecksum('/tmp/document.txt');
$checker->saveChecksum('/tmp/image.jpg');
echo "チェックサムを保存しました\n";
// 検証(正常)
$result = $checker->verify('/tmp/document.txt');
echo "\n検証結果: {$result['status']}\n";
echo "メッセージ: {$result['message']}\n";
// ファイルを改変
file_put_contents('/tmp/document.txt', 'Modified content');
// 再検証(改変検出)
$result = $checker->verify('/tmp/document.txt');
echo "\n検証結果: {$result['status']}\n";
echo "メッセージ: {$result['message']}\n";
if (isset($result['expected']) && isset($result['actual'])) {
echo "期待値: {$result['expected']}\n";
echo "実際値: {$result['actual']}\n";
}
// ディレクトリスキャン
echo "\n=== ディレクトリスキャン ===\n";
$checksums = $checker->scanDirectory('/tmp');
echo count($checksums) . "個のファイルをスキャンしました\n";
例2: 重複ファイル検出
class DuplicateFileFinder {
/**
* ディレクトリ内の重複ファイルを検出
*/
public function findDuplicates($directory, $recursive = false) {
$hashes = [];
$files = $this->getFiles($directory, $recursive);
foreach ($files as $file) {
$hash = sha1_file($file);
if ($hash === false) {
continue;
}
if (!isset($hashes[$hash])) {
$hashes[$hash] = [];
}
$hashes[$hash][] = [
'path' => $file,
'size' => filesize($file),
'modified' => filemtime($file)
];
}
// 2つ以上のファイルがあるグループのみ返す
return array_filter($hashes, function($files) {
return count($files) > 1;
});
}
/**
* ファイルリストを取得
*/
private function getFiles($directory, $recursive) {
$files = [];
if ($recursive) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$files[] = $file->getPathname();
}
}
} else {
$items = glob($directory . '/*');
foreach ($items as $item) {
if (is_file($item)) {
$files[] = $item;
}
}
}
return $files;
}
/**
* レポートを生成
*/
public function generateReport($directory, $recursive = false) {
$duplicates = $this->findDuplicates($directory, $recursive);
$totalDuplicates = 0;
$wastedSpace = 0;
foreach ($duplicates as $hash => $files) {
$fileCount = count($files);
$fileSize = $files[0]['size'];
$totalDuplicates += $fileCount - 1;
$wastedSpace += $fileSize * ($fileCount - 1);
}
return [
'duplicate_groups' => count($duplicates),
'total_duplicates' => $totalDuplicates,
'wasted_space' => $wastedSpace,
'wasted_space_mb' => round($wastedSpace / 1024 / 1024, 2),
'duplicates' => $duplicates
];
}
/**
* 重複の1つを残して削除
*/
public function removeDuplicates($directory, $keepOldest = true) {
$duplicates = $this->findDuplicates($directory, false);
$removed = [];
foreach ($duplicates as $hash => $files) {
// ソート
usort($files, function($a, $b) use ($keepOldest) {
return $keepOldest ?
$a['modified'] <=> $b['modified'] :
$b['modified'] <=> $a['modified'];
});
// 最初の1つを残して削除
for ($i = 1; $i < count($files); $i++) {
if (unlink($files[$i]['path'])) {
$removed[] = $files[$i]['path'];
}
}
}
return $removed;
}
}
// 使用例
echo "=== 重複ファイル検出 ===\n";
// テストファイルを作成
file_put_contents('/tmp/file1.txt', 'Content A');
file_put_contents('/tmp/file2.txt', 'Content A'); // 重複
file_put_contents('/tmp/file3.txt', 'Content B');
file_put_contents('/tmp/file4.txt', 'Content A'); // 重複
$finder = new DuplicateFileFinder();
// 重複を検出
$duplicates = $finder->findDuplicates('/tmp');
echo "重複グループ数: " . count($duplicates) . "\n\n";
foreach ($duplicates as $hash => $files) {
echo "ハッシュ: {$hash}\n";
echo "ファイル数: " . count($files) . "\n";
foreach ($files as $file) {
echo " - {$file['path']} ({$file['size']} bytes)\n";
}
echo "\n";
}
// レポート生成
echo "=== レポート ===\n";
$report = $finder->generateReport('/tmp');
echo "重複グループ: {$report['duplicate_groups']}\n";
echo "重複ファイル数: {$report['total_duplicates']}\n";
echo "無駄な容量: {$report['wasted_space_mb']} MB\n";
例3: ファイルバージョン管理
class FileVersionManager {
private $versionDir;
public function __construct($versionDir = '/tmp/versions') {
$this->versionDir = $versionDir;
if (!is_dir($versionDir)) {
mkdir($versionDir, 0755, true);
}
}
/**
* ファイルのバージョンを保存
*/
public function saveVersion($filepath, $comment = '') {
if (!file_exists($filepath)) {
return false;
}
$hash = sha1_file($filepath);
$filename = basename($filepath);
$versionPath = $this->versionDir . '/' . $filename . '.' . $hash;
// 既に同じバージョンが存在する場合はスキップ
if (file_exists($versionPath)) {
return [
'status' => 'exists',
'hash' => $hash,
'message' => '同じバージョンが既に存在します'
];
}
// ファイルをコピー
if (!copy($filepath, $versionPath)) {
return false;
}
// メタデータを保存
$metadataPath = $versionPath . '.meta';
$metadata = [
'original_path' => $filepath,
'hash' => $hash,
'timestamp' => time(),
'filesize' => filesize($filepath),
'comment' => $comment
];
file_put_contents($metadataPath, json_encode($metadata, JSON_PRETTY_PRINT));
return [
'status' => 'saved',
'hash' => $hash,
'version_path' => $versionPath
];
}
/**
* ファイルのバージョン履歴を取得
*/
public function getVersions($filename) {
$pattern = $this->versionDir . '/' . $filename . '.*';
$files = glob($pattern);
$versions = [];
foreach ($files as $file) {
if (substr($file, -5) === '.meta') {
continue;
}
$metadataPath = $file . '.meta';
if (file_exists($metadataPath)) {
$metadata = json_decode(file_get_contents($metadataPath), true);
$versions[] = $metadata;
}
}
// タイムスタンプでソート
usort($versions, function($a, $b) {
return $b['timestamp'] <=> $a['timestamp'];
});
return $versions;
}
/**
* 特定バージョンを復元
*/
public function restore($filepath, $hash) {
$filename = basename($filepath);
$versionPath = $this->versionDir . '/' . $filename . '.' . $hash;
if (!file_exists($versionPath)) {
return false;
}
return copy($versionPath, $filepath);
}
/**
* 現在のファイルと比較
*/
public function compareWithCurrent($filepath) {
if (!file_exists($filepath)) {
return null;
}
$currentHash = sha1_file($filepath);
$versions = $this->getVersions(basename($filepath));
$hasChanges = true;
foreach ($versions as $version) {
if ($version['hash'] === $currentHash) {
$hasChanges = false;
break;
}
}
return [
'current_hash' => $currentHash,
'has_changes' => $hasChanges,
'version_count' => count($versions)
];
}
}
// 使用例
echo "=== ファイルバージョン管理 ===\n";
$manager = new FileVersionManager();
$testFile = '/tmp/document.txt';
// バージョン1
file_put_contents($testFile, 'Version 1 content');
$result = $manager->saveVersion($testFile, 'Initial version');
echo "保存: {$result['status']} (ハッシュ: {$result['hash']})\n";
// バージョン2
file_put_contents($testFile, 'Version 2 content - updated');
$result = $manager->saveVersion($testFile, 'Added more content');
echo "保存: {$result['status']} (ハッシュ: {$result['hash']})\n";
// バージョン3
file_put_contents($testFile, 'Version 3 content - final');
$result = $manager->saveVersion($testFile, 'Final version');
echo "保存: {$result['status']} (ハッシュ: {$result['hash']})\n";
// バージョン履歴を表示
echo "\n=== バージョン履歴 ===\n";
$versions = $manager->getVersions('document.txt');
foreach ($versions as $i => $version) {
echo "バージョン " . ($i + 1) . ":\n";
echo " ハッシュ: {$version['hash']}\n";
echo " 日時: " . date('Y-m-d H:i:s', $version['timestamp']) . "\n";
echo " サイズ: {$version['filesize']} bytes\n";
echo " コメント: {$version['comment']}\n\n";
}
// 現在との比較
$comparison = $manager->compareWithCurrent($testFile);
echo "=== 現在のファイル ===\n";
echo "ハッシュ: {$comparison['current_hash']}\n";
echo "未保存の変更: " . ($comparison['has_changes'] ? 'あり' : 'なし') . "\n";
例4: ダウンロード検証システム
class DownloadVerifier {
/**
* ダウンロード用のメタデータを生成
*/
public static function generateMetadata($filepath) {
if (!file_exists($filepath)) {
return null;
}
return [
'filename' => basename($filepath),
'filesize' => filesize($filepath),
'sha1' => sha1_file($filepath),
'md5' => md5_file($filepath),
'created' => time()
];
}
/**
* チェックサムファイルを生成
*/
public static function createChecksumFile($filepath) {
$metadata = self::generateMetadata($filepath);
if ($metadata === null) {
return false;
}
$checksumFile = $filepath . '.sha1';
$content = "{$metadata['sha1']} {$metadata['filename']}\n";
return file_put_contents($checksumFile, $content);
}
/**
* ダウンロードしたファイルを検証
*/
public static function verifyDownload($filepath, $expectedHash) {
if (!file_exists($filepath)) {
return [
'valid' => false,
'error' => 'ファイルが見つかりません'
];
}
$actualHash = sha1_file($filepath);
return [
'valid' => $actualHash === $expectedHash,
'expected' => $expectedHash,
'actual' => $actualHash,
'filesize' => filesize($filepath)
];
}
/**
* チェックサムファイルを読み込んで検証
*/
public static function verifyWithChecksumFile($filepath) {
$checksumFile = $filepath . '.sha1';
if (!file_exists($checksumFile)) {
return [
'valid' => false,
'error' => 'チェックサムファイルが見つかりません'
];
}
$content = file_get_contents($checksumFile);
$parts = preg_split('/\s+/', trim($content));
if (count($parts) < 1) {
return [
'valid' => false,
'error' => 'チェックサムファイルの形式が不正です'
];
}
$expectedHash = $parts[0];
return self::verifyDownload($filepath, $expectedHash);
}
/**
* 進行状況付きでファイルを検証
*/
public static function verifyWithProgress($filepath, $expectedHash, $callback = null) {
if (!file_exists($filepath)) {
return false;
}
$filesize = filesize($filepath);
$chunkSize = 1024 * 1024; // 1MB
$hash = hash_init('sha1');
$fp = fopen($filepath, 'rb');
$processed = 0;
while (!feof($fp)) {
$chunk = fread($fp, $chunkSize);
hash_update($hash, $chunk);
$processed += strlen($chunk);
if ($callback !== null) {
$progress = ($processed / $filesize) * 100;
call_user_func($callback, $progress, $processed, $filesize);
}
}
fclose($fp);
$actualHash = hash_final($hash);
return $actualHash === $expectedHash;
}
}
// 使用例
echo "=== ダウンロード検証 ===\n";
// テストファイルを作成
$testFile = '/tmp/download.zip';
file_put_contents($testFile, str_repeat('DATA', 10000));
// メタデータ生成
$metadata = DownloadVerifier::generateMetadata($testFile);
echo "ファイル: {$metadata['filename']}\n";
echo "サイズ: {$metadata['filesize']} bytes\n";
echo "SHA-1: {$metadata['sha1']}\n";
// チェックサムファイル生成
DownloadVerifier::createChecksumFile($testFile);
echo "\nチェックサムファイルを生成しました\n";
// 検証
$result = DownloadVerifier::verifyWithChecksumFile($testFile);
echo "\n検証結果: " . ($result['valid'] ? 'OK' : 'NG') . "\n";
// ファイルを改変
file_put_contents($testFile, 'CORRUPTED');
// 再検証
$result = DownloadVerifier::verifyWithChecksumFile($testFile);
echo "改変後の検証: " . ($result['valid'] ? 'OK' : 'NG') . "\n";
if (!$result['valid']) {
echo " 期待値: {$result['expected']}\n";
echo " 実際値: {$result['actual']}\n";
}
// 進行状況付き検証
echo "\n=== 進行状況付き検証 ===\n";
$testFile = '/tmp/largefile.bin';
file_put_contents($testFile, str_repeat('X', 10000000)); // 10MB
$expectedHash = sha1_file($testFile);
$isValid = DownloadVerifier::verifyWithProgress(
$testFile,
$expectedHash,
function($progress, $processed, $total) {
echo sprintf("\r検証中: %.1f%% (%d / %d bytes)", $progress, $processed, $total);
}
);
echo "\n検証完了: " . ($isValid ? 'OK' : 'NG') . "\n";
例5: ファイル同期システム
class FileSynchronizer {
/**
* ファイルリストとハッシュを取得
*/
public function getFileList($directory) {
$files = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$relativePath = substr($file->getPathname(), strlen($directory) + 1);
$files[$relativePath] = [
'path' => $file->getPathname(),
'hash' => sha1_file($file->getPathname()),
'size' => $file->getSize(),
'modified' => $file->getMTime()
];
}
}
return $files;
}
/**
* 2つのディレクトリを比較
*/
public function compare($sourceDir, $targetDir) {
$sourceFiles = $this->getFileList($sourceDir);
$targetFiles = $this->getFileList($targetDir);
$result = [
'new' => [], // ソースにのみ存在
'modified' => [], // 両方に存在するが変更あり
'deleted' => [], // ターゲットにのみ存在
'unchanged' => [] // 変更なし
];
// ソースのファイルをチェック
foreach ($sourceFiles as $path => $sourceFile) {
if (!isset($targetFiles[$path])) {
$result['new'][] = $path;
} elseif ($sourceFile['hash'] !== $targetFiles[$path]['hash']) {
$result['modified'][] = $path;
} else {
$result['unchanged'][] = $path;
}
}
// 削除されたファイルをチェック
foreach ($targetFiles as $path => $targetFile) {
if (!isset($sourceFiles[$path])) {
$result['deleted'][] = $path;
}
}
return $result;
}
/**
* ファイルを同期
*/
public function sync($sourceDir, $targetDir, $deleteExtra = false) {
$comparison = $this->compare($sourceDir, $targetDir);
$synced = [];
// 新規ファイルをコピー
foreach ($comparison['new'] as $path) {
$sourcePath = $sourceDir . '/' . $path;
$targetPath = $targetDir . '/' . $path;
$targetDirPath = dirname($targetPath);
if (!is_dir($targetDirPath)) {
mkdir($targetDirPath, 0755, true);
}
if (copy($sourcePath, $targetPath)) {
$synced['new'][] = $path;
}
}
// 変更されたファイルをコピー
foreach ($comparison['modified'] as $path) {
$sourcePath = $sourceDir . '/' . $path;
$targetPath = $targetDir . '/' . $path;
if (copy($sourcePath, $targetPath)) {
$synced['modified'][] = $path;
}
}
// 余分なファイルを削除(オプション)
if ($deleteExtra) {
foreach ($comparison['deleted'] as $path) {
$targetPath = $targetDir . '/' . $path;
if (unlink($targetPath)) {
$synced['deleted'][] = $path;
}
}
}
return $synced;
}
}
// 使用例
echo "=== ファイル同期 ===\n";
// ソースディレクトリとターゲットディレクトリを作成
$sourceDir = '/tmp/source';
$targetDir = '/tmp/target';
mkdir($sourceDir, 0755, true);
mkdir($targetDir, 0755, true);
// ソースにファイルを作成
file_put_contents($sourceDir . '/file1.txt', 'Content 1');
file_put_contents($sourceDir . '/file2.txt', 'Content 2');
mkdir($sourceDir . '/subdir', 0755, true);
file_put_contents($sourceDir . '/subdir/file3.txt', 'Content 3');
// ターゲットに古いバージョンを作成
file_put_contents($targetDir . '/file2.txt', 'Old Content 2');
file_put_contents($targetDir . '/file4.txt', 'Extra file');
$synchronizer = new FileSynchronizer();
// 比較
$comparison = $synchronizer->compare($sourceDir, $targetDir);
echo "新規: " . count($comparison['new']) . "件\n";
echo "変更: " . count($comparison['modified']) . "件\n";
echo "削除: " . count($comparison['deleted']) . "件\n";
echo "変更なし: " . count($comparison['unchanged']) . "件\n";
// 同期
echo "\n=== 同期実行 ===\n";
$synced = $synchronizer->sync($sourceDir, $targetDir, true);
print_r($synced);
例6: バックアップシステム
class BackupManager {
private $backupDir;
public function __construct($backupDir) {
$this->backupDir = $backupDir;
if (!is_dir($backupDir)) {
mkdir($backupDir, 0755, true);
}
}
/**
* 増分バックアップを作成
*/
public function incrementalBackup($sourceDir, $backupName) {
$backupPath = $this->backupDir . '/' . $backupName;
$manifestPath = $backupPath . '/manifest.json';
// 既存のマニフェストを読み込み
$previousManifest = [];
if (file_exists($manifestPath)) {
$previousManifest = json_decode(file_get_contents($manifestPath), true);
}
// 現在のファイルリストを取得
$currentFiles = $this->scanDirectory($sourceDir);
$backed = [];
$skipped = [];
foreach ($currentFiles as $relativePath => $fileInfo) {
$targetPath = $backupPath . '/' . $relativePath;
// 前回のバックアップと比較
if (isset($previousManifest[$relativePath]) &&
$previousManifest[$relativePath]['hash'] === $fileInfo['hash']) {
$skipped[] = $relativePath;
continue;
}
// バックアップ
$targetDir = dirname($targetPath);
if (!is_dir($targetDir)) {
mkdir($targetDir, 0755, true);
}
if (copy($fileInfo['path'], $targetPath)) {
$backed[] = $relativePath;
}
}
// マニフェストを更新
file_put_contents(
$manifestPath,
json_encode($currentFiles, JSON_PRETTY_PRINT)
);
return [
'backed_up' => count($backed),
'skipped' => count($skipped),
'files' => $backed
];
}
/**
* ディレクトリをスキャン
*/
private function scanDirectory($directory) {
$files = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$relativePath = substr($file->getPathname(), strlen($directory) + 1);
$files[$relativePath] = [
'path' => $file->getPathname(),
'hash' => sha1_file($file->getPathname()),
'size' => $file->getSize(),
'modified' => $file->getMTime()
];
}
}
return $files;
}
/**
* バックアップを復元
*/
public function restore($backupName, $targetDir) {
$backupPath = $this->backupDir . '/' . $backupName;
$manifestPath = $backupPath . '/manifest.json';
if (!file_exists($manifestPath)) {
return false;
}
$manifest = json_decode(file_get_contents($manifestPath), true);
$restored = [];
foreach ($manifest as $relativePath => $fileInfo) {
$sourcePath = $backupPath . '/' . $relativePath;
$targetPath = $targetDir . '/' . $relativePath;
if (!file_exists($sourcePath)) {
continue;
}
$targetDirPath = dirname($targetPath);
if (!is_dir($targetDirPath)) {
mkdir($targetDirPath, 0755, true);
}
if (copy($sourcePath, $targetPath)) {
$restored[] = $relativePath;
}
}
return $restored;
}
}
// 使用例
echo "=== バックアップシステム ===\n";
$backupManager = new BackupManager('/tmp/backups');
$sourceDir = '/tmp/mydata';
// ソースディレクトリを作成
mkdir($sourceDir, 0755, true);
file_put_contents($sourceDir . '/document1.txt', 'Document 1');
file_put_contents($sourceDir . '/document2.txt', 'Document 2');
// 初回バックアップ
$result = $backupManager->incrementalBackup($sourceDir, 'backup1');
echo "バックアップ完了:\n";
echo " バックアップ: {$result['backed_up']}件\n";
echo " スキップ: {$result['skipped']}件\n";
// ファイルを変更
file_put_contents($sourceDir . '/document1.txt', 'Document 1 - Updated');
file_put_contents($sourceDir . '/document3.txt', 'Document 3 - New');
// 増分バックアップ
echo "\n=== 増分バックアップ ===\n";
$result = $backupManager->incrementalBackup($sourceDir, 'backup1');
echo "バックアップ完了:\n";
echo " バックアップ: {$result['backed_up']}件\n";
echo " スキップ: {$result['skipped']}件\n";
md5_file()との比較
$testFile = '/tmp/test.txt';
file_put_contents($testFile, 'Test content');
// SHA-1(160ビット、40文字)
$sha1 = sha1_file($testFile);
echo "SHA-1: {$sha1}\n";
echo "長さ: " . strlen($sha1) . "文字\n";
// MD5(128ビット、32文字)
$md5 = md5_file($testFile);
echo "MD5: {$md5}\n";
echo "長さ: " . strlen($md5) . "文字\n";
// SHA-256(より安全)
$sha256 = hash_file('sha256', $testFile);
echo "SHA-256: {$sha256}\n";
echo "長さ: " . strlen($sha256) . "文字\n";
まとめ
sha1_file()関数の特徴をまとめると:
できること:
- ファイルの SHA-1ハッシュを計算
- メモリ効率の良い処理
- 大きなファイルにも対応
推奨される使用場面:
- ファイル整合性チェック
- 重複ファイル検出
- バージョン管理
- ダウンロード検証
- ファイル同期
- バックアップシステム
利点:
- メモリを大量に使わない
- 大きなファイルでも高速
- MD5より衝突耐性が高い
注意点:
- セキュリティ用途には不適切(SHA-256以上推奨)
- ファイルが存在しない場合は
falseを返す - 読み取り権限が必要
関連関数:
sha1(): 文字列のSHA-1ハッシュmd5_file(): ファイルのMD5ハッシュhash_file(): 様々なハッシュアルゴリズム
使い分け:
// ファイル整合性、重複検出: SHA-1でOK
$hash = sha1_file($filepath);
// セキュリティ重要: SHA-256以上
$hash = hash_file('sha256', $filepath);
// 高速チェック: MD5(セキュリティ不要な場合)
$hash = md5_file($filepath);
sha1_file()は、ファイルの整合性確認や重複検出に非常に便利です。大きなファイルでもメモリ効率よく処理できるので、ファイル管理システムで活躍します!
