Skip to content
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

Turn path prefixing into an adapter decorator #1549

Merged
merged 1 commit into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -1,36 +1,47 @@
<?php

namespace League\Flysystem;

namespace League\Flysystem\PathPrefixing;

use Generator;
use League\Flysystem\Config;
use League\Flysystem\FileAttributes;
use League\Flysystem\FilesystemAdapter;
use League\Flysystem\PathPrefixer;
use League\Flysystem\UnableToCheckDirectoryExistence;
use League\Flysystem\UnableToCheckFileExistence;
use League\Flysystem\UnableToCopyFile;
use League\Flysystem\UnableToCreateDirectory;
use League\Flysystem\UnableToDeleteDirectory;
use League\Flysystem\UnableToDeleteFile;
use League\Flysystem\UnableToMoveFile;
use League\Flysystem\UnableToReadFile;
use League\Flysystem\UnableToRetrieveMetadata;
use League\Flysystem\UnableToSetVisibility;
use League\Flysystem\UnableToWriteFile;
use Throwable;

class PathPrefixedFilesystem implements FilesystemOperator
class PathPrefixedAdapter implements FilesystemAdapter
{
protected FilesystemOperator $filesystem;
protected FilesystemAdapter $adapter;
private PathPrefixer $prefix;

/**
* @internal
*/
public function __construct(FilesystemOperator $filesystem, string $prefix)
public function __construct(FilesystemAdapter $adapter, string $prefix)
{
if (empty($prefix)) {
throw new \InvalidArgumentException('The prefix must not be empty.');
}

$this->filesystem = $filesystem;
$this->adapter = $adapter;
$this->prefix = new PathPrefixer($prefix);
}

public function has(string $location): bool
{
return $this->filesystem->has($this->preparePath($location));
}

public function read(string $location): string
{
try {
return $this->filesystem->read($this->preparePath($location));
return $this->adapter->read($this->prefix->prefixPath($location));
} catch (Throwable $previous) {
throw UnableToReadFile::fromLocation($location, $previous->getMessage(), $previous);
}
Expand All @@ -39,23 +50,23 @@ public function read(string $location): string
public function readStream(string $location)
{
try {
return $this->filesystem->readStream($this->preparePath($location));
return $this->adapter->readStream($this->prefix->prefixPath($location));
} catch (Throwable $previous) {
throw UnableToReadFile::fromLocation($location, $previous->getMessage(), $previous);
}
}

public function listContents(string $location, bool $deep = self::LIST_SHALLOW): DirectoryListing
public function listContents(string $location, bool $deep): Generator
{
return $this->filesystem->listContents($this->preparePath($location), $deep)->map(
fn(StorageAttributes $attributes) => $attributes->withPath($this->stripPath($attributes->path()))
);
foreach ($this->adapter->listContents($this->prefix->prefixPath($location), $deep) as $attributes) {
yield $attributes->withPath($this->prefix->stripPrefix($attributes->path()));
}
}

public function fileExists(string $location): bool
{
try {
return $this->filesystem->fileExists($this->preparePath($location));
return $this->adapter->fileExists($this->prefix->prefixPath($location));
} catch (Throwable $previous) {
throw UnableToCheckFileExistence::forLocation($location, $previous);
}
Expand All @@ -64,61 +75,61 @@ public function fileExists(string $location): bool
public function directoryExists(string $location): bool
{
try {
return $this->filesystem->directoryExists($this->preparePath($location));
return $this->adapter->directoryExists($this->prefix->prefixPath($location));
} catch (Throwable $previous) {
throw UnableToCheckDirectoryExistence::forLocation($location, $previous);
}
}

public function lastModified(string $path): int
public function lastModified(string $path): FileAttributes
{
try {
return $this->filesystem->lastModified($this->preparePath($path));
return $this->adapter->lastModified($this->prefix->prefixPath($path));
} catch (Throwable $previous) {
throw UnableToRetrieveMetadata::lastModified($path, $previous->getMessage(), $previous);
}
}

public function fileSize(string $path): int
public function fileSize(string $path): FileAttributes
{
try {
return $this->filesystem->fileSize($this->preparePath($path));
return $this->adapter->fileSize($this->prefix->prefixPath($path));
} catch (Throwable $previous) {
throw UnableToRetrieveMetadata::fileSize($path, $previous->getMessage(), $previous);
}
}

public function mimeType(string $path): string
public function mimeType(string $path): FileAttributes
{
try {
return $this->filesystem->mimeType($this->preparePath($path));
return $this->adapter->mimeType($this->prefix->prefixPath($path));
} catch (Throwable $previous) {
throw UnableToRetrieveMetadata::mimeType($path, $previous->getMessage(), $previous);
}
}

public function visibility(string $path): string
public function visibility(string $path): FileAttributes
{
try {
return $this->filesystem->visibility($this->preparePath($path));
return $this->adapter->visibility($this->prefix->prefixPath($path));
} catch (Throwable $previous) {
throw UnableToRetrieveMetadata::visibility($path, $previous->getMessage(), $previous);
}
}

public function write(string $location, string $contents, array $config = []): void
public function write(string $location, string $contents, Config $config): void
{
try {
$this->filesystem->write($this->preparePath($location), $contents, $config);
$this->adapter->write($this->prefix->prefixPath($location), $contents, $config);
} catch (Throwable $previous) {
throw UnableToWriteFile::atLocation($location, $previous->getMessage(), $previous);
}
}

public function writeStream(string $location, $contents, array $config = []): void
public function writeStream(string $location, $contents, Config $config): void
{
try {
$this->filesystem->writeStream($this->preparePath($location), $contents, $config);
$this->adapter->writeStream($this->prefix->prefixPath($location), $contents, $config);
} catch (Throwable $previous) {
throw UnableToWriteFile::atLocation($location, $previous->getMessage(), $previous);
}
Expand All @@ -127,7 +138,7 @@ public function writeStream(string $location, $contents, array $config = []): vo
public function setVisibility(string $path, string $visibility): void
{
try {
$this->filesystem->setVisibility($this->preparePath($path), $visibility);
$this->adapter->setVisibility($this->prefix->prefixPath($path), $visibility);
} catch (Throwable $previous) {
throw UnableToSetVisibility::atLocation($path, $previous->getMessage(), $previous);
}
Expand All @@ -136,7 +147,7 @@ public function setVisibility(string $path, string $visibility): void
public function delete(string $location): void
{
try {
$this->filesystem->delete($this->preparePath($location));
$this->adapter->delete($this->prefix->prefixPath($location));
} catch (Throwable $previous) {
throw UnableToDeleteFile::atLocation($location, $previous->getMessage(), $previous);
}
Expand All @@ -145,46 +156,36 @@ public function delete(string $location): void
public function deleteDirectory(string $location): void
{
try {
$this->filesystem->deleteDirectory($this->preparePath($location));
$this->adapter->deleteDirectory($this->prefix->prefixPath($location));
} catch (Throwable $previous) {
throw UnableToDeleteDirectory::atLocation($location, $previous->getMessage(), $previous);
}
}

public function createDirectory(string $location, array $config = []): void
public function createDirectory(string $location, Config $config): void
{
try {
$this->filesystem->createDirectory($this->preparePath($location), $config);
$this->adapter->createDirectory($this->prefix->prefixPath($location), $config);
} catch (Throwable $previous) {
throw UnableToCreateDirectory::atLocation($location, $previous->getMessage(), $previous);
}
}

public function move(string $source, string $destination, array $config = []): void
public function move(string $source, string $destination, Config $config): void
{
try {
$this->filesystem->move($this->preparePath($source), $this->preparePath($destination), $config);
$this->adapter->move($this->prefix->prefixPath($source), $this->prefix->prefixPath($destination), $config);
} catch (Throwable $previous) {
throw UnableToMoveFile::fromLocationTo($source, $destination, $previous);
}
}

public function copy(string $source, string $destination, array $config = []): void
public function copy(string $source, string $destination, Config $config): void
{
try {
$this->filesystem->copy($this->preparePath($source), $this->preparePath($destination), $config);
$this->adapter->copy($this->prefix->prefixPath($source), $this->prefix->prefixPath($destination), $config);
} catch (Throwable $previous) {
throw UnableToCopyFile::fromLocationTo($source, $destination, $previous);
}
}

private function stripPath(string $path): string
{
return $this->prefix->stripPrefix($path);
}

private function preparePath(string $path): string
{
return $this->prefix->prefixPath($path);
}
}
Original file line number Diff line number Diff line change
@@ -1,63 +1,66 @@
<?php

namespace League\Flysystem;
namespace League\Flysystem\PathPrefixing;

use League\Flysystem\Config;
use League\Flysystem\InMemory\InMemoryFilesystemAdapter;
use League\Flysystem\Visibility;
use PHPUnit\Framework\TestCase;

class PathPrefixedFilesystemTest extends TestCase
use function iterator_to_array;

class PathPrefixedAdapterTest extends TestCase
{
public function testPrefix(): void
{
$fs = new Filesystem(new InMemoryFilesystemAdapter());
$prefix = new PathPrefixedFilesystem($fs, 'foo');
$adapter = new InMemoryFilesystemAdapter();
$prefix = new PathPrefixedAdapter($adapter, 'foo');

$prefix->write('foo.txt', 'bla');
$prefix->write('foo.txt', 'bla', new Config);
static::assertTrue($prefix->fileExists('foo.txt'));
static::assertTrue($prefix->has('foo.txt'));
static::assertFalse($prefix->directoryExists('foo.txt'));
static::assertTrue($fs->has('foo/foo.txt'));
static::assertFalse($fs->directoryExists('foo/foo.txt'));
static::assertTrue($adapter->fileExists('foo/foo.txt'));
static::assertFalse($adapter->directoryExists('foo/foo.txt'));

static::assertSame('bla', $prefix->read('foo.txt'));
static::assertSame('bla', stream_get_contents($prefix->readStream('foo.txt')));
static::assertSame('text/plain', $prefix->mimeType('foo.txt'));
static::assertSame(3, $prefix->fileSize('foo.txt'));
static::assertSame(Visibility::PUBLIC, $prefix->visibility('foo.txt'));
static::assertSame('text/plain', $prefix->mimeType('foo.txt')->mimeType());
static::assertSame(3, $prefix->fileSize('foo.txt')->fileSize());
static::assertSame(Visibility::PUBLIC, $prefix->visibility('foo.txt')->visibility());
$prefix->setVisibility('foo.txt', Visibility::PRIVATE);
static::assertSame(Visibility::PRIVATE, $prefix->visibility('foo.txt'));
static::assertEqualsWithDelta($prefix->lastModified('foo.txt'), time(), 2);
static::assertSame(Visibility::PRIVATE, $prefix->visibility('foo.txt')->visibility());
static::assertEqualsWithDelta($prefix->lastModified('foo.txt')->lastModified(), time(), 2);

$prefix->copy('foo.txt', 'bla.txt');
static::assertTrue($prefix->has('bla.txt'));
$prefix->copy('foo.txt', 'bla.txt', new Config);
static::assertTrue($prefix->fileExists('bla.txt'));

$prefix->createDirectory('dir');
$prefix->createDirectory('dir', new Config());
static::assertTrue($prefix->directoryExists('dir'));
static::assertFalse($prefix->directoryExists('dir2'));
$prefix->deleteDirectory('dir');
static::assertFalse($prefix->directoryExists('dir'));

$prefix->move('bla.txt', 'bla2.txt');
static::assertFalse($prefix->has('bla.txt'));
static::assertTrue($prefix->has('bla2.txt'));
$prefix->move('bla.txt', 'bla2.txt', new Config());
static::assertFalse($prefix->fileExists('bla.txt'));
static::assertTrue($prefix->fileExists('bla2.txt'));

$prefix->delete('bla2.txt');
static::assertFalse($prefix->has('bla2.txt'));
static::assertFalse($prefix->fileExists('bla2.txt'));

$prefix->createDirectory('test');
$prefix->createDirectory('test', new Config());

$files = $prefix->listContents('', true)->toArray();
$files = iterator_to_array($prefix->listContents('', true));
static::assertCount(2, $files);
}

public function testWriteStream(): void
{
$fs = new Filesystem(new InMemoryFilesystemAdapter());
$prefix = new PathPrefixedFilesystem($fs, 'foo');
$adapter = new InMemoryFilesystemAdapter();
$prefix = new PathPrefixedAdapter($adapter, 'foo');
$tmpFile = sys_get_temp_dir() . '/' . uniqid('test', true);
file_put_contents($tmpFile, 'test');

$prefix->writeStream('a.txt', fopen($tmpFile, 'rb'));
$prefix->writeStream('a.txt', fopen($tmpFile, 'rb'), new Config());

static::assertTrue($prefix->fileExists('a.txt'));
static::assertSame('test', $prefix->read('a.txt'));
Expand All @@ -69,6 +72,6 @@ public function testWriteStream(): void
public function testEmptyPrefix(): void
{
static::expectException(\InvalidArgumentException::class);
new PathPrefixedFilesystem(new Filesystem(new InMemoryFilesystemAdapter()), '');
new PathPrefixedAdapter(new InMemoryFilesystemAdapter(), '');
}
}