Skip to content

Commit

Permalink
feat(filesystem): refactor Psl\Filesystem\write_file, `Psl\Filesyst…
Browse files Browse the repository at this point in the history
…em\append_file`, and `Psl\Filesystem\read_file` to use `Psl\File` component.

Signed-off-by: azjezz <azjezz@protonmail.com>
  • Loading branch information
azjezz committed Nov 3, 2021
1 parent 5f1071e commit 4c21fb9
Show file tree
Hide file tree
Showing 16 changed files with 80 additions and 116 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -15,3 +15,4 @@
* introduced a new `Psl\IO\Stream` component.
* refactored `Psl\IO` handles API.
* introduced a new `Psl\File` component.
* refactor `Psl\Filesystem\write_file`, `Psl\Filesystem\append_file`, and `Psl\Filesystem\read_file` to use `Psl\File` component.
6 changes: 3 additions & 3 deletions docs/component/filesystem.md
Expand Up @@ -16,7 +16,7 @@

#### `Functions`

- [append_file](./../../src/Psl/Filesystem/append_file.php#L18)
- [append_file](./../../src/Psl/Filesystem/append_file.php#L21)
- [canonicalize](./../../src/Psl/Filesystem/canonicalize.php#L15)
- [change_group](./../../src/Psl/Filesystem/change_group.php#L20)
- [change_owner](./../../src/Psl/Filesystem/change_owner.php#L20)
Expand Down Expand Up @@ -49,8 +49,8 @@
- [is_symbolic_link](./../../src/Psl/Filesystem/is_symbolic_link.php#L19)
- [is_writable](./../../src/Psl/Filesystem/is_writable.php#L20)
- [read_directory](./../../src/Psl/Filesystem/read_directory.php#L19)
- [read_file](./../../src/Psl/Filesystem/read_file.php#L24)
- [read_file](./../../src/Psl/Filesystem/read_file.php#L23)
- [read_symbolic_link](./../../src/Psl/Filesystem/read_symbolic_link.php#L21)
- [write_file](./../../src/Psl/Filesystem/write_file.php#L18)
- [write_file](./../../src/Psl/Filesystem/write_file.php#L21)


2 changes: 1 addition & 1 deletion docs/documenter.php
Expand Up @@ -12,7 +12,7 @@
use Psl\Type;
use Psl\Vec;

require_once __DIR__ . "/../src/bootstrap.php";
require_once __DIR__ . "/../vendor/autoload.php";

(static function (array $args) {
$command = Str\lowercase($args[1] ?? 'regenerate');
Expand Down
4 changes: 2 additions & 2 deletions src/Psl/File/ReadHandle.php
Expand Up @@ -22,8 +22,8 @@ final class ReadHandle extends Internal\AbstractHandleWrapper implements ReadHan
*/
public function __construct(string $path)
{
Psl\invariant(Filesystem\is_file($path), '$filename is not a file.');
Psl\invariant(Filesystem\is_readable($path), '$filename is not readable.');
Psl\invariant(Filesystem\is_file($path), 'File "%s" is not a file.', $path);
Psl\invariant(Filesystem\is_readable($path), 'File "%s" is not readable.', $path);

$this->readHandle = Internal\open($path, 'r', read: true, write: false);

Expand Down
8 changes: 4 additions & 4 deletions src/Psl/File/ReadWriteHandle.php
Expand Up @@ -25,21 +25,21 @@ final class ReadWriteHandle extends Internal\AbstractHandleWrapper implements Re
public function __construct(string $path, WriteMode $write_mode = WriteMode::OPEN_OR_CREATE)
{
$is_file = Filesystem\is_file($path);
Psl\invariant(!Filesystem\exists($path) || $is_file, '$path points to a non-file node.');
Psl\invariant(!Filesystem\exists($path) || $is_file, 'File "%s" is not a file.', $path);

$open_or_create = $write_mode === WriteMode::OPEN_OR_CREATE;
$must_create = $write_mode === WriteMode::MUST_CREATE;
if ($must_create && $is_file) {
Psl\invariant_violation('$path already exists.');
Psl\invariant_violation('File "%s" already exists.', $path);
}

$creating = $open_or_create || $must_create;
if (!$creating && !$is_file) {
Psl\invariant_violation('$path does not exist.');
Psl\invariant_violation('File "%s" does not exist.', $path);
}

if ((!$creating || ($open_or_create && $is_file)) && !Filesystem\is_writable($path)) {
Psl\invariant_violation('$path is not writable.');
Psl\invariant_violation('File "%s" is not writable.', $path);
}

if ($creating && !$is_file) {
Expand Down
8 changes: 4 additions & 4 deletions src/Psl/File/WriteHandle.php
Expand Up @@ -24,21 +24,21 @@ final class WriteHandle extends Internal\AbstractHandleWrapper implements WriteH
public function __construct(string $path, WriteMode $write_mode = WriteMode::OPEN_OR_CREATE)
{
$is_file = Filesystem\is_file($path);
Psl\invariant(!Filesystem\exists($path) || $is_file, '$path points to a non-file node.');
Psl\invariant(!Filesystem\exists($path) || $is_file, 'File "%s" is not a file.', $path);

$open_or_create = $write_mode === WriteMode::OPEN_OR_CREATE;
$must_create = $write_mode === WriteMode::MUST_CREATE;
if ($must_create && $is_file) {
Psl\invariant_violation('$path already exists.');
Psl\invariant_violation('File "%s" already exists.', $path);
}

$creating = $open_or_create || $must_create;
if (!$creating && !$is_file) {
Psl\invariant_violation('$path does not exist.');
Psl\invariant_violation('File "%s" does not exist.', $path);
}

if ((!$creating || ($open_or_create && $is_file)) && !Filesystem\is_writable($path)) {
Psl\invariant_violation('$path is not writable.');
Psl\invariant_violation('File "%s" is not writable.', $path);
}

/**
Expand Down
67 changes: 0 additions & 67 deletions src/Psl/Filesystem/Internal/write_file.php

This file was deleted.

18 changes: 17 additions & 1 deletion src/Psl/Filesystem/append_file.php
Expand Up @@ -5,6 +5,9 @@
namespace Psl\Filesystem;

use Psl;
use Psl\File;
use Psl\IO;
use Psl\Str;

/**
* Append $content to $file.
Expand All @@ -17,5 +20,18 @@
*/
function append_file(string $file, string $content): void
{
Internal\write_file($file, $content, true);
try {
$handle = File\open_write_only($file, File\WriteMode::APPEND);
$lock = $handle->lock(File\LockType::EXCLUSIVE);

$handle->writeAll($content);

$lock->release();
$handle->close();
} catch (File\Exception\ExceptionInterface | IO\Exception\ExceptionInterface $previous) {
throw new Exception\RuntimeException(Str\format(
'Failed to write to file "%s".',
$file,
), 0, $previous);
}
}
39 changes: 15 additions & 24 deletions src/Psl/Filesystem/read_file.php
Expand Up @@ -5,11 +5,10 @@
namespace Psl\Filesystem;

use Psl;
use Psl\Internal;
use Psl\File;
use Psl\IO;
use Psl\Str;

use function file_get_contents;

/**
* Reads entire file into a string.
*
Expand All @@ -19,33 +18,25 @@
*
* @throws Psl\Exception\InvariantViolationException If the file specified by
* $file does not exist, or is not readable.
* @throws Exception\RuntimeException If an error
* @throws Exception\RuntimeException In case of an error.
*/
function read_file(string $file, int $offset = 0, ?int $length = null): string
{
Psl\invariant(exists($file), 'File "%s" does not exist.', $file);
Psl\invariant(is_file($file), 'File "%s" is not a file.', $file);
Psl\invariant(is_readable($file), 'File "%s" is not readable.', $file);
try {
$handle = File\open_read_only($file);
$lock = $handle->lock(File\LockType::SHARED);

if (null === $length) {
[$content, $error] = Internal\box(
static fn() => file_get_contents($file, false, null, $offset)
);
} else {
[$content, $error] = Internal\box(
static fn() => file_get_contents($file, false, null, $offset, $length)
);
}
$handle->seek($offset);
$content = $handle->readAll($length);

// @codeCoverageIgnoreStart
if (false === $content || null !== $error) {
$lock->release();
$handle->close();

return $content;
} catch (File\Exception\ExceptionInterface | IO\Exception\ExceptionInterface $previous) {
throw new Exception\RuntimeException(Str\format(
'Failed to read file "%s": %s.',
'Failed to read file "%s".',
$file,
$error ?? 'internal error',
));
), 0, $previous);
}
// @codeCoverageIgnoreEnd

return $content;
}
24 changes: 23 additions & 1 deletion src/Psl/Filesystem/write_file.php
Expand Up @@ -5,6 +5,9 @@
namespace Psl\Filesystem;

use Psl;
use Psl\File;
use Psl\IO;
use Psl\Str;

/**
* Write $content to $file.
Expand All @@ -17,5 +20,24 @@
*/
function write_file(string $file, string $content): void
{
Internal\write_file($file, $content, false);
try {
if (namespace\is_file($file)) {
$mode = File\WriteMode::TRUNCATE;
} else {
$mode = File\WriteMode::OPEN_OR_CREATE;
}

$handle = File\open_write_only($file, $mode);
$lock = $handle->lock(File\LockType::EXCLUSIVE);

$handle->writeAll($content);

$lock->release();
$handle->close();
} catch (File\Exception\ExceptionInterface | IO\Exception\ExceptionInterface $previous) {
throw new Exception\RuntimeException(Str\format(
'Failed to write to file "%s".',
$file,
), 0, $previous);
}
}
2 changes: 2 additions & 0 deletions src/Psl/IO/Internal/ResourceHandle.php
Expand Up @@ -160,6 +160,8 @@ public function seek(int $offset): void
throw new Exception\AlreadyClosedException('Handle has already been closed.');
}

Psl\invariant($offset >= 0, '$offset must be a positive-int.');

/** @psalm-suppress PossiblyInvalidArgument */
$result = @fseek($this->resource, $offset);
if (0 !== $result) {
Expand Down
2 changes: 1 addition & 1 deletion src/Psl/IO/SeekHandleInterface.php
Expand Up @@ -17,7 +17,7 @@ interface SeekHandleInterface extends HandleInterface
* Offset is relative to the start of the handle - so, the beginning of the
* handle is always offset 0.
*
* @throws Psl\Exception\InvalidArgumentException If $offset is negative.
* @throws Psl\Exception\InvariantViolationException If $offset is negative.
* @throws Exception\AlreadyClosedException If the handle has been already closed.
* @throws Exception\RuntimeException If an error occurred during the operation.
*/
Expand Down
1 change: 0 additions & 1 deletion src/Psl/Internal/Loader.php
Expand Up @@ -391,7 +391,6 @@ final class Loader
'Psl\Html\decode',
'Psl\Html\decode_special_characters',
'Psl\Html\strip_tags',
'Psl\Filesystem\Internal\write_file',
'Psl\Filesystem\change_group',
'Psl\Filesystem\change_owner',
'Psl\Filesystem\change_permissions',
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/File/ReadWriteHandleTest.php
Expand Up @@ -62,15 +62,15 @@ public function testReading(): void
public function testMustCreateExistingFile(): void
{
$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('$path already exists.');
$this->expectExceptionMessage('already exists.');

new File\ReadWriteHandle(__FILE__, File\WriteMode::MUST_CREATE);
}

public function testAppendToNonExistingFile(): void
{
$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('$path does not exist.');
$this->expectExceptionMessage('does not exist.');

new File\ReadWriteHandle(__FILE__ . '.fake', File\WriteMode::APPEND);
}
Expand All @@ -81,7 +81,7 @@ public function testAppendToANonWritableFile(): void
Filesystem\change_permissions($temporary_file, 0555);

$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('$path is not writable.');
$this->expectExceptionMessage('is not writable.');

new File\ReadWriteHandle($temporary_file, File\WriteMode::APPEND);
}
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/File/WriteHandleTest.php
Expand Up @@ -14,15 +14,15 @@ final class WriteHandleTest extends TestCase
public function testMustCreateExistingFile(): void
{
$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('$path already exists.');
$this->expectExceptionMessage('already exists.');

new File\WriteHandle(__FILE__, File\WriteMode::MUST_CREATE);
}

public function testAppendToNonExistingFile(): void
{
$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('$path does not exist.');
$this->expectExceptionMessage('does not exist.');

$f = new File\WriteHandle(__FILE__ . '.fake', File\WriteMode::APPEND);
$f->write('g');
Expand All @@ -34,7 +34,7 @@ public function testAppendToANonWritableFile(): void
Filesystem\change_permissions($temporary_file, 0555);

$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('$path is not writable.');
$this->expectExceptionMessage('is not writable.');

new File\WriteHandle($temporary_file, File\WriteMode::APPEND);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/Filesystem/FileTest.php
Expand Up @@ -110,7 +110,7 @@ public function testWriteFileThrowsForNonWritableFiles(): void
Filesystem\change_permissions($file, 0111);

$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('File "' . $file . '" is not writeable.');
$this->expectExceptionMessage('File "' . $file . '" is not writable.');

Filesystem\write_file($file, 'hello');
}
Expand Down

0 comments on commit 4c21fb9

Please sign in to comment.