Skip to content

Commit

Permalink
[Filesystem] Introduce filesystem component
Browse files Browse the repository at this point in the history
  • Loading branch information
azjezz committed Feb 23, 2021
1 parent c34f75b commit 1617047
Show file tree
Hide file tree
Showing 26 changed files with 802 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/Psl/Filesystem/Exception/ExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem\Exception;

use Psl\Exception;

interface ExceptionInterface extends Exception\ExceptionInterface
{
}
11 changes: 11 additions & 0 deletions src/Psl/Filesystem/Exception/RuntimeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem\Exception;

use Psl\Exception;

class RuntimeException extends Exception\RuntimeException implements ExceptionInterface
{
}
45 changes: 45 additions & 0 deletions src/Psl/Filesystem/Internal/change_group.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem\Internal;

use FilesystemIterator;
use Psl\Filesystem;
use Psl\Filesystem\Exception;
use Psl\Internal;
use Psl\Str;

use function chgrp;
use function lchgrp;

/**
* @param iterable<string> $files
* @param string|int $group
*
* @throws Exception\RuntimeException If unable to change the ownership for
* the given file.
*/
function change_group(iterable $files, $group, bool $recursive = false): void
{
foreach ($files as $file) {
if ($recursive && Filesystem\is_directory($file) && !Filesystem\is_link($file)) {
change_group(new FilesystemIterator($file), $group, true);
}

if (Filesystem\is_link($file)) {
$fun = static fn(): bool => lchgrp($file, $group);
} else {
$fun = static fn(): bool => chgrp($file, $group);
}

[$success, $error] = Internal\box($fun);
if (!$success) {
throw new Exception\RuntimeException(Str\format(
'Failed to change the group for file "%s": %s',
$file,
$error ?? 'internal error.',
));
}
}
}
45 changes: 45 additions & 0 deletions src/Psl/Filesystem/Internal/change_owner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem\Internal;

use FilesystemIterator;
use Psl\Filesystem;
use Psl\Filesystem\Exception;
use Psl\Internal;
use Psl\Str;

use function chown;
use function lchown;

/**
* @param iterable<string> $files
* @param string|int $user
*
* @throws Exception\RuntimeException If unable to change the ownership for
* the given file.
*/
function change_owner(iterable $files, $user, bool $recursive = false): void
{
foreach ($files as $file) {
if ($recursive && Filesystem\is_directory($file) && !Filesystem\is_link($file)) {
change_owner(new FilesystemIterator($file), $user, true);
}

if (Filesystem\is_link($file)) {
$fun = static fn(): bool => lchown($file, $user);
} else {
$fun = static fn(): bool => chown($file, $user);
}

[$success, $error] = Internal\box($fun);
if (!$success) {
throw new Exception\RuntimeException(Str\format(
'Failed to change owner for file "%s": %s',
$file,
$error ?? 'internal error.',
));
}
}
}
37 changes: 37 additions & 0 deletions src/Psl/Filesystem/Internal/change_permissions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem\Internal;

use FilesystemIterator;
use Psl\Filesystem;
use Psl\Filesystem\Exception;
use Psl\Internal;
use Psl\Str;

use function chmod;

/**
* @param iterable<string> $files
*
* @throws Exception\RuntimeException If unable to change the ownership for
* the given file.
*/
function change_permissions(iterable $files, int $permission, bool $recursive = false): void
{
foreach ($files as $file) {
if ($recursive && Filesystem\is_directory($file) && !Filesystem\is_link($file)) {
change_permissions(new FilesystemIterator($file), $permission, true);
}

[$success, $error] = Internal\box(static fn(): bool => chmod($file, $permission));
if (!$success) {
throw new Exception\RuntimeException(Str\format(
'Failed to change permissions for file "%s": %s',
$file,
$error ?? 'internal error.',
));
}
}
}
22 changes: 22 additions & 0 deletions src/Psl/Filesystem/change_group.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem;

use Psl;

/**
* Change the group ownership of $filename.
*
* @param bool $recursive Whether change the group ownership recursively or not.
*
* @throws Exception\RuntimeException If unable to change the group ownership for $filename.
* @throws Psl\Exception\InvariantViolationException If $filename does not exist.
*/
function change_group(string $filename, int $group, bool $recursive = false): void
{
Psl\invariant(exists($filename), '$filename does not exists.');

Internal\change_owner([$filename], $group, $recursive);
}
22 changes: 22 additions & 0 deletions src/Psl/Filesystem/change_owner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem;

use Psl;

