Skip to content

Commit

Permalink
Merge pull request #3 from infocyph/feature/code-refactor
Browse files Browse the repository at this point in the history
♻️ refactor(file-manager): improve locking and writing logic for file…
  • Loading branch information
abmmhasan authored Nov 18, 2024
2 parents 6c6d35b + 77d3de4 commit 325a1e2
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 65 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ vendor
composer.lock
git-story_media
test.php
diff.txt
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

Pathwise is a robust PHP library designed for streamlined file and directory management. With features like safe reading/writing, metadata extraction, path utilities, compression, and permission management, it ensures a developer-friendly experience while handling complex file operations.



## **Table of Contents**
1. [Introduction](#pathwise-file-management-made-simple)
2. [Prerequisites](#prerequisites)
Expand All @@ -33,8 +31,6 @@ Pathwise is a robust PHP library designed for streamlined file and directory man
9. [Support](#support)
10. [License](#license)



## **Prerequisites**
- Language: PHP 8.2/+

Expand Down
2 changes: 1 addition & 1 deletion src/FileManager/SafeFileReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ private function applyLock(): void
*/
public function releaseLock(): void
{
if ($this->isLocked) {
if ($this->isLocked && isset($this->file)) {
$this->file->flock(LOCK_UN);
$this->isLocked = false;
}
Expand Down
109 changes: 49 additions & 60 deletions src/FileManager/SafeFileWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private function initiate(string $mode = 'w'): void
* @param int $delay Delay between retries in milliseconds (used only if $waitForLock is true).
* @throws FileAccessException If lock could not be acquired.
*/
public function lock(int $lockType = LOCK_EX, bool $waitForLock = false, int $retries = 3, int $delay = 100): void
public function lock(int $lockType = LOCK_EX, bool $waitForLock = false, int $retries = 5, int $delay = 200): void
{
$this->initiate($this->append ? 'a' : 'w');
$attempt = 0;
Expand All @@ -80,12 +80,11 @@ public function lock(int $lockType = LOCK_EX, bool $waitForLock = false, int $re
$this->isLocked = true;
return;
}
if ($waitForLock) {
usleep($delay * 1000); // Convert milliseconds to microseconds
$attempt++;
} else {
if (!$waitForLock) {
break;
}
usleep($delay * 1000);
$attempt++;
} while ($attempt < $retries);

throw new FileAccessException("Failed to acquire lock on file {$this->filename} after $retries attempts.");
Expand Down Expand Up @@ -120,31 +119,20 @@ public function unlock(): void
public function __call(string $type, array $params)
{
$this->initiate($this->append ? 'a' : 'w');

try {
if (!$this->isLocked) {
$this->lock(); // Non-blocking by default
}

$returnable = match ($type) {
'character' => $this->writeCharacter(...$params),
'line' => $this->writeLine(...$params),
'csv' => $this->writeCSV(...$params),
'binary' => $this->writeBinary(...$params),
'json' => $this->writeJSON(...$params),
'regex' => $this->writePatternMatch(...$params),
'fixedWidth' => $this->writeFixedWidth(...$params),
'xml' => $this->writeXML(...$params),
'serialized' => $this->writeSerialized(...$params),
'jsonArray' => $this->writeJSONArray(...$params),
default => throw new Exception("Unknown write type '$type'"),
};

$this->trackWriteType($type);
} finally {
$this->unlock(); // Ensure unlock even if an exception occurs
}

$returnable = match ($type) {
'character' => $this->writeCharacter(...$params),
'line' => $this->writeLine(...$params),
'csv' => $this->writeCSV(...$params),
'binary' => $this->writeBinary(...$params),
'json' => $this->writeJSON(...$params),
'regex' => $this->writePatternMatch(...$params),
'fixedWidth' => $this->writeFixedWidth(...$params),
'xml' => $this->writeXML(...$params),
'serialized' => $this->writeSerialized(...$params),
'jsonArray' => $this->writeJSONArray(...$params),
default => throw new Exception("Unknown write type '$type'"),
};
$this->trackWriteType($type);
return $returnable;
}

Expand All @@ -155,12 +143,12 @@ public function __call(string $type, array $params)
* The write count is incremented after writing the data.
*
* @param string $char The character to write to the file.
* @return bool True if writing is successful, false otherwise.
* @return int|false The number of bytes written, or false on failure.
*/
private function writeCharacter(string $char): bool
private function writeCharacter(string $char): int|false
{
$this->writeCount++;
return (bool)$this->file->fwrite($char);
return $this->file->fwrite($char);
}

/**
Expand All @@ -171,12 +159,12 @@ private function writeCharacter(string $char): bool
* The write count is incremented after writing the data.
*
* @param string $content The content to write to the file.
* @return bool True if writing is successful, false otherwise.
* @return int|false The number of bytes written, or false on failure.
*/
private function writeLine(string $content): bool
private function writeLine(string $content): int|false
{
$this->writeCount++;
return (bool)$this->file->fwrite($content . PHP_EOL);
return $this->file->fwrite($content . PHP_EOL);
}

/**
Expand All @@ -186,12 +174,12 @@ private function writeLine(string $content): bool
* The write count is incremented after writing the data.
*
* @param string $data The binary data to write.
* @return bool True if writing is successful, false otherwise.
* @return int|false The number of bytes written, or false on failure.
*/
private function writeBinary(string $data): bool
private function writeBinary(string $data): int|false
{
$this->writeCount++;
return (bool)$this->file->fwrite($data);
return $this->file->fwrite($data);
}

/**
Expand All @@ -205,16 +193,16 @@ private function writeBinary(string $data): bool
* @param string $separator The character used to separate fields. Defaults to ','.
* @param string $enclosure The character used to enclose fields. Defaults to '"'.
* @param string $escape The character used to escape special characters. Defaults to '\\'.
* @return bool True if writing is successful, false on failure.
* @return int|false The number of bytes written, or false on failure.
*/
private function writeCSV(
array $row,
string $separator = ",",
string $enclosure = "\"",
string $escape = "\\",
): bool {
): int|false {
$this->writeCount++;
return (bool)$this->file->fputcsv($row, $separator, $enclosure, $escape);
return $this->file->fputcsv($row, $separator, $enclosure, $escape);
}

/**
Expand All @@ -225,18 +213,18 @@ private function writeCSV(
*
* @param mixed $data The data to encode as JSON and write.
* @param bool $prettyPrint If true, the JSON will be formatted for readability. Defaults to false.
* @return bool True if writing is successful, false on failure.
* @return int|false The number of bytes written, or false on failure.
* @throws Exception If JSON encoding fails.
*/
private function writeJSON(mixed $data, bool $prettyPrint = false): bool
private function writeJSON(mixed $data, bool $prettyPrint = false): int|false
{
$jsonOptions = $prettyPrint ? JSON_PRETTY_PRINT : 0;
$jsonData = json_encode($data, $jsonOptions);
if ($jsonData === false) {
throw new Exception("JSON encoding failed: " . json_last_error_msg());
}
$this->writeCount++;
return (bool)$this->file->fwrite($jsonData . PHP_EOL);
return $this->file->fwrite($jsonData . PHP_EOL);
}

/**
Expand All @@ -248,13 +236,13 @@ private function writeJSON(mixed $data, bool $prettyPrint = false): bool
*
* @param string $content The content to be checked and potentially written.
* @param string $pattern The regex pattern to match against the content.
* @return bool True if the content was written to the file, false otherwise.
* @return int|false The number of bytes written, or false on failure.
*/
private function writePatternMatch(string $content, string $pattern): bool
private function writePatternMatch(string $content, string $pattern): int|false
{
if (preg_match($pattern, $content)) {
$this->writeCount++;
return (bool)$this->file->fwrite($content . PHP_EOL);
return $this->file->fwrite($content . PHP_EOL);
}
return false;
}
Expand All @@ -267,10 +255,10 @@ private function writePatternMatch(string $content, string $pattern): bool
*
* @param array $data The data to write. Each element is written as a string.
* @param array $widths The widths of each field. Each element is a positive integer.
* @return bool True if writing is successful, false on failure.
* @return int|false The number of bytes written, or false on failure.
* @throws Exception If the count of $data does not match the count of $widths.
*/
private function writeFixedWidth(array $data, array $widths): bool
private function writeFixedWidth(array $data, array $widths): int|false
{
if (count($data) !== count($widths)) {
throw new Exception("Data and widths arrays must match.");
Expand All @@ -280,7 +268,7 @@ private function writeFixedWidth(array $data, array $widths): bool
$line .= str_pad((string) $field, $widths[$index]);
}
$this->writeCount++;
return (bool)$this->file->fwrite($line . PHP_EOL);
return $this->file->fwrite($line . PHP_EOL);
}

/**
Expand All @@ -290,12 +278,12 @@ private function writeFixedWidth(array $data, array $widths): bool
* and writes it to the file, appending a newline character.
*
* @param SimpleXMLElement $element The XML element to write.
* @return bool True if to write was successful, false on failure.
* @return int|false The number of bytes written, or false on failure.
*/
private function writeXML(SimpleXMLElement $element): bool
private function writeXML(SimpleXMLElement $element): int|false
{
$this->writeCount++;
return (bool)$this->file->fwrite($element->asXML() . PHP_EOL);
return $this->file->fwrite($element->asXML() . PHP_EOL);
}

/**
Expand All @@ -306,13 +294,13 @@ private function writeXML(SimpleXMLElement $element): bool
* followed by a newline.
*
* @param mixed $data The data to serialize and write.
* @return bool True if to write was successful, false on failure.
* @return int|false The number of bytes written, or false on failure.
*/
private function writeSerialized(mixed $data): bool
private function writeSerialized(mixed $data): int|false
{
$serializedData = serialize($data);
$this->writeCount++;
return (bool)$this->file->fwrite($serializedData . PHP_EOL);
return $this->file->fwrite($serializedData . PHP_EOL);
}

/**
Expand All @@ -321,18 +309,18 @@ private function writeSerialized(mixed $data): bool
* @param array $data The array of data to write.
* @param bool $prettyPrint If true, the JSON will be formatted with
* indentation and whitespace for readability. Defaults to false.
* @return bool True if to write was successful, false on failure.
* @return int|false The number of bytes written, or false on failure.
* @throws Exception If the JSON encoding fails.
*/
private function writeJSONArray(array $data, bool $prettyPrint = false): bool
private function writeJSONArray(array $data, bool $prettyPrint = false): int|false
{
$jsonOptions = $prettyPrint ? JSON_PRETTY_PRINT : 0;
$jsonData = json_encode($data, $jsonOptions);
if ($jsonData === false) {
throw new Exception("JSON encoding failed: " . json_last_error_msg());
}
$this->writeCount++;
return (bool)$this->file->fwrite($jsonData . PHP_EOL);
return $this->file->fwrite($jsonData . PHP_EOL);
}

/**
Expand Down Expand Up @@ -418,6 +406,7 @@ public function getCreationDate(): DateTime
* Closes the file and releases any system resources associated with it.
*
* Called automatically when the object is no longer referenced.
* @throws FileAccessException
*/
public function __destruct()
{
Expand Down

0 comments on commit 325a1e2

Please sign in to comment.