Skip to content

Commit

Permalink
find and replace feature (#15)
Browse files Browse the repository at this point in the history
Signed-off-by: rahul <[email protected]>
  • Loading branch information
rcsofttech85 authored Sep 14, 2023
1 parent 8d73b3a commit 55db14e
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 15 deletions.
162 changes: 147 additions & 15 deletions src/FileHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class FileHandler

private array $files = [];


/**
* @throws FileHandlerException
*/
Expand Down Expand Up @@ -160,31 +161,44 @@ public function delete(string $filename): void
/**
* @throws FileHandlerException
*/
private function getRows(): Generator
private function getRows(string|null $filename = null): Generator
{
if (count($this->files) > 1) {
throw new FileHandlerException("multiple files not allowed");
}

$file = $this->files[0];
$headers = fgetcsv($file);

$this->isValidCsvFileFormat($headers);
$file = $this->ensureSingleFileProcessing($filename);
$headers = $this->extractHeader($file);

$isEmptyFile = true;
while (($row = fgetcsv($file)) !== false) {
$isEmptyFile = false;
$this->isValidCsvFileFormat($row);
$item = array_combine($headers, $row);
yield $item;
try {
while (($row = fgetcsv($file)) !== false) {
$isEmptyFile = false;
$this->isValidCsvFileFormat($row);
$item = array_combine($headers, $row);

yield $item;
}
} finally {
fclose($file);
}
fclose($file);


if ($isEmptyFile) {
throw new FileHandlerException('invalid file format');
}
}

private function ensureSingleFileProcessing(string|null $filename): mixed
{
if (count($this->files) < 1) {
if (!$filename || !file_exists($filename)) {
throw new FileHandlerException("no files to process");
}
$this->open($filename);
}
if (count($this->files) > 1) {
throw new FileHandlerException("multiple files not allowed");
}
return $this->files[0];
}

/**
* @throws FileHandlerException
*/
Expand All @@ -198,6 +212,100 @@ private function search(string $keyword, string $column, string|null $format): b
return false;
}

public function findAndReplaceInCsv(
string $filename,
string $keyword,
string $replace,
string|null $column = null
): bool {
$headers = $this->extractHeader($filename);


if (!$headers) {
throw new FileHandlerException('failed to extract header');
}

$tempFilePath = $this->createTempFileWithHeaders($headers);

try {
$count = 0;
foreach ($this->getRows($filename) as $row) {
if (!$column) {
$count += $this->replaceKeywordInRow($row, $keyword, $replace);
} else {
$count += $this->replaceKeywordInColumn($row, $column, $keyword, $replace);
}

$this->writeRowToTempFile($tempFilePath, $row);
}

if ($count < 1) {
return false;
}

$this->renameTempFile($tempFilePath, $filename);
} finally {
$this->cleanupTempFile($tempFilePath);
}

return true;
}

private function replaceKeywordInRow(array &$row, string $keyword, string $replace): int
{
$count = 0;
$replacement = array_search($keyword, $row);

if ($replacement !== false) {
$row[$replacement] = $replace;
$count++;
}

return $count;
}

private function replaceKeywordInColumn(array &$row, string $column, string $keyword, string $replace): int
{
$count = 0;

if ($keyword === $row[$column]) {
$row[$column] = $replace;
$count++;
}

return $count;
}

private function writeRowToTempFile(string $tempFilePath, array $row): void
{
$tempFileHandle = fopen($tempFilePath, 'a');
fputs($tempFileHandle, implode(',', $row) . PHP_EOL);
fclose($tempFileHandle);
}

private function renameTempFile(string $tempFilePath, string $filename): void
{
if (!rename($tempFilePath, $filename)) {
throw new FileHandlerException('Failed to rename temp file');
}
}

private function cleanupTempFile(string $tempFilePath): void
{
unlink($tempFilePath);
}

private function createTempFileWithHeaders(array $headers): string
{
$tempFilePath = tempnam(sys_get_temp_dir(), 'tempfile_');
$tempFileHandle = fopen($tempFilePath, 'w');
fputs($tempFileHandle, implode(',', $headers) . PHP_EOL);
fclose($tempFileHandle);

return $tempFilePath;
}


/**
* @throws FileHandlerException
*/
Expand All @@ -207,4 +315,28 @@ private function isValidCsvFileFormat(array|false $row): void
throw new FileHandlerException('invalid file format');
}
}

private function extractHeader(mixed $file): array|false
{
if (is_resource($file)) {
$headers = fgetcsv($file);
}
if (is_string($file)) {
if (!file_exists($file)) {
return false;
}
try {
$file = fopen($file, 'r');
$headers = fgetcsv($file);
} finally {
fclose($file);
}
}

if ($this->isValidCsvFileFormat($headers) !== false) {
return $headers;
}

return false;
}
}
33 changes: 33 additions & 0 deletions tests/unit/FileHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public function shouldThrowExceptionIfFileIsNotWritable()
$this->expectException(FileHandlerException::class);
$this->expectExceptionMessage('Error writing to file');
$this->fileHandler->write(data: "hello world");
$this->fileHandler->close();
}


Expand Down Expand Up @@ -107,6 +108,7 @@ public function multipleFileCanBeWrittenSimultaneously()
$this->assertEquals("hello world", file_get_contents(filename: 'file'));

$this->assertEquals("hello world", file_get_contents(filename: 'file1'));
$this->fileHandler->close();
}


Expand Down Expand Up @@ -252,6 +254,37 @@ public function throwErrorIfFileFormatIsInvalid(string $file)
}
}

#[Test]
public function findAndReplaceInCsvMethodShouldReplaceTextUsingColumnOption()
{
$fileHandler = new FileHandler();

$hasReplaced = $fileHandler->findAndReplaceInCsv("movie.csv", "Twilight", "Inception", "Film");

$this->assertTrue($hasReplaced);


$data = $this->fileHandler->open("movie.csv")->searchInCsvFile("Inception", "Film", FileHandler::ARRAY_FORMAT);

$this->assertEquals($data["Film"], "Inception");
}

#[Test]
public function findAndReplaceInCsvMethodShouldReplaceTextWithoutColumnOption()
{
$fileHandler = new FileHandler();


$hasReplaced = $fileHandler->findAndReplaceInCsv("movie.csv", "Inception", "Twilight");

$this->assertTrue($hasReplaced);


$data = $this->fileHandler->open("movie.csv")->searchInCsvFile("Twilight", "Film", FileHandler::ARRAY_FORMAT);

$this->assertEquals($data["Film"], "Twilight");
}

// Data Providers

public static function provideStudioNames(): iterable
Expand Down

0 comments on commit 55db14e

Please sign in to comment.