Skip to content

Find and replace #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
find and replace feature
Signed-off-by: rahul <rcsofttech85@gmail.com>

Signed-off-by: rahul <rcsofttech85@gmail.com>
  • Loading branch information
rcsofttech85 committed Sep 14, 2023
commit 57054370c88dfcfa709a43dda035d6b275646b6f
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