/**
* Change the owner of $filename.
*
* @param bool $recursive Whether change the owner recursively or not.
*
* @throws Exception\RuntimeException If unable to change the ownership for $filename.
* @throws Psl\Exception\InvariantViolationException If $filename does not exist.
*/
function change_owner(string $filename, int $user, bool $recursive = false): void
{
Psl\invariant(exists($filename), '$filename does not exist.');

Internal\change_owner([$filename], $user, $recursive);
}
22 changes: 22 additions & 0 deletions src/Psl/Filesystem/change_permissions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem;

use Psl;

/**
* Changes mode permission of $filename.
*
* @param bool $recursive Whether change the mode recursively or not.
*
* @throws Exception\RuntimeException If unable to change the mode for the given $filename.
* @throws Psl\Exception\InvariantViolationException If $filename does not exists.
*/
function change_permissions(string $filename, int $permissions, bool $recursive = false): void
{
Psl\invariant(exists($filename), '$filename does not exist.');

Internal\change_permissions([$filename], $permissions, $recursive);
}
67 changes: 67 additions & 0 deletions src/Psl/Filesystem/copy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem;

use Psl;
use Psl\Internal;
use Psl\Str;

use function fclose;
use function fopen;
use function stream_copy_to_stream;

/**
* Change the group ownership of $filename.
*
*
* @throws Exception\RuntimeException If unable to change the group ownership for $filename.
* @throws Psl\Exception\InvariantViolationException If $source does not exist or is not readable.
*/
function copy(string $source, string $destination, bool $overwrite = false): void
{
Psl\invariant(is_file($source) && is_readable($source), '$source does not exist or is not readable.');

if (!$overwrite && is_file($destination)) {
return;
}

/**
* @psalm-suppress InvalidArgument - callable is not pure..
*/
$source_stream = Internal\suppress(static fn() => fopen($source, 'rb'));
if (false === $source_stream) {
throw new Exception\RuntimeException('Failed to open $source file for reading');
}

/**
* @psalm-suppress InvalidArgument - callable is not pure..
*/
$destination_stream = Internal\suppress(static fn() => fopen($destination, 'wb'));
if (false === $destination_stream) {
throw new Exception\RuntimeException('Failed to open $destination file for writing.');
}

$copied_bytes = stream_copy_to_stream($source_stream, $destination_stream);
fclose($source_stream);
fclose($destination_stream);

$total_bytes = file_size($source);

// preserve executable permission bits
change_permissions(
$destination,
get_permissions($destination) | (get_permissions($source) & 0111)
);

if ($copied_bytes !== $total_bytes) {
throw new Exception\RuntimeException(Str\format(
'Failed to copy the whole content of "%s" to "%s" ( %g of %g bytes copied ).',
$source,
$destination,
$copied_bytes,
$total_bytes
));
}
}
30 changes: 30 additions & 0 deletions src/Psl/Filesystem/create_directory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem;

use Psl\Internal;
use Psl\Str;

use function mkdir;

/**
* Create the directory specified by $directory.
*
* @throws Exception\RuntimeException If unable to create the directory.
*/
function create_directory(string $directory, int $permissions = 0777): void
{
[$result, $error_message] = Internal\box(
static fn() => mkdir($directory, $permissions, true)
);

if (false === $result && !is_directory($directory)) {
throw new Exception\RuntimeException(Str\format(
'Failed to create directory "%s": %s.',
$directory,
$error_message ?? 'internal error'
));
}
}
41 changes: 41 additions & 0 deletions src/Psl/Filesystem/create_file.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Psl\Filesystem;

use Psl\Internal;
use Psl\Str;

use function touch;

/**
* Create the file specified by $filename.
*
* @param int|null $time The touch time as a Unix timestamp,
* If not supplied the current system time is used.
*
* @param int|null $access_time The access time as a Unix timestamp,
* If not supplied the current system time is used.
*
* @throws Exception\RuntimeException If unable to create the file.
*/
function create_file(string $filename, ?int $time = null, ?int $access_time = null): void
{
if (null === $access_time && null === $time) {
$fun = static fn(): bool => touch($filename);
} elseif (null === $access_time) {
$fun = static fn(): bool => touch($filename, $time);
} else {
$fun = static fn(): bool => touch($filename, $time ?? $access_time, $access_time);
}

[$result, $error_message] = Internal\box($fun);
if (false === $result && !is_file($filename)) {
throw new Exception\RuntimeException(Str\format(
'Failed to create file "%s": %s.',
$filename,
$error_message ?? 'internal error'
));
}
}
Loading

0 comments on commit 1617047

Please sign in to comment.