diff --git a/src/FileHashChecker.php b/src/FileHashChecker.php index 7756f38..953c681 100644 --- a/src/FileHashChecker.php +++ b/src/FileHashChecker.php @@ -2,6 +2,7 @@ namespace Rcsofttech85\FileHandler; +use Rcsofttech85\FileHandler\Exception\FileHandlerException; use Rcsofttech85\FileHandler\Exception\HashException; use Rcsofttech85\FileHandler\Validator\FileValidatorTrait; @@ -9,46 +10,47 @@ class FileHashChecker { use FileValidatorTrait; + public const ALGO_256 = 'sha3-256'; public const ALGO_512 = 'sha3-512'; + private const SEARCH_COLUMN_NAME = 'File'; + private const SEARCH_COLUMN_VALUE = 'Hash'; + + /** - * @param string $filename * @param CsvFileHandler $csvFileHandler - * @throws Exception\FileHandlerException */ - public function __construct(private string $filename, private readonly CsvFileHandler $csvFileHandler) + public function __construct(private readonly CsvFileHandler $csvFileHandler) { - $this->filename = $this->validateFileName($filename); } /** - * @param string $storedHashesFile + * @param string $filename * @param string $algo * @return bool - * @throws Exception\FileHandlerException + * @throws FileHandlerException * @throws HashException */ - public function verifyHash(string $storedHashesFile, string $algo = self::ALGO_256): bool + public function verifyHash(string $filename, string $algo = self::ALGO_256): bool { - if (!$storedHashesFile) { - throw new HashException('file not found'); - } - + $storedHashesFile = $this->getParameter(self::STORED_HASH_FILE); $file = $this->csvFileHandler->searchInCsvFile( filename: $storedHashesFile, - keyword: $this->filename, - column: 'File', + keyword: $filename, + column: self::SEARCH_COLUMN_NAME, format: FileHandler::ARRAY_FORMAT ); + if (!$file || !is_array($file)) { throw new HashException('this file is not hashed'); } $expectedHash = $file['Hash']; - $hash = $this->hashFile($algo); + $hash = $this->hashFile($filename, $algo); + if ($hash !== $expectedHash) { return false; @@ -58,16 +60,65 @@ public function verifyHash(string $storedHashesFile, string $algo = self::ALGO_2 } /** + * @param string $filename * @param string $algo * @return string - * @throws HashException + * @throws HashException|FileHandlerException */ - public function hashFile(string $algo = self::ALGO_256): string + public function hashFile(string $filename, string $algo = self::ALGO_256): string { + $this->validateFileName($filename); if (!in_array($algo, [self::ALGO_512, self::ALGO_256])) { throw new HashException('algorithm not supported'); } - return hash_file($algo, $this->filename); + + if (!$hash = hash_file($algo, $filename)) { + throw new HashException('could not hash file'); + } + + $storedHashesFile = $this->getParameter(self::STORED_HASH_FILE); + + + $file = fopen($storedHashesFile, 'a+'); + if (!$file) { + throw new FileHandlerException('file not found'); + } + $this->checkHeaderExists($file); + + + try { + $filenameExists = $this->csvFileHandler->searchInCsvFile( + filename: $storedHashesFile, + keyword: $filename, + column: 'File' + ); + + if (!$filenameExists) { + fputcsv($file, [$filename, $hash]); + } + } catch (FileHandlerException) { + fputcsv($file, [$filename, $hash]); + } finally { + fclose($file); + } + + + return $hash; + } + + /** + * @param mixed $storedHashFile + * @return void + */ + private function checkHeaderExists(mixed $storedHashFile): void + { + $header = fgetcsv($storedHashFile); + + if (!$header || $header[0] !== self::SEARCH_COLUMN_NAME || $header[1] !== self::SEARCH_COLUMN_VALUE) { + fseek($storedHashFile, 0); + fputcsv($storedHashFile, [self::SEARCH_COLUMN_NAME, self::SEARCH_COLUMN_VALUE]); + fflush($storedHashFile); + } } } diff --git a/src/config/services.yaml b/src/config/services.yaml index 19cef37..76eba7a 100644 --- a/src/config/services.yaml +++ b/src/config/services.yaml @@ -1,7 +1,7 @@ parameters: filename: 'movie.csv' secret: 'password' - hash_file_name: 'test' + @@ -22,7 +22,7 @@ services: file_hash: class: 'Rcsofttech85\FileHandler\FileHashChecker' - arguments: [ '%filename%','@csv_file_handler' ] + arguments: [ '@csv_file_handler' ] json_file_handler: class: 'Rcsofttech85\FileHandler\JsonFileHandler' diff --git a/tests/unit/FileHashCheckerTest.php b/tests/unit/FileHashCheckerTest.php index 23a66fb..e0c85bb 100644 --- a/tests/unit/FileHashCheckerTest.php +++ b/tests/unit/FileHashCheckerTest.php @@ -10,7 +10,6 @@ class FileHashCheckerTest extends BaseTest { - private static string $file; private FileHashChecker|null $fileHash = null; @@ -31,30 +30,27 @@ public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); - $file = self::$containerBuilder->getParameter('STORED_HASH_FILE'); - if (is_string($file)) { - self::$file = $file; - } static::$files[] = 'sample'; } /** * @throws HashException + * @throws FileHandlerException */ #[Test] public function shouldGenerateValidHashForDifferentAlgo(): void { $expectedHash = "5923032f7e18edf69e1a3221be3205ce658ec0e4fb274016212a09a804240683"; - $actualHash = $this->fileHash->hashFile(); //default ALGO_256 + $actualHash = $this->fileHash->hashFile(filename: 'movie.csv'); //default ALGO_256 $this->assertEquals($expectedHash, $actualHash); $expectedHash = "1050bcc2d7d840d634f067a22abb4cd693b1f2590849982e29a6f9bb28963f733" . "92b63ea24ae17edfaa500ee62b9e5482b9648af0b2b7d941992af3b0f9cbd3b"; - $actualHash = $this->fileHash->hashFile(FileHashChecker::ALGO_512); + $actualHash = $this->fileHash->hashFile(filename: 'movie.csv', algo: FileHashChecker::ALGO_512); $this->assertEquals($expectedHash, $actualHash); } @@ -66,7 +62,7 @@ public function shouldGenerateValidHashForDifferentAlgo(): void #[Test] public function checkFileIntegrityReturnsTrueIfHashMatch(): void { - $isVerified = $this->fileHash->verifyHash(storedHashesFile: self::$file); + $isVerified = $this->fileHash->verifyHash(filename: 'movie.csv'); $this->assertTrue($isVerified); } @@ -81,7 +77,7 @@ public function shouldReturnFalseIfFileIsModified(): void $backup = file_get_contents("movie.csv"); file_put_contents("movie.csv", "modified", FILE_APPEND); - $isVerified = $this->fileHash->verifyHash(self::$file); + $isVerified = $this->fileHash->verifyHash('movie.csv'); $this->assertfalse($isVerified); @@ -95,8 +91,24 @@ public function shouldReturnFalseIfFileIsModified(): void #[Test] public function shouldReturnFalseIfDifferentAlgoIsUsedForVerifyHash(): void { - $isVerified = $this->fileHash->verifyHash(self::$file, FileHashChecker::ALGO_512); + $isVerified = $this->fileHash->verifyHash('movie.csv', FileHashChecker::ALGO_512); $this->assertFalse($isVerified); } + + /** + * @throws HashException + * @throws FileHandlerException + */ + #[Test] + public function shouldAddRecordIfNewFileIsHashed(): void + { + file_put_contents('sample', "hello"); + + $this->fileHash->hashFile('sample', FileHashChecker::ALGO_512); + + $isVerified = $this->fileHash->verifyHash('sample', FileHashChecker::ALGO_512); + + $this->assertTrue($isVerified); + } }