Skip to content

Commit

Permalink
Add FilesystemService
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusklocke committed Dec 19, 2023
1 parent fac20cb commit 4df7ceb
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 37 deletions.
16 changes: 8 additions & 8 deletions src/Infrastructure/API/Logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
namespace HexagonalPlayground\Infrastructure\API;

use InvalidArgumentException;
use Psr\Http\Message\StreamInterface;
use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;

class Logger extends AbstractLogger
{
/** @var resource */
private $stream;
private StreamInterface $stream;

/** @var string */
private string $minLevel;
Expand All @@ -26,13 +26,13 @@ class Logger extends AbstractLogger
];

/**
* @param resource $stream
* @param StreamInterface $stream
* @param string $minLevel
*/
public function __construct($stream, string $minLevel)
public function __construct(StreamInterface $stream, string $minLevel)
{
if (!is_resource($stream)) {
throw new InvalidArgumentException('Invalid argument: stream is not a resource');
if (!$stream->isWritable()) {
throw new InvalidArgumentException('Invalid argument: stream is not writable');
}

if (!array_key_exists($minLevel, self::$severityMap)) {
Expand All @@ -49,7 +49,7 @@ public function __construct($stream, string $minLevel)
* @param array $context
* @return void
*/
public function log($level, $message, array $context = array())
public function log($level, $message, array $context = array()): void
{
if (self::$severityMap[$level] < self::$severityMap[$this->minLevel]) {
return;
Expand All @@ -63,6 +63,6 @@ public function log($level, $message, array $context = array())
}
$line .= PHP_EOL;

fwrite($this->stream, $line);
$this->stream->write($line);
}
}
10 changes: 7 additions & 3 deletions src/Infrastructure/API/LoggerProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use DI;
use HexagonalPlayground\Application\ServiceProviderInterface;
use HexagonalPlayground\Infrastructure\Config;
use HexagonalPlayground\Infrastructure\Filesystem\FilesystemService;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;

Expand All @@ -17,10 +18,13 @@ public function getDefinitions(): array
LoggerInterface::class => DI\factory(function (ContainerInterface $container) {
/** @var Config $config */
$config = $container->get(Config::class);
$stream = fopen($config->logPath, 'w');
$logLevel = $config->logLevel;
/** @var FilesystemService $filesystem */
$filesystem = $container->get(FilesystemService::class);

return new Logger($stream, $logLevel);
return new Logger(
$filesystem->openFile($config->logPath, 'w'),
$config->logLevel
);
})
];
}
Expand Down
10 changes: 6 additions & 4 deletions src/Infrastructure/CLI/SetupEnvCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace HexagonalPlayground\Infrastructure\CLI;

use HexagonalPlayground\Infrastructure\Filesystem\FileStream;
use HexagonalPlayground\Infrastructure\Filesystem\FilesystemService;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

Expand All @@ -17,6 +18,8 @@ protected function configure(): void

protected function execute(InputInterface $input, OutputInterface $output): int
{
/** @var FilesystemService $filesystem */
$filesystem = $this->container->get(FilesystemService::class);
$io = $this->getStyledIO($input, $output);

$env = [];
Expand All @@ -32,16 +35,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$env['EMAIL_URL'] = $io->ask('Enter URL to use for sending email', 'smtp://maildev:25?verify_peer=0');
$env['JWT_SECRET'] = bin2hex(random_bytes(32));

$envPath = join(DIRECTORY_SEPARATOR, [$this->container->get('app.home'), '.env']);

if (is_writeable($envPath)) {
$envPath = $filesystem->joinPaths([$this->container->get('app.home'), '.env']);
if ($filesystem->isWritable($envPath)) {
$confirmed = $io->confirm(
'Your .env file seems to be writeable. Do want to write your configuration directly?',
false
);

if ($confirmed) {
$stream = new FileStream($envPath, 'w');
$stream = $filesystem->openFile($envPath, 'w');

foreach ($env as $name => $value) {
$stream->write("$name=$value\n");
Expand Down
11 changes: 7 additions & 4 deletions src/Infrastructure/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use DI;
use HexagonalPlayground\Application\ServiceProviderInterface;
use HexagonalPlayground\Infrastructure\Filesystem\FilesystemService;
use Psr\Container\ContainerInterface;

class ContainerBuilder
Expand All @@ -17,15 +18,17 @@ public static function build(array $serviceProviders, string $version): Containe
{
$builder = new DI\ContainerBuilder();
$builder->useAutowiring(true);
$builder->addDefinitions([
'app.home' => realpath(join(DIRECTORY_SEPARATOR, [__DIR__ , '..', '..'])),
'app.version' => $version
]);

foreach ($serviceProviders as $provider) {
$builder->addDefinitions($provider->getDefinitions());
}

$filesystem = new FilesystemService();
$builder->addDefinitions([
'app.home' => $filesystem->getRealPath($filesystem->joinPaths([__DIR__ , '..', '..'])),
'app.version' => $version
]);

return $builder->build();
}
}
198 changes: 198 additions & 0 deletions src/Infrastructure/Filesystem/FilesystemService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<?php
declare(strict_types=1);

namespace HexagonalPlayground\Infrastructure\Filesystem;

use HexagonalPlayground\Domain\Exception\InternalException;
use Iterator;
use Nyholm\Psr7\Stream;
use Psr\Http\Message\StreamInterface;

class FilesystemService
{
/**
* Create a directory (non-recursive)
*
* @param string $path
* @return void
* @throws InternalException
*/
public function createDirectory(string $path): void
{
if (false === mkdir($path)) {
throw new InternalException("Failed to create directory $path");
}
}

/**
* Create a file (directory must exist)
*
* @param string $path
* @param string $data
* @return void
* @throws InternalException
*/
public function createFile(string $path, string $data = ''): void
{
if (false === file_put_contents($path, $data)) {
throw new InternalException("Failed to create file $path");
}
}

/**
* Delete a directory (must be empty)
*
* @param string $path
* @return void
* @throws InternalException
*/
public function deleteDirectory(string $path): void
{
if (!file_exists($path)) {
return;
}

if (!is_dir($path)) {
throw new InternalException("Failed to delete $path: Not a directory");
}

if (false === rmdir($path)) {
throw new InternalException("Failed to delete directory $path");
}
}

/**
* Delete a file
*
* @param string $path
* @return void
* @throws InternalException
*/
public function deleteFile(string $path): void
{
if (!file_exists($path)) {
return;
}

if (!is_file($path)) {
throw new InternalException("Failed to delete $path: Not a file");
}

if (false === unlink($path)) {
throw new InternalException("Failed to delete file $path");
}
}

/**
* List contents of a directory
*
* @param string $path
* @return array
* @throws InternalException
*/
public function getDirectoryContents(string $path): array
{
return iterator_to_array($this->getDirectoryIterator($path));
}

/**
* Create an Iterator representing a directory's content
*
* @param string $path
* @return Iterator
* @throws InternalException
*/
public function getDirectoryIterator(string $path): Iterator
{
$handle = opendir($path);
if (!is_resource($handle)) {
throw new InternalException("Failed to open directory $path");
}

while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
yield $entry;
}
}
closedir($handle);
}

/**
* Read a file entirely into memory
*
* @param string $path
* @return string
* @throws InternalException
*/
public function getFileContents(string $path): string
{
$data = file_get_contents($path);
if ($data === false) {
throw new InternalException("Failed to read file $path");
}

return $data;
}

/**
* Returns absolute path (resolving symlinks and dots)
*
* @param string $path
* @return string
*/
public function getRealPath(string $path): string
{
return realpath($path);
}

/**
* Determines if there is a directory at the specified path
*
* @param string $path
* @return bool
*/
public function isDirectory(string $path): bool
{
return is_dir($path);
}

/**
* Determines if the file/directory at the specified path is writable
*
* @param string $path
* @return bool
*/
public function isWritable(string $path): bool
{
return is_writable($path);
}

/**
* Joins multiple path segment into a single path (not resolving symlink or dots)
*
* @param array $paths
* @return string
*/
public function joinPaths(array $paths): string
{
return join(DIRECTORY_SEPARATOR, $paths);
}

/**
* Opens a file and returns a stream
*
* @param string $path
* @param string $mode
* @return StreamInterface
* @throws InternalException
*/
public function openFile(string $path, string $mode): StreamInterface
{
$resource = fopen($path, $mode);
if (!is_resource($resource)) {
throw new InternalException("Failed to open file $path in mode '$mode'");
}

return new Stream($resource);
}
}
11 changes: 9 additions & 2 deletions src/Infrastructure/Filesystem/TeamLogoRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@

class TeamLogoRepository
{
private FilesystemService $filesystemService;

public function __construct(FilesystemService $filesystemService)
{
$this->filesystemService = $filesystemService;
}

public function delete(string $logoId): void
{
unlink($this->generateStoragePath($logoId));
$this->filesystemService->deleteFile($this->generateStoragePath($logoId));
}

public function generatePublicPath(string $logoId): string
Expand All @@ -21,7 +28,7 @@ public function generatePublicPath(string $logoId): string

private function generateStoragePath(string $logoId): string
{
return join(DIRECTORY_SEPARATOR, [Config::getInstance()->appLogosPath, "$logoId.webp"]);
return $this->filesystemService->joinPaths([Config::getInstance()->appLogosPath, "$logoId.webp"]);
}

public function save(UploadedFileInterface $uploadedFile): string
Expand Down
Loading

0 comments on commit 4df7ceb

Please sign in to comment.