Skip to content

Commit

Permalink
Add cache clearing features
Browse files Browse the repository at this point in the history
  • Loading branch information
Slamdunk committed Dec 3, 2021
1 parent 0dbe379 commit b41a5c7
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 9 deletions.
15 changes: 15 additions & 0 deletions src/CachedFilesystemAdapter.php
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace SlamFlysystem\LocalCache;

use DateTimeInterface;
use League\Flysystem\FilesystemAdapter;

interface CachedFilesystemAdapter extends FilesystemAdapter
{
public function touch(string $path, DateTimeInterface $date): void;

public function clearCacheOlderThan(DateTimeInterface $date): void;
}
35 changes: 30 additions & 5 deletions src/LocalCacheProxyAdapter.php
Expand Up @@ -4,13 +4,15 @@

namespace SlamFlysystem\LocalCache;

use DateTimeImmutable;
use DateTimeInterface;
use League\Flysystem\Config;
use League\Flysystem\FileAttributes;
use League\Flysystem\FilesystemAdapter;
use League\Flysystem\Local\LocalFilesystemAdapter;
use League\Flysystem\PathPrefixer;

final class LocalCacheProxyAdapter implements FilesystemAdapter
final class LocalCacheProxyAdapter implements CachedFilesystemAdapter
{
private LocalFilesystemAdapter $localCacheAdapter;
private PathPrefixer $pathPrefixer;
Expand Down Expand Up @@ -69,6 +71,8 @@ public function writeStream(string $path, $contents, Config $config): void
public function read(string $path): string
{
if ($this->localCacheAdapter->fileExists($path)) {
$this->touch($path, new DateTimeImmutable());

return $this->localCacheAdapter->read($path);
}

Expand All @@ -81,6 +85,8 @@ public function read(string $path): string
public function readStream(string $path)
{
if ($this->localCacheAdapter->fileExists($path)) {
$this->touch($path, new DateTimeImmutable());

return $this->localCacheAdapter->readStream($path);
}

Expand Down Expand Up @@ -150,10 +156,6 @@ public function mimeType(string $path): FileAttributes
*/
public function lastModified(string $path): FileAttributes
{
if ($this->localCacheAdapter->fileExists($path)) {
return $this->localCacheAdapter->lastModified($path);
}

return $this->remoteAdapter->lastModified($path);
}

Expand Down Expand Up @@ -216,4 +218,27 @@ public function copy(string $source, string $destination, Config $config): void
);
}
}

public function touch(string $path, DateTimeInterface $date): void
{
touch($this->pathPrefixer->prefixPath($path), $date->getTimestamp());
}

public function clearCacheOlderThan(DateTimeInterface $date): void
{
$timestamp = $date->getTimestamp();
foreach ($this->localCacheAdapter->listContents('/', true) as $fileAttributes) {
if (!$fileAttributes->isFile()) {
continue;
}

$lastModified = $fileAttributes->lastModified();
\assert(null !== $lastModified);
if ($timestamp <= $lastModified) {
continue;
}

$this->localCacheAdapter->delete($fileAttributes->path());
}
}
}
98 changes: 94 additions & 4 deletions test/LocalCacheAdapterProxyTest.php
Expand Up @@ -4,13 +4,16 @@

namespace SlamFlysystem\LocalCache\Test;

use DateTimeImmutable;
use League\Flysystem\AdapterTestUtilities\FilesystemAdapterTestCase;
use League\Flysystem\Config;
use League\Flysystem\FilesystemAdapter;
use League\Flysystem\Local\LocalFilesystemAdapter;
use League\Flysystem\UnableToRetrieveMetadata;
use League\MimeTypeDetection\EmptyExtensionToMimeTypeMap;
use League\MimeTypeDetection\ExtensionMimeTypeDetector;
use RuntimeException;
use SlamFlysystem\LocalCache\CachedFilesystemAdapter;
use SlamFlysystem\LocalCache\LocalCacheProxyAdapter;

/**
Expand All @@ -21,7 +24,7 @@
*/
final class LocalCacheAdapterProxyTest extends FilesystemAdapterTestCase
{
protected ?FilesystemAdapter $customAdapter = null;
protected ?CachedFilesystemAdapter $customAdapter = null;
protected LocalFilesystemAdapter $remoteAdapter;
protected LocalFilesystemAdapter $localCacheAdapter;
protected string $remoteRoot;
Expand All @@ -44,7 +47,7 @@ protected function tearDown(): void
delete_directory($this->localRoot);
}

public function adapter(): FilesystemAdapter
public function adapter(): CachedFilesystemAdapter
{
if (null === $this->customAdapter) {
$this->localCacheAdapter = new LocalFilesystemAdapter($this->localRoot);
Expand Down Expand Up @@ -241,13 +244,15 @@ public function list_contents_ignores_local_cache(): void
/**
* @test
*/
public function on_last_modified_calls_cache_replies_first(): void
public function last_modified_call_is_never_proxied_to_let_mtime_be_used_for_cache_usage(): void
{
$adapter = $this->adapter();

$this->localCacheAdapter->write('file.txt', 'xyz', new Config());

static::assertGreaterThan(1, $adapter->lastModified('file.txt')->lastModified());
$this->expectException(UnableToRetrieveMetadata::class);

$adapter->lastModified('file.txt');
}

/**
Expand Down Expand Up @@ -292,6 +297,91 @@ public function copy_acts_on_remote_even_when_local_cache_is_empty(): void
static::assertTrue($adapter->fileExists('file.txt'));
}

/**
* @test
*/
public function clear_cache_older_than(): void
{
$adapter = $this->adapter();

$new = new DateTimeImmutable('2021-12-01');
$old = new DateTimeImmutable('2021-01-01');
$limit = $new->modify('-1 day');

$file1Path = 'file1.txt';
$file2Path = 'subfolder/file2.txt';
$file3Path = 'file3.txt';
$file4Path = 'file4.txt';

$adapter->write($file1Path, 'bar', new Config());
$adapter->write($file2Path, 'foo', new Config());
$adapter->write($file3Path, 'baz', new Config());
$adapter->write($file4Path, 'xyz', new Config());

$adapter->touch($file1Path, $limit);
$adapter->touch($file2Path, $old);
$adapter->touch($file3Path, $new);
$adapter->touch($file4Path, $limit->modify('-1 day'));

$adapter->clearCacheOlderThan($limit);

static::assertTrue($adapter->fileExists($file1Path));
static::assertTrue($adapter->fileExists($file2Path));
static::assertTrue($adapter->fileExists($file3Path));
static::assertTrue($adapter->fileExists($file4Path));

static::assertTrue($this->localCacheAdapter->fileExists($file1Path));
static::assertFalse($this->localCacheAdapter->fileExists($file2Path));
static::assertTrue($this->localCacheAdapter->fileExists($file3Path));
static::assertFalse($this->localCacheAdapter->fileExists($file4Path));
}

/**
* @test
*/
public function read_refreshes_cache_timestamp(): void
{
$adapter = $this->adapter();

$old = new DateTimeImmutable('2021-01-01');
$oldPath = 'subfolder/old.txt';

$adapter->write($oldPath, 'foo', new Config());
$adapter->touch($oldPath, $old);

static::assertTrue($adapter->fileExists($oldPath));

static::assertSame('foo', $adapter->read($oldPath));

$adapter->clearCacheOlderThan($old->modify('+1 day'));

static::assertTrue($adapter->fileExists($oldPath));
static::assertTrue($this->localCacheAdapter->fileExists($oldPath));
}

/**
* @test
*/
public function read_stream_refreshes_cache_timestamp(): void
{
$adapter = $this->adapter();

$old = new DateTimeImmutable('2021-01-01');
$oldPath = 'subfolder/old.txt';

$adapter->write($oldPath, 'foo', new Config());
$adapter->touch($oldPath, $old);

static::assertTrue($adapter->fileExists($oldPath));

static::assertSame('foo', stream_get_contents($adapter->readStream($oldPath)));

$adapter->clearCacheOlderThan($old->modify('+1 day'));

static::assertTrue($adapter->fileExists($oldPath));
static::assertTrue($this->localCacheAdapter->fileExists($oldPath));
}

protected static function createFilesystemAdapter(): FilesystemAdapter
{
throw new RuntimeException('Only non-static adapter creation allowed');
Expand Down

0 comments on commit b41a5c7

Please sign in to comment.