Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 68 additions & 17 deletions src/FileHashChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,55 @@

namespace Rcsofttech85\FileHandler;

use Rcsofttech85\FileHandler\Exception\FileHandlerException;
use Rcsofttech85\FileHandler\Exception\HashException;
use Rcsofttech85\FileHandler\Validator\FileValidatorTrait;

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;
Expand All @@ -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);
}
}
}
4 changes: 2 additions & 2 deletions src/config/services.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
parameters:
filename: 'movie.csv'
secret: 'password'
hash_file_name: 'test'




Expand All @@ -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'
Expand Down
32 changes: 22 additions & 10 deletions tests/unit/FileHashCheckerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

class FileHashCheckerTest extends BaseTest
{
private static string $file;
private FileHashChecker|null $fileHash = null;


Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);

Expand All @@ -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);
}
}