初始化代码

This commit is contained in:
2025-12-22 14:34:25 +08:00
parent c2c5ae2fdd
commit a77dbc743f
1510 changed files with 213008 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
; top-most EditorConfig file
root = true
; Unix-style newlines
[*]
end_of_line = LF
[*.php]
indent_style = space
indent_size = 4

View File

@@ -0,0 +1,4 @@
coverage
coverage.xml
composer.lock
vendor

View File

@@ -0,0 +1,7 @@
<?php
return Symfony\CS\Config\Config::create()
->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
->fixers(['-yoda_conditions', 'ordered_use', 'short_array_syntax'])
->finder(Symfony\CS\Finder\DefaultFinder::create()
->in(__DIR__.'/src/'));

View File

@@ -0,0 +1,34 @@
filter:
paths: [src/*]
checks:
php:
code_rating: true
remove_extra_empty_lines: true
remove_php_closing_tag: true
remove_trailing_whitespace: true
fix_use_statements:
remove_unused: true
preserve_multiple: false
preserve_blanklines: true
order_alphabetically: true
fix_php_opening_tag: true
fix_linefeed: true
fix_line_ending: true
fix_identation_4spaces: true
fix_doc_comments: true
tools:
external_code_coverage:
timeout: 900
runs: 6
php_code_coverage: false
php_code_sniffer:
config:
standard: PSR2
filter:
paths: ['src']
php_loc:
enabled: true
excluded_dirs: [vendor, spec, stubs]
php_cpd:
enabled: true
excluded_dirs: [vendor, spec, stubs]

View File

@@ -0,0 +1,29 @@
language: php
php:
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
matrix:
allow_failures:
- php: 5.5
env:
- COMPOSER_OPTS=""
- COMPOSER_OPTS="--prefer-lowest"
install:
- if [[ "${TRAVIS_PHP_VERSION}" == "5.5" ]]; then composer require phpunit/phpunit:^4.8.36 phpspec/phpspec:^2 --prefer-dist --update-with-dependencies; fi
- if [[ "${TRAVIS_PHP_VERSION}" == "7.2" ]]; then composer require phpunit/phpunit:^6.0 --prefer-dist --update-with-dependencies; fi
- travis_retry composer update --prefer-dist $COMPOSER_OPTS
script:
- vendor/bin/phpspec run
- vendor/bin/phpunit
after_script:
- wget https://scrutinizer-ci.com/ocular.phar'
- php ocular.phar code-coverage:upload --format=php-clover ./clover/phpunit.xml'

View File

@@ -0,0 +1,19 @@
Copyright (c) 2015 Frank de Jonge
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,2 @@
*
!.gitignore

View File

@@ -0,0 +1,30 @@
{
"name": "league/flysystem-cached-adapter",
"description": "An adapter decorator to enable meta-data caching.",
"autoload": {
"psr-4": {
"League\\Flysystem\\Cached\\": "src/"
}
},
"require": {
"league/flysystem": "~1.0",
"psr/cache": "^1.0.0"
},
"require-dev": {
"phpspec/phpspec": "^3.4",
"phpunit/phpunit": "^5.7",
"mockery/mockery": "~0.9",
"predis/predis": "~1.0",
"tedivm/stash": "~0.12"
},
"suggest": {
"ext-phpredis": "Pure C implemented extension for PHP"
},
"license": "MIT",
"authors": [
{
"name": "frankdejonge",
"email": "info@frenky.net"
}
]
}

View File

@@ -0,0 +1,6 @@
---
suites:
cached_adapter_suite:
namespace: League\Flysystem\Cached
psr4_prefix: League\Flysystem\Cached
formatter.name: pretty

View File

@@ -0,0 +1,3 @@
<?php
include __DIR__.'/vendor/autoload.php';

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="./phpunit.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="true"
verbose="true"
>
<testsuites>
<testsuite name="flysystem/tests">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true"/>
<log type="coverage-html" target="coverage" showUncoveredFiles="true"/>
<log type="coverage-clover" target="clover/phpunit.xml" showUncoveredFiles="true"/>
</logging>
</phpunit>

View File

@@ -0,0 +1,20 @@
# Flysystem Cached CachedAdapter
[![Author](http://img.shields.io/badge/author-@frankdejonge-blue.svg?style=flat-square)](https://twitter.com/frankdejonge)
[![Build Status](https://img.shields.io/travis/thephpleague/flysystem-cached-adapter/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/flysystem-cached-adapter)
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/flysystem-cached-adapter.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/flysystem-cached-adapter/code-structure)
[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/flysystem-cached-adapter.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/flysystem-cached-adapter)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Packagist Version](https://img.shields.io/packagist/v/league/flysystem-cached-adapter.svg?style=flat-square)](https://packagist.org/packages/league/flysystem-cached-adapter)
[![Total Downloads](https://img.shields.io/packagist/dt/league/flysystem-cached-adapter.svg?style=flat-square)](https://packagist.org/packages/league/flysystem-cached-adapter)
The adapter decorator caches metadata and directory listings.
```bash
composer require league/flysystem-cached-adapter
```
## Usage
[Check out the docs.](https://flysystem.thephpleague.com/docs/advanced/caching/)

View File

@@ -0,0 +1,435 @@
<?php
namespace spec\League\Flysystem\Cached;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Cached\CacheInterface;
use League\Flysystem\Config;
use PhpSpec\ObjectBehavior;
class CachedAdapterSpec extends ObjectBehavior
{
/**
* @var AdapterInterface
*/
private $adapter;
/**
* @var CacheInterface
*/
private $cache;
public function let(AdapterInterface $adapter, CacheInterface $cache)
{
$this->adapter = $adapter;
$this->cache = $cache;
$this->cache->load()->shouldBeCalled();
$this->beConstructedWith($adapter, $cache);
}
public function it_is_initializable()
{
$this->shouldHaveType('League\Flysystem\Cached\CachedAdapter');
$this->shouldHaveType('League\Flysystem\AdapterInterface');
}
public function it_should_forward_read_streams()
{
$path = 'path.txt';
$response = ['path' => $path];
$this->adapter->readStream($path)->willReturn($response);
$this->readStream($path)->shouldbe($response);
}
public function it_should_cache_writes()
{
$type = 'file';
$path = 'path.txt';
$contents = 'contents';
$config = new Config();
$response = compact('path', 'contents', 'type');
$this->adapter->write($path, $contents, $config)->willReturn($response);
$this->cache->updateObject($path, $response, true)->shouldBeCalled();
$this->write($path, $contents, $config)->shouldBe($response);
}
public function it_should_cache_streamed_writes()
{
$type = 'file';
$path = 'path.txt';
$stream = tmpfile();
$config = new Config();
$response = compact('path', 'stream', 'type');
$this->adapter->writeStream($path, $stream, $config)->willReturn($response);
$this->cache->updateObject($path, ['contents' => false] + $response, true)->shouldBeCalled();
$this->writeStream($path, $stream, $config)->shouldBe($response);
fclose($stream);
}
public function it_should_cache_streamed_updates()
{
$type = 'file';
$path = 'path.txt';
$stream = tmpfile();
$config = new Config();
$response = compact('path', 'stream', 'type');
$this->adapter->updateStream($path, $stream, $config)->willReturn($response);
$this->cache->updateObject($path, ['contents' => false] + $response, true)->shouldBeCalled();
$this->updateStream($path, $stream, $config)->shouldBe($response);
fclose($stream);
}
public function it_should_ignore_failed_writes()
{
$path = 'path.txt';
$contents = 'contents';
$config = new Config();
$this->adapter->write($path, $contents, $config)->willReturn(false);
$this->write($path, $contents, $config)->shouldBe(false);
}
public function it_should_ignore_failed_streamed_writes()
{
$path = 'path.txt';
$contents = tmpfile();
$config = new Config();
$this->adapter->writeStream($path, $contents, $config)->willReturn(false);
$this->writeStream($path, $contents, $config)->shouldBe(false);
fclose($contents);
}
public function it_should_cache_updated()
{
$type = 'file';
$path = 'path.txt';
$contents = 'contents';
$config = new Config();
$response = compact('path', 'contents', 'type');
$this->adapter->update($path, $contents, $config)->willReturn($response);
$this->cache->updateObject($path, $response, true)->shouldBeCalled();
$this->update($path, $contents, $config)->shouldBe($response);
}
public function it_should_ignore_failed_updates()
{
$path = 'path.txt';
$contents = 'contents';
$config = new Config();
$this->adapter->update($path, $contents, $config)->willReturn(false);
$this->update($path, $contents, $config)->shouldBe(false);
}
public function it_should_ignore_failed_streamed_updates()
{
$path = 'path.txt';
$contents = tmpfile();
$config = new Config();
$this->adapter->updateStream($path, $contents, $config)->willReturn(false);
$this->updateStream($path, $contents, $config)->shouldBe(false);
fclose($contents);
}
public function it_should_cache_renames()
{
$old = 'old.txt';
$new = 'new.txt';
$this->adapter->rename($old, $new)->willReturn(true);
$this->cache->rename($old, $new)->shouldBeCalled();
$this->rename($old, $new)->shouldBe(true);
}
public function it_should_ignore_rename_fails()
{
$old = 'old.txt';
$new = 'new.txt';
$this->adapter->rename($old, $new)->willReturn(false);
$this->rename($old, $new)->shouldBe(false);
}
public function it_should_cache_copies()
{
$old = 'old.txt';
$new = 'new.txt';
$this->adapter->copy($old, $new)->willReturn(true);
$this->cache->copy($old, $new)->shouldBeCalled();
$this->copy($old, $new)->shouldBe(true);
}
public function it_should_ignore_copy_fails()
{
$old = 'old.txt';
$new = 'new.txt';
$this->adapter->copy($old, $new)->willReturn(false);
$this->copy($old, $new)->shouldBe(false);
}
public function it_should_cache_deletes()
{
$delete = 'delete.txt';
$this->adapter->delete($delete)->willReturn(true);
$this->cache->delete($delete)->shouldBeCalled();
$this->delete($delete)->shouldBe(true);
}
public function it_should_ignore_delete_fails()
{
$delete = 'delete.txt';
$this->adapter->delete($delete)->willReturn(false);
$this->delete($delete)->shouldBe(false);
}
public function it_should_cache_dir_deletes()
{
$delete = 'delete';
$this->adapter->deleteDir($delete)->willReturn(true);
$this->cache->deleteDir($delete)->shouldBeCalled();
$this->deleteDir($delete)->shouldBe(true);
}
public function it_should_ignore_delete_dir_fails()
{
$delete = 'delete';
$this->adapter->deleteDir($delete)->willReturn(false);
$this->deleteDir($delete)->shouldBe(false);
}
public function it_should_cache_dir_creates()
{
$dirname = 'dirname';
$config = new Config();
$response = ['path' => $dirname, 'type' => 'dir'];
$this->adapter->createDir($dirname, $config)->willReturn($response);
$this->cache->updateObject($dirname, $response, true)->shouldBeCalled();
$this->createDir($dirname, $config)->shouldBe($response);
}
public function it_should_ignore_create_dir_fails()
{
$dirname = 'dirname';
$config = new Config();
$this->adapter->createDir($dirname, $config)->willReturn(false);
$this->createDir($dirname, $config)->shouldBe(false);
}
public function it_should_cache_set_visibility()
{
$path = 'path.txt';
$visibility = AdapterInterface::VISIBILITY_PUBLIC;
$this->adapter->setVisibility($path, $visibility)->willReturn(true);
$this->cache->updateObject($path, ['path' => $path, 'visibility' => $visibility], true)->shouldBeCalled();
$this->setVisibility($path, $visibility)->shouldBe(true);
}
public function it_should_ignore_set_visibility_fails()
{
$dirname = 'delete';
$visibility = AdapterInterface::VISIBILITY_PUBLIC;
$this->adapter->setVisibility($dirname, $visibility)->willReturn(false);
$this->setVisibility($dirname, $visibility)->shouldBe(false);
}
public function it_should_indicate_missing_files()
{
$this->cache->has($path = 'path.txt')->willReturn(false);
$this->has($path)->shouldBe(false);
}
public function it_should_indicate_file_existance()
{
$this->cache->has($path = 'path.txt')->willReturn(true);
$this->has($path)->shouldBe(true);
}
public function it_should_cache_missing_files()
{
$this->cache->has($path = 'path.txt')->willReturn(null);
$this->adapter->has($path)->willReturn(false);
$this->cache->storeMiss($path)->shouldBeCalled();
$this->has($path)->shouldBe(false);
}
public function it_should_delete_when_metadata_is_missing()
{
$path = 'path.txt';
$this->cache->has($path)->willReturn(true);
$this->cache->getSize($path)->willReturn(['path' => $path]);
$this->adapter->getSize($path)->willReturn($response = ['path' => $path, 'size' => 1024]);
$this->cache->updateObject($path, $response, true)->shouldBeCalled();
$this->getSize($path)->shouldBe($response);
}
public function it_should_cache_has()
{
$this->cache->has($path = 'path.txt')->willReturn(null);
$this->adapter->has($path)->willReturn(true);
$this->cache->updateObject($path, compact('path'), true)->shouldBeCalled();
$this->has($path)->shouldBe(true);
}
public function it_should_list_cached_contents()
{
$this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(true);
$response = [['path' => 'path.txt']];
$this->cache->listContents($dirname, $recursive)->willReturn($response);
$this->listContents($dirname, $recursive)->shouldBe($response);
}
public function it_should_ignore_failed_list_contents()
{
$this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(false);
$this->adapter->listContents($dirname, $recursive)->willReturn(false);
$this->listContents($dirname, $recursive)->shouldBe(false);
}
public function it_should_cache_contents_listings()
{
$this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(false);
$response = [['path' => 'path.txt']];
$this->adapter->listContents($dirname, $recursive)->willReturn($response);
$this->cache->storeContents($dirname, $response, $recursive)->shouldBeCalled();
$this->listContents($dirname, $recursive)->shouldBe($response);
}
public function it_should_use_cached_visibility()
{
$this->make_it_use_getter_cache('getVisibility', 'path.txt', [
'path' => 'path.txt',
'visibility' => AdapterInterface::VISIBILITY_PUBLIC,
]);
}
public function it_should_cache_get_visibility()
{
$path = 'path.txt';
$response = ['visibility' => AdapterInterface::VISIBILITY_PUBLIC, 'path' => $path];
$this->make_it_cache_getter('getVisibility', $path, $response);
}
public function it_should_ignore_failed_get_visibility()
{
$path = 'path.txt';
$this->make_it_ignore_failed_getter('getVisibility', $path);
}
public function it_should_use_cached_timestamp()
{
$this->make_it_use_getter_cache('getTimestamp', 'path.txt', [
'path' => 'path.txt',
'timestamp' => 1234,
]);
}
public function it_should_cache_timestamps()
{
$this->make_it_cache_getter('getTimestamp', 'path.txt', [
'path' => 'path.txt',
'timestamp' => 1234,
]);
}
public function it_should_ignore_failed_get_timestamps()
{
$this->make_it_ignore_failed_getter('getTimestamp', 'path.txt');
}
public function it_should_cache_get_metadata()
{
$path = 'path.txt';
$response = ['visibility' => AdapterInterface::VISIBILITY_PUBLIC, 'path' => $path];
$this->make_it_cache_getter('getMetadata', $path, $response);
}
public function it_should_use_cached_metadata()
{
$this->make_it_use_getter_cache('getMetadata', 'path.txt', [
'path' => 'path.txt',
'timestamp' => 1234,
]);
}
public function it_should_ignore_failed_get_metadata()
{
$this->make_it_ignore_failed_getter('getMetadata', 'path.txt');
}
public function it_should_cache_get_size()
{
$path = 'path.txt';
$response = ['size' => 1234, 'path' => $path];
$this->make_it_cache_getter('getSize', $path, $response);
}
public function it_should_use_cached_size()
{
$this->make_it_use_getter_cache('getSize', 'path.txt', [
'path' => 'path.txt',
'size' => 1234,
]);
}
public function it_should_ignore_failed_get_size()
{
$this->make_it_ignore_failed_getter('getSize', 'path.txt');
}
public function it_should_cache_get_mimetype()
{
$path = 'path.txt';
$response = ['mimetype' => 'text/plain', 'path' => $path];
$this->make_it_cache_getter('getMimetype', $path, $response);
}
public function it_should_use_cached_mimetype()
{
$this->make_it_use_getter_cache('getMimetype', 'path.txt', [
'path' => 'path.txt',
'mimetype' => 'text/plain',
]);
}
public function it_should_ignore_failed_get_mimetype()
{
$this->make_it_ignore_failed_getter('getMimetype', 'path.txt');
}
public function it_should_cache_reads()
{
$path = 'path.txt';
$response = ['path' => $path, 'contents' => 'contents'];
$this->make_it_cache_getter('read', $path, $response);
}
public function it_should_use_cached_file_contents()
{
$this->make_it_use_getter_cache('read', 'path.txt', [
'path' => 'path.txt',
'contents' => 'contents'
]);
}
public function it_should_ignore_failed_reads()
{
$this->make_it_ignore_failed_getter('read', 'path.txt');
}
protected function make_it_use_getter_cache($method, $path, $response)
{
$this->cache->{$method}($path)->willReturn($response);
$this->{$method}($path)->shouldBe($response);
}
protected function make_it_cache_getter($method, $path, $response)
{
$this->cache->{$method}($path)->willReturn(false);
$this->adapter->{$method}($path)->willReturn($response);
$this->cache->updateObject($path, $response, true)->shouldBeCalled();
$this->{$method}($path)->shouldBe($response);
}
protected function make_it_ignore_failed_getter($method, $path)
{
$this->cache->{$method}($path)->willReturn(false);
$this->adapter->{$method}($path)->willReturn(false);
$this->{$method}($path)->shouldBe(false);
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace League\Flysystem\Cached;
use League\Flysystem\ReadInterface;
interface CacheInterface extends ReadInterface
{
/**
* Check whether the directory listing of a given directory is complete.
*
* @param string $dirname
* @param bool $recursive
*
* @return bool
*/
public function isComplete($dirname, $recursive);
/**
* Set a directory to completely listed.
*
* @param string $dirname
* @param bool $recursive
*/
public function setComplete($dirname, $recursive);
/**
* Store the contents of a directory.
*
* @param string $directory
* @param array $contents
* @param bool $recursive
*/
public function storeContents($directory, array $contents, $recursive);
/**
* Flush the cache.
*/
public function flush();
/**
* Autosave trigger.
*/
public function autosave();
/**
* Store the cache.
*/
public function save();
/**
* Load the cache.
*/
public function load();
/**
* Rename a file.
*
* @param string $path
* @param string $newpath
*/
public function rename($path, $newpath);
/**
* Copy a file.
*
* @param string $path
* @param string $newpath
*/
public function copy($path, $newpath);
/**
* Delete an object from cache.
*
* @param string $path object path
*/
public function delete($path);
/**
* Delete all objects from from a directory.
*
* @param string $dirname directory path
*/
public function deleteDir($dirname);
/**
* Update the metadata for an object.
*
* @param string $path object path
* @param array $object object metadata
* @param bool $autosave whether to trigger the autosave routine
*/
public function updateObject($path, array $object, $autosave = false);
/**
* Store object hit miss.
*
* @param string $path
*/
public function storeMiss($path);
}

View File

@@ -0,0 +1,324 @@
<?php
namespace League\Flysystem\Cached;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
class CachedAdapter implements AdapterInterface
{
/**
* @var AdapterInterface
*/
private $adapter;
/**
* @var CacheInterface
*/
private $cache;
/**
* Constructor.
*
* @param AdapterInterface $adapter
* @param CacheInterface $cache
*/
public function __construct(AdapterInterface $adapter, CacheInterface $cache)
{
$this->adapter = $adapter;
$this->cache = $cache;
$this->cache->load();
}
/**
* Get the underlying Adapter implementation.
*
* @return AdapterInterface
*/
public function getAdapter()
{
return $this->adapter;
}
/**
* Get the used Cache implementation.
*
* @return CacheInterface
*/
public function getCache()
{
return $this->cache;
}
/**
* {@inheritdoc}
*/
public function write($path, $contents, Config $config)
{
$result = $this->adapter->write($path, $contents, $config);
if ($result !== false) {
$result['type'] = 'file';
$this->cache->updateObject($path, $result + compact('path', 'contents'), true);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function writeStream($path, $resource, Config $config)
{
$result = $this->adapter->writeStream($path, $resource, $config);
if ($result !== false) {
$result['type'] = 'file';
$contents = false;
$this->cache->updateObject($path, $result + compact('path', 'contents'), true);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function update($path, $contents, Config $config)
{
$result = $this->adapter->update($path, $contents, $config);
if ($result !== false) {
$result['type'] = 'file';
$this->cache->updateObject($path, $result + compact('path', 'contents'), true);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function updateStream($path, $resource, Config $config)
{
$result = $this->adapter->updateStream($path, $resource, $config);
if ($result !== false) {
$result['type'] = 'file';
$contents = false;
$this->cache->updateObject($path, $result + compact('path', 'contents'), true);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function rename($path, $newPath)
{
$result = $this->adapter->rename($path, $newPath);
if ($result !== false) {
$this->cache->rename($path, $newPath);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function copy($path, $newpath)
{
$result = $this->adapter->copy($path, $newpath);
if ($result !== false) {
$this->cache->copy($path, $newpath);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function delete($path)
{
$result = $this->adapter->delete($path);
if ($result !== false) {
$this->cache->delete($path);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function deleteDir($dirname)
{
$result = $this->adapter->deleteDir($dirname);
if ($result !== false) {
$this->cache->deleteDir($dirname);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function createDir($dirname, Config $config)
{
$result = $this->adapter->createDir($dirname, $config);
if ($result !== false) {
$type = 'dir';
$path = $dirname;
$this->cache->updateObject($dirname, compact('path', 'type'), true);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function setVisibility($path, $visibility)
{
$result = $this->adapter->setVisibility($path, $visibility);
if ($result !== false) {
$this->cache->updateObject($path, compact('path', 'visibility'), true);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function has($path)
{
$cacheHas = $this->cache->has($path);
if ($cacheHas !== null) {
return $cacheHas;
}
$adapterResponse = $this->adapter->has($path);
if (! $adapterResponse) {
$this->cache->storeMiss($path);
} else {
$cacheEntry = is_array($adapterResponse) ? $adapterResponse : compact('path');
$this->cache->updateObject($path, $cacheEntry, true);
}
return $adapterResponse;
}
/**
* {@inheritdoc}
*/
public function read($path)
{
return $this->callWithFallback('contents', $path, 'read');
}
/**
* {@inheritdoc}
*/
public function readStream($path)
{
return $this->adapter->readStream($path);
}
/**
* {@inheritdoc}
*/
public function listContents($directory = '', $recursive = false)
{
if ($this->cache->isComplete($directory, $recursive)) {
return $this->cache->listContents($directory, $recursive);
}
$result = $this->adapter->listContents($directory, $recursive);
if ($result !== false) {
$this->cache->storeContents($directory, $result, $recursive);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function getMetadata($path)
{
return $this->callWithFallback(null, $path, 'getMetadata');
}
/**
* {@inheritdoc}
*/
public function getSize($path)
{
return $this->callWithFallback('size', $path, 'getSize');
}
/**
* {@inheritdoc}
*/
public function getMimetype($path)
{
return $this->callWithFallback('mimetype', $path, 'getMimetype');
}
/**
* {@inheritdoc}
*/
public function getTimestamp($path)
{
return $this->callWithFallback('timestamp', $path, 'getTimestamp');
}
/**
* {@inheritdoc}
*/
public function getVisibility($path)
{
return $this->callWithFallback('visibility', $path, 'getVisibility');
}
/**
* Call a method and cache the response.
*
* @param string $property
* @param string $path
* @param string $method
*
* @return mixed
*/
protected function callWithFallback($property, $path, $method)
{
$result = $this->cache->{$method}($path);
if ($result !== false && ($property === null || array_key_exists($property, $result))) {
return $result;
}
$result = $this->adapter->{$method}($path);
if ($result) {
$object = $result + compact('path');
$this->cache->updateObject($path, $object, true);
}
return $result;
}
}

View File

@@ -0,0 +1,417 @@
<?php
namespace League\Flysystem\Cached\Storage;
use League\Flysystem\Cached\CacheInterface;
use League\Flysystem\Util;
abstract class AbstractCache implements CacheInterface
{
/**
* @var bool
*/
protected $autosave = true;
/**
* @var array
*/
protected $cache = [];
/**
* @var array
*/
protected $complete = [];
/**
* Destructor.
*/
public function __destruct()
{
if (! $this->autosave) {
$this->save();
}
}
/**
* Get the autosave setting.
*
* @return bool autosave
*/
public function getAutosave()
{
return $this->autosave;
}
/**
* Get the autosave setting.
*
* @param bool $autosave
*/
public function setAutosave($autosave)
{
$this->autosave = $autosave;
}
/**
* Store the contents listing.
*
* @param string $directory
* @param array $contents
* @param bool $recursive
*
* @return array contents listing
*/
public function storeContents($directory, array $contents, $recursive = false)
{
$directories = [$directory];
foreach ($contents as $object) {
$this->updateObject($object['path'], $object);
$object = $this->cache[$object['path']];
if ($recursive && $this->pathIsInDirectory($directory, $object['path'])) {
$directories[] = $object['dirname'];
}
}
foreach (array_unique($directories) as $directory) {
$this->setComplete($directory, $recursive);
}
$this->autosave();
}
/**
* Update the metadata for an object.
*
* @param string $path object path
* @param array $object object metadata
* @param bool $autosave whether to trigger the autosave routine
*/
public function updateObject($path, array $object, $autosave = false)
{
if (! $this->has($path)) {
$this->cache[$path] = Util::pathinfo($path);
}
$this->cache[$path] = array_merge($this->cache[$path], $object);
if ($autosave) {
$this->autosave();
}
$this->ensureParentDirectories($path);
}
/**
* Store object hit miss.
*
* @param string $path
*/
public function storeMiss($path)
{
$this->cache[$path] = false;
$this->autosave();
}
/**
* Get the contents listing.
*
* @param string $dirname
* @param bool $recursive
*
* @return array contents listing
*/
public function listContents($dirname = '', $recursive = false)
{
$result = [];
foreach ($this->cache as $object) {
if ($object === false) {
continue;
}
if ($object['dirname'] === $dirname) {
$result[] = $object;
} elseif ($recursive && $this->pathIsInDirectory($dirname, $object['path'])) {
$result[] = $object;
}
}
return $result;
}
/**
* {@inheritdoc}
*/
public function has($path)
{
if ($path !== false && array_key_exists($path, $this->cache)) {
return $this->cache[$path] !== false;
}
if ($this->isComplete(Util::dirname($path), false)) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function read($path)
{
if (isset($this->cache[$path]['contents']) && $this->cache[$path]['contents'] !== false) {
return $this->cache[$path];
}
return false;
}
/**
* {@inheritdoc}
*/
public function readStream($path)
{
return false;
}
/**
* {@inheritdoc}
*/
public function rename($path, $newpath)
{
if ($this->has($path)) {
$object = $this->cache[$path];
unset($this->cache[$path]);
$object['path'] = $newpath;
$object = array_merge($object, Util::pathinfo($newpath));
$this->cache[$newpath] = $object;
$this->autosave();
}
}
/**
* {@inheritdoc}
*/
public function copy($path, $newpath)
{
if ($this->has($path)) {
$object = $this->cache[$path];
$object = array_merge($object, Util::pathinfo($newpath));
$this->updateObject($newpath, $object, true);
}
}
/**
* {@inheritdoc}
*/
public function delete($path)
{
$this->storeMiss($path);
}
/**
* {@inheritdoc}
*/
public function deleteDir($dirname)
{
foreach ($this->cache as $path => $object) {
if ($this->pathIsInDirectory($dirname, $path) || $path === $dirname) {
unset($this->cache[$path]);
}
}
unset($this->complete[$dirname]);
$this->autosave();
}
/**
* {@inheritdoc}
*/
public function getMimetype($path)
{
if (isset($this->cache[$path]['mimetype'])) {
return $this->cache[$path];
}
if (! $result = $this->read($path)) {
return false;
}
$mimetype = Util::guessMimeType($path, $result['contents']);
$this->cache[$path]['mimetype'] = $mimetype;
return $this->cache[$path];
}
/**
* {@inheritdoc}
*/
public function getSize($path)
{
if (isset($this->cache[$path]['size'])) {
return $this->cache[$path];
}
return false;
}
/**
* {@inheritdoc}
*/
public function getTimestamp($path)
{
if (isset($this->cache[$path]['timestamp'])) {
return $this->cache[$path];
}
return false;
}
/**
* {@inheritdoc}
*/
public function getVisibility($path)
{
if (isset($this->cache[$path]['visibility'])) {
return $this->cache[$path];
}
return false;
}
/**
* {@inheritdoc}
*/
public function getMetadata($path)
{
if (isset($this->cache[$path]['type'])) {
return $this->cache[$path];
}
return false;
}
/**
* {@inheritdoc}
*/
public function isComplete($dirname, $recursive)
{
if (! array_key_exists($dirname, $this->complete)) {
return false;
}
if ($recursive && $this->complete[$dirname] !== 'recursive') {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function setComplete($dirname, $recursive)
{
$this->complete[$dirname] = $recursive ? 'recursive' : true;
}
/**
* Filter the contents from a listing.
*
* @param array $contents object listing
*
* @return array filtered contents
*/
public function cleanContents(array $contents)
{
$cachedProperties = array_flip([
'path', 'dirname', 'basename', 'extension', 'filename',
'size', 'mimetype', 'visibility', 'timestamp', 'type',
]);
foreach ($contents as $path => $object) {
if (is_array($object)) {
$contents[$path] = array_intersect_key($object, $cachedProperties);
}
}
return $contents;
}
/**
* {@inheritdoc}
*/
public function flush()
{
$this->cache = [];
$this->complete = [];
$this->autosave();
}
/**
* {@inheritdoc}
*/
public function autosave()
{
if ($this->autosave) {
$this->save();
}
}
/**
* Retrieve serialized cache data.
*
* @return string serialized data
*/
public function getForStorage()
{
$cleaned = $this->cleanContents($this->cache);
return json_encode([$cleaned, $this->complete]);
}
/**
* Load from serialized cache data.
*
* @param string $json
*/
public function setFromStorage($json)
{
list($cache, $complete) = json_decode($json, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($cache) && is_array($complete)) {
$this->cache = $cache;
$this->complete = $complete;
}
}
/**
* Ensure parent directories of an object.
*
* @param string $path object path
*/
public function ensureParentDirectories($path)
{
$object = $this->cache[$path];
while ($object['dirname'] !== '' && ! isset($this->cache[$object['dirname']])) {
$object = Util::pathinfo($object['dirname']);
$object['type'] = 'dir';
$this->cache[$object['path']] = $object;
}
}
/**
* Determines if the path is inside the directory.
*
* @param string $directory
* @param string $path
*
* @return bool
*/
protected function pathIsInDirectory($directory, $path)
{
return $directory === '' || strpos($path, $directory . '/') === 0;
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace League\Flysystem\Cached\Storage;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
class Adapter extends AbstractCache
{
/**
* @var AdapterInterface An adapter
*/
protected $adapter;
/**
* @var string the file to cache to
*/
protected $file;
/**
* @var int|null seconds until cache expiration
*/
protected $expire = null;
/**
* Constructor.
*
* @param AdapterInterface $adapter adapter
* @param string $file the file to cache to
* @param int|null $expire seconds until cache expiration
*/
public function __construct(AdapterInterface $adapter, $file, $expire = null)
{
$this->adapter = $adapter;
$this->file = $file;
$this->setExpire($expire);
}
/**
* Set the expiration time in seconds.
*
* @param int $expire relative expiration time
*/
protected function setExpire($expire)
{
if ($expire) {
$this->expire = $this->getTime($expire);
}
}
/**
* Get expiration time in seconds.
*
* @param int $time relative expiration time
*
* @return int actual expiration time
*/
protected function getTime($time = 0)
{
return intval(microtime(true)) + $time;
}
/**
* {@inheritdoc}
*/
public function setFromStorage($json)
{
list($cache, $complete, $expire) = json_decode($json, true);
if (! $expire || $expire > $this->getTime()) {
$this->cache = $cache;
$this->complete = $complete;
} else {
$this->adapter->delete($this->file);
}
}
/**
* {@inheritdoc}
*/
public function load()
{
if ($this->adapter->has($this->file)) {
$file = $this->adapter->read($this->file);
if ($file && !empty($file['contents'])) {
$this->setFromStorage($file['contents']);
}
}
}
/**
* {@inheritdoc}
*/
public function getForStorage()
{
$cleaned = $this->cleanContents($this->cache);
return json_encode([$cleaned, $this->complete, $this->expire]);
}
/**
* {@inheritdoc}
*/
public function save()
{
$config = new Config();
$contents = $this->getForStorage();
if ($this->adapter->has($this->file)) {
$this->adapter->update($this->file, $contents, $config);
} else {
$this->adapter->write($this->file, $contents, $config);
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace League\Flysystem\Cached\Storage;
use Memcached as NativeMemcached;
class Memcached extends AbstractCache
{
/**
* @var string storage key
*/
protected $key;
/**
* @var int|null seconds until cache expiration
*/
protected $expire;
/**
* @var \Memcached Memcached instance
*/
protected $memcached;
/**
* Constructor.
*
* @param \Memcached $memcached
* @param string $key storage key
* @param int|null $expire seconds until cache expiration
*/
public function __construct(NativeMemcached $memcached, $key = 'flysystem', $expire = null)
{
$this->key = $key;
$this->expire = $expire;
$this->memcached = $memcached;
}
/**
* {@inheritdoc}
*/
public function load()
{
$contents = $this->memcached->get($this->key);
if ($contents !== false) {
$this->setFromStorage($contents);
}
}
/**
* {@inheritdoc}
*/
public function save()
{
$contents = $this->getForStorage();
$expiration = $this->expire === null ? 0 : time() + $this->expire;
$this->memcached->set($this->key, $contents, $expiration);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace League\Flysystem\Cached\Storage;
class Memory extends AbstractCache
{
/**
* {@inheritdoc}
*/
public function save()
{
// There is nothing to save
}
/**
* {@inheritdoc}
*/
public function load()
{
// There is nothing to load
}
}

View File

@@ -0,0 +1,171 @@
<?php
namespace League\Flysystem\Cached\Storage;
class Noop extends AbstractCache
{
/**
* {@inheritdoc}
*/
protected $autosave = false;
/**
* {@inheritdoc}
*/
public function updateObject($path, array $object, $autosave = false)
{
return $object;
}
/**
* {@inheritdoc}
*/
public function isComplete($dirname, $recursive)
{
return false;
}
/**
* {@inheritdoc}
*/
public function setComplete($dirname, $recursive)
{
//
}
/**
* {@inheritdoc}
*/
public function copy($path, $newpath)
{
return false;
}
/**
* {@inheritdoc}
*/
public function rename($path, $newpath)
{
return false;
}
/**
* {@inheritdoc}
*/
public function storeContents($directory, array $contents, $recursive = false)
{
return $contents;
}
/**
* {@inheritdoc}
*/
public function storeMiss($path)
{
return $this;
}
/**
* {@inheritdoc}
*/
public function flush()
{
//
}
/**
* {@inheritdoc}
*/
public function autosave()
{
//
}
/**
* {@inheritdoc}
*/
public function save()
{
//
}
/**
* {@inheritdoc}
*/
public function load()
{
//
}
/**
* {@inheritdoc}
*/
public function has($path)
{
return;
}
/**
* {@inheritdoc}
*/
public function read($path)
{
return false;
}
/**
* {@inheritdoc}
*/
public function readStream($path)
{
return false;
}
/**
* {@inheritdoc}
*/
public function listContents($directory = '', $recursive = false)
{
return [];
}
/**
* {@inheritdoc}
*/
public function getMetadata($path)
{
return false;
}
/**
* {@inheritdoc}
*/
public function getSize($path)
{
return false;
}
/**
* {@inheritdoc}
*/
public function getMimetype($path)
{
return false;
}
/**
* {@inheritdoc}
*/
public function getTimestamp($path)
{
return false;
}
/**
* {@inheritdoc}
*/
public function getVisibility($path)
{
return false;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace League\Flysystem\Cached\Storage;
use Redis;
class PhpRedis extends AbstractCache
{
/**
* @var Redis PhpRedis Client
*/
protected $client;
/**
* @var string storage key
*/
protected $key;
/**
* @var int|null seconds until cache expiration
*/
protected $expire;
/**
* Constructor.
*
* @param Redis|null $client phpredis client
* @param string $key storage key
* @param int|null $expire seconds until cache expiration
*/
public function __construct(Redis $client = null, $key = 'flysystem', $expire = null)
{
$this->client = $client ?: new Redis();
$this->key = $key;
$this->expire = $expire;
}
/**
* {@inheritdoc}
*/
public function load()
{
$contents = $this->client->get($this->key);
if ($contents !== false) {
$this->setFromStorage($contents);
}
}
/**
* {@inheritdoc}
*/
public function save()
{
$contents = $this->getForStorage();
$this->client->set($this->key, $contents);
if ($this->expire !== null) {
$this->client->expire($this->key, $this->expire);
}
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace League\Flysystem\Cached\Storage;
use Predis\Client;
class Predis extends AbstractCache
{
/**
* @var \Predis\Client Predis Client
*/
protected $client;
/**
* @var string storage key
*/
protected $key;
/**
* @var int|null seconds until cache expiration
*/
protected $expire;
/**
* Constructor.
*
* @param \Predis\Client $client predis client
* @param string $key storage key
* @param int|null $expire seconds until cache expiration
*/
public function __construct(Client $client = null, $key = 'flysystem', $expire = null)
{
$this->client = $client ?: new Client();
$this->key = $key;
$this->expire = $expire;
}
/**
* {@inheritdoc}
*/
public function load()
{
if (($contents = $this->executeCommand('get', [$this->key])) !== null) {
$this->setFromStorage($contents);
}
}
/**
* {@inheritdoc}
*/
public function save()
{
$contents = $this->getForStorage();
$this->executeCommand('set', [$this->key, $contents]);
if ($this->expire !== null) {
$this->executeCommand('expire', [$this->key, $this->expire]);
}
}
/**
* Execute a Predis command.
*
* @param string $name
* @param array $arguments
*
* @return string
*/
protected function executeCommand($name, array $arguments)
{
$command = $this->client->createCommand($name, $arguments);
return $this->client->executeCommand($command);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace League\Flysystem\Cached\Storage;
use Psr\Cache\CacheItemPoolInterface;
class Psr6Cache extends AbstractCache
{
/**
* @var CacheItemPoolInterface
*/
private $pool;
/**
* @var string storage key
*/
protected $key;
/**
* @var int|null seconds until cache expiration
*/
protected $expire;
/**
* Constructor.
*
* @param CacheItemPoolInterface $pool
* @param string $key storage key
* @param int|null $expire seconds until cache expiration
*/
public function __construct(CacheItemPoolInterface $pool, $key = 'flysystem', $expire = null)
{
$this->pool = $pool;
$this->key = $key;
$this->expire = $expire;
}
/**
* {@inheritdoc}
*/
public function save()
{
$item = $this->pool->getItem($this->key);
$item->set($this->getForStorage());
$item->expiresAfter($this->expire);
$this->pool->save($item);
}
/**
* {@inheritdoc}
*/
public function load()
{
$item = $this->pool->getItem($this->key);
if ($item->isHit()) {
$this->setFromStorage($item->get());
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace League\Flysystem\Cached\Storage;
use Stash\Pool;
class Stash extends AbstractCache
{
/**
* @var string storage key
*/
protected $key;
/**
* @var int|null seconds until cache expiration
*/
protected $expire;
/**
* @var \Stash\Pool Stash pool instance
*/
protected $pool;
/**
* Constructor.
*
* @param \Stash\Pool $pool
* @param string $key storage key
* @param int|null $expire seconds until cache expiration
*/
public function __construct(Pool $pool, $key = 'flysystem', $expire = null)
{
$this->key = $key;
$this->expire = $expire;
$this->pool = $pool;
}
/**
* {@inheritdoc}
*/
public function load()
{
$item = $this->pool->getItem($this->key);
$contents = $item->get();
if ($item->isMiss() === false) {
$this->setFromStorage($contents);
}
}
/**
* {@inheritdoc}
*/
public function save()
{
$contents = $this->getForStorage();
$item = $this->pool->getItem($this->key);
$item->set($contents, $this->expire);
}
}

View File

@@ -0,0 +1,104 @@
<?php
use League\Flysystem\Cached\Storage\Adapter;
use PHPUnit\Framework\TestCase;
class AdapterCacheTests extends TestCase
{
public function testLoadFail()
{
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(false);
$cache = new Adapter($adapter, 'file.json', 10);
$cache->load();
$this->assertFalse($cache->isComplete('', false));
}
public function testLoadExpired()
{
$response = ['contents' => json_encode([[], ['' => true], 1234567890]), 'path' => 'file.json'];
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(true);
$adapter->shouldReceive('read')->once()->with('file.json')->andReturn($response);
$adapter->shouldReceive('delete')->once()->with('file.json');
$cache = new Adapter($adapter, 'file.json', 10);
$cache->load();
$this->assertFalse($cache->isComplete('', false));
}
public function testLoadSuccess()
{
$response = ['contents' => json_encode([[], ['' => true], 9876543210]), 'path' => 'file.json'];
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(true);
$adapter->shouldReceive('read')->once()->with('file.json')->andReturn($response);
$cache = new Adapter($adapter, 'file.json', 10);
$cache->load();
$this->assertTrue($cache->isComplete('', false));
}
public function testSaveExists()
{
$response = json_encode([[], [], null]);
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(true);
$adapter->shouldReceive('update')->once()->with('file.json', $response, Mockery::any());
$cache = new Adapter($adapter, 'file.json', null);
$cache->save();
}
public function testSaveNew()
{
$response = json_encode([[], [], null]);
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(false);
$adapter->shouldReceive('write')->once()->with('file.json', $response, Mockery::any());
$cache = new Adapter($adapter, 'file.json', null);
$cache->save();
}
public function testStoreContentsRecursive()
{
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(false);
$adapter->shouldReceive('write')->once()->with('file.json', Mockery::any(), Mockery::any());
$cache = new Adapter($adapter, 'file.json', null);
$contents = [
['path' => 'foo/bar', 'dirname' => 'foo'],
['path' => 'afoo/bang', 'dirname' => 'afoo'],
];
$cache->storeContents('foo', $contents, true);
$this->assertTrue($cache->isComplete('foo', true));
$this->assertFalse($cache->isComplete('afoo', true));
}
public function testDeleteDir()
{
$cache_data = [
'foo' => ['path' => 'foo', 'type' => 'dir', 'dirname' => ''],
'foo/bar' => ['path' => 'foo/bar', 'type' => 'file', 'dirname' => 'foo'],
'foobaz' => ['path' => 'foobaz', 'type' => 'file', 'dirname' => ''],
];
$response = [
'contents' => json_encode([$cache_data, [], null]),
'path' => 'file.json',
];
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
$adapter->shouldReceive('has')->zeroOrMoreTimes()->with('file.json')->andReturn(true);
$adapter->shouldReceive('read')->once()->with('file.json')->andReturn($response);
$adapter->shouldReceive('update')->once()->with('file.json', Mockery::any(), Mockery::any())->andReturn(true);
$cache = new Adapter($adapter, 'file.json', null);
$cache->load();
$cache->deleteDir('foo', true);
$this->assertSame(1, count($cache->listContents('', true)));
}
}

View File

@@ -0,0 +1,16 @@
<?php
use League\Flysystem\Cached\CachedAdapter;
use PHPUnit\Framework\TestCase;
class InspectionTests extends TestCase {
public function testGetAdapter()
{
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
$cache = Mockery::mock('League\Flysystem\Cached\CacheInterface');
$cache->shouldReceive('load')->once();
$cached_adapter = new CachedAdapter($adapter, $cache);
$this->assertInstanceOf('League\Flysystem\AdapterInterface', $cached_adapter->getAdapter());
}
}

View File

@@ -0,0 +1,35 @@
<?php
use League\Flysystem\Cached\Storage\Memcached;
use PHPUnit\Framework\TestCase;
class MemcachedTests extends TestCase
{
public function testLoadFail()
{
$client = Mockery::mock('Memcached');
$client->shouldReceive('get')->once()->andReturn(false);
$cache = new Memcached($client);
$cache->load();
$this->assertFalse($cache->isComplete('', false));
}
public function testLoadSuccess()
{
$response = json_encode([[], ['' => true]]);
$client = Mockery::mock('Memcached');
$client->shouldReceive('get')->once()->andReturn($response);
$cache = new Memcached($client);
$cache->load();
$this->assertTrue($cache->isComplete('', false));
}
public function testSave()
{
$response = json_encode([[], []]);
$client = Mockery::mock('Memcached');
$client->shouldReceive('set')->once()->andReturn($response);
$cache = new Memcached($client);
$cache->save();
}
}

View File

@@ -0,0 +1,255 @@
<?php
use League\Flysystem\Cached\Storage\Memory;
use League\Flysystem\Util;
use PHPUnit\Framework\TestCase;
class MemoryCacheTests extends TestCase
{
public function testAutosave()
{
$cache = new Memory();
$cache->setAutosave(true);
$this->assertTrue($cache->getAutosave());
$cache->setAutosave(false);
$this->assertFalse($cache->getAutosave());
}
public function testCacheMiss()
{
$cache = new Memory();
$cache->storeMiss('path.txt');
$this->assertFalse($cache->has('path.txt'));
}
public function testIsComplete()
{
$cache = new Memory();
$this->assertFalse($cache->isComplete('dirname', false));
$cache->setComplete('dirname', false);
$this->assertFalse($cache->isComplete('dirname', true));
$cache->setComplete('dirname', true);
$this->assertTrue($cache->isComplete('dirname', true));
}
public function testCleanContents()
{
$cache = new Memory();
$input = [[
'path' => 'path.txt',
'visibility' => 'public',
'invalid' => 'thing',
]];
$expected = [[
'path' => 'path.txt',
'visibility' => 'public',
]];
$output = $cache->cleanContents($input);
$this->assertEquals($expected, $output);
}
public function testGetForStorage()
{
$cache = new Memory();
$input = [[
'path' => 'path.txt',
'visibility' => 'public',
'type' => 'file',
]];
$cache->storeContents('', $input, true);
$contents = $cache->listContents('', true);
$cached = [];
foreach ($contents as $item) {
$cached[$item['path']] = $item;
}
$this->assertEquals(json_encode([$cached, ['' => 'recursive']]), $cache->getForStorage());
}
public function testParentCompleteIsUsedDuringHas()
{
$cache = new Memory();
$cache->setComplete('dirname', false);
$this->assertFalse($cache->has('dirname/path.txt'));
}
public function testFlush()
{
$cache = new Memory();
$cache->setComplete('dirname', true);
$cache->updateObject('path.txt', [
'path' => 'path.txt',
'visibility' => 'public',
]);
$cache->flush();
$this->assertFalse($cache->isComplete('dirname', true));
$this->assertNull($cache->has('path.txt'));
}
public function testSetFromStorage()
{
$cache = new Memory();
$json = [[
'path.txt' => ['path' => 'path.txt', 'type' => 'file'],
], ['dirname' => 'recursive']];
$jsonString = json_encode($json);
$cache->setFromStorage($jsonString);
$this->assertTrue($cache->has('path.txt'));
$this->assertTrue($cache->isComplete('dirname', true));
}
public function testGetMetadataFail()
{
$cache = new Memory();
$this->assertFalse($cache->getMetadata('path.txt'));
}
public function metaGetterProvider()
{
return [
['getTimestamp', 'timestamp', 12344],
['getMimetype', 'mimetype', 'text/plain'],
['getSize', 'size', 12],
['getVisibility', 'visibility', 'private'],
['read', 'contents', '__contents__'],
];
}
/**
* @dataProvider metaGetterProvider
*
* @param $method
* @param $key
* @param $value
*/
public function testMetaGetters($method, $key, $value)
{
$cache = new Memory();
$this->assertFalse($cache->{$method}('path.txt'));
$cache->updateObject('path.txt', $object = [
'path' => 'path.txt',
'type' => 'file',
$key => $value,
] + Util::pathinfo('path.txt'), true);
$this->assertEquals($object, $cache->{$method}('path.txt'));
$this->assertEquals($object, $cache->getMetadata('path.txt'));
}
public function testGetDerivedMimetype()
{
$cache = new Memory();
$cache->updateObject('path.txt', [
'contents' => 'something',
]);
$response = $cache->getMimetype('path.txt');
$this->assertEquals('text/plain', $response['mimetype']);
}
public function testCopyFail()
{
$cache = new Memory();
$cache->copy('one', 'two');
$this->assertNull($cache->has('two'));
$this->assertNull($cache->load());
}
public function testStoreContents()
{
$cache = new Memory();
$cache->storeContents('dirname', [
['path' => 'dirname', 'type' => 'dir'],
['path' => 'dirname/nested', 'type' => 'dir'],
['path' => 'dirname/nested/deep', 'type' => 'dir'],
['path' => 'other/nested/deep', 'type' => 'dir'],
], true);
$this->isTrue($cache->isComplete('other/nested', true));
}
public function testDelete()
{
$cache = new Memory();
$cache->updateObject('path.txt', ['type' => 'file']);
$this->assertTrue($cache->has('path.txt'));
$cache->delete('path.txt');
$this->assertFalse($cache->has('path.txt'));
}
public function testDeleteDir()
{
$cache = new Memory();
$cache->storeContents('dirname', [
['path' => 'dirname/path.txt', 'type' => 'file'],
]);
$this->assertTrue($cache->isComplete('dirname', false));
$this->assertTrue($cache->has('dirname/path.txt'));
$cache->deleteDir('dirname');
$this->assertFalse($cache->isComplete('dirname', false));
$this->assertNull($cache->has('dirname/path.txt'));
}
public function testReadStream()
{
$cache = new Memory();
$this->assertFalse($cache->readStream('path.txt'));
}
public function testRename()
{
$cache = new Memory();
$cache->updateObject('path.txt', ['type' => 'file']);
$cache->rename('path.txt', 'newpath.txt');
$this->assertTrue($cache->has('newpath.txt'));
}
public function testCopy()
{
$cache = new Memory();
$cache->updateObject('path.txt', ['type' => 'file']);
$cache->copy('path.txt', 'newpath.txt');
$this->assertTrue($cache->has('newpath.txt'));
}
public function testComplextListContents()
{
$cache = new Memory();
$cache->storeContents('', [
['path' => 'dirname', 'type' => 'dir'],
['path' => 'dirname/file.txt', 'type' => 'file'],
['path' => 'other', 'type' => 'dir'],
['path' => 'other/file.txt', 'type' => 'file'],
['path' => 'other/nested/file.txt', 'type' => 'file'],
]);
$this->assertCount(3, $cache->listContents('other', true));
}
public function testComplextListContentsWithDeletedFile()
{
$cache = new Memory();
$cache->storeContents('', [
['path' => 'dirname', 'type' => 'dir'],
['path' => 'dirname/file.txt', 'type' => 'file'],
['path' => 'other', 'type' => 'dir'],
['path' => 'other/file.txt', 'type' => 'file'],
['path' => 'other/another_file.txt', 'type' => 'file'],
]);
$cache->delete('other/another_file.txt');
$this->assertCount(4, $cache->listContents('', true));
}
public function testCacheMissIfContentsIsFalse()
{
$cache = new Memory();
$cache->updateObject('path.txt', [
'path' => 'path.txt',
'contents' => false,
], true);
$this->assertFalse($cache->read('path.txt'));
}
}

View File

@@ -0,0 +1,35 @@
<?php
use League\Flysystem\Cached\Storage\Noop;
use PHPUnit\Framework\TestCase;
class NoopCacheTests extends TestCase
{
public function testNoop()
{
$cache = new Noop();
$this->assertEquals($cache, $cache->storeMiss('file.txt'));
$this->assertNull($cache->setComplete('', false));
$this->assertNull($cache->load());
$this->assertNull($cache->flush());
$this->assertNull($cache->has('path.txt'));
$this->assertNull($cache->autosave());
$this->assertFalse($cache->isComplete('', false));
$this->assertFalse($cache->read('something'));
$this->assertFalse($cache->readStream('something'));
$this->assertFalse($cache->getMetadata('something'));
$this->assertFalse($cache->getMimetype('something'));
$this->assertFalse($cache->getSize('something'));
$this->assertFalse($cache->getTimestamp('something'));
$this->assertFalse($cache->getVisibility('something'));
$this->assertEmpty($cache->listContents('', false));
$this->assertFalse($cache->rename('', ''));
$this->assertFalse($cache->copy('', ''));
$this->assertNull($cache->save());
$object = ['path' => 'path.ext'];
$this->assertEquals($object, $cache->updateObject('path.txt', $object));
$this->assertEquals([['path' => 'some/file.txt']], $cache->storeContents('unknwon', [
['path' => 'some/file.txt'],
], false));
}
}

View File

@@ -0,0 +1,45 @@
<?php
use League\Flysystem\Cached\Storage\PhpRedis;
use PHPUnit\Framework\TestCase;
class PhpRedisTests extends TestCase
{
public function testLoadFail()
{
$client = Mockery::mock('Redis');
$client->shouldReceive('get')->with('flysystem')->once()->andReturn(false);
$cache = new PhpRedis($client);
$cache->load();
$this->assertFalse($cache->isComplete('', false));
}
public function testLoadSuccess()
{
$response = json_encode([[], ['' => true]]);
$client = Mockery::mock('Redis');
$client->shouldReceive('get')->with('flysystem')->once()->andReturn($response);
$cache = new PhpRedis($client);
$cache->load();
$this->assertTrue($cache->isComplete('', false));
}
public function testSave()
{
$data = json_encode([[], []]);
$client = Mockery::mock('Redis');
$client->shouldReceive('set')->with('flysystem', $data)->once();
$cache = new PhpRedis($client);
$cache->save();
}
public function testSaveWithExpire()
{
$data = json_encode([[], []]);
$client = Mockery::mock('Redis');
$client->shouldReceive('set')->with('flysystem', $data)->once();
$client->shouldReceive('expire')->with('flysystem', 20)->once();
$cache = new PhpRedis($client, 'flysystem', 20);
$cache->save();
}
}

View File

@@ -0,0 +1,55 @@
<?php
use League\Flysystem\Cached\Storage\Predis;
use PHPUnit\Framework\TestCase;
class PredisTests extends TestCase
{
public function testLoadFail()
{
$client = Mockery::mock('Predis\Client');
$command = Mockery::mock('Predis\Command\CommandInterface');
$client->shouldReceive('createCommand')->with('get', ['flysystem'])->once()->andReturn($command);
$client->shouldReceive('executeCommand')->with($command)->andReturn(null);
$cache = new Predis($client);
$cache->load();
$this->assertFalse($cache->isComplete('', false));
}
public function testLoadSuccess()
{
$response = json_encode([[], ['' => true]]);
$client = Mockery::mock('Predis\Client');
$command = Mockery::mock('Predis\Command\CommandInterface');
$client->shouldReceive('createCommand')->with('get', ['flysystem'])->once()->andReturn($command);
$client->shouldReceive('executeCommand')->with($command)->andReturn($response);
$cache = new Predis($client);
$cache->load();
$this->assertTrue($cache->isComplete('', false));
}
public function testSave()
{
$data = json_encode([[], []]);
$client = Mockery::mock('Predis\Client');
$command = Mockery::mock('Predis\Command\CommandInterface');
$client->shouldReceive('createCommand')->with('set', ['flysystem', $data])->once()->andReturn($command);
$client->shouldReceive('executeCommand')->with($command)->once();
$cache = new Predis($client);
$cache->save();
}
public function testSaveWithExpire()
{
$data = json_encode([[], []]);
$client = Mockery::mock('Predis\Client');
$command = Mockery::mock('Predis\Command\CommandInterface');
$client->shouldReceive('createCommand')->with('set', ['flysystem', $data])->once()->andReturn($command);
$client->shouldReceive('executeCommand')->with($command)->once();
$expireCommand = Mockery::mock('Predis\Command\CommandInterface');
$client->shouldReceive('createCommand')->with('expire', ['flysystem', 20])->once()->andReturn($expireCommand);
$client->shouldReceive('executeCommand')->with($expireCommand)->once();
$cache = new Predis($client, 'flysystem', 20);
$cache->save();
}
}

View File

@@ -0,0 +1,45 @@
<?php
use League\Flysystem\Cached\Storage\Psr6Cache;
use PHPUnit\Framework\TestCase;
class Psr6CacheTests extends TestCase
{
public function testLoadFail()
{
$pool = Mockery::mock('Psr\Cache\CacheItemPoolInterface');
$item = Mockery::mock('Psr\Cache\CacheItemInterface');
$item->shouldReceive('isHit')->once()->andReturn(false);
$pool->shouldReceive('getItem')->once()->andReturn($item);
$cache = new Psr6Cache($pool);
$cache->load();
$this->assertFalse($cache->isComplete('', false));
}
public function testLoadSuccess()
{
$response = json_encode([[], ['' => true]]);
$pool = Mockery::mock('Psr\Cache\CacheItemPoolInterface');
$item = Mockery::mock('Psr\Cache\CacheItemInterface');
$item->shouldReceive('get')->once()->andReturn($response);
$item->shouldReceive('isHit')->once()->andReturn(true);
$pool->shouldReceive('getItem')->once()->andReturn($item);
$cache = new Psr6Cache($pool);
$cache->load();
$this->assertTrue($cache->isComplete('', false));
}
public function testSave()
{
$response = json_encode([[], []]);
$ttl = 4711;
$pool = Mockery::mock('Psr\Cache\CacheItemPoolInterface');
$item = Mockery::mock('Psr\Cache\CacheItemInterface');
$item->shouldReceive('expiresAfter')->once()->with($ttl);
$item->shouldReceive('set')->once()->andReturn($response);
$pool->shouldReceive('getItem')->once()->andReturn($item);
$pool->shouldReceive('save')->once()->with($item);
$cache = new Psr6Cache($pool, 'foo', $ttl);
$cache->save();
}
}

View File

@@ -0,0 +1,43 @@
<?php
use League\Flysystem\Cached\Storage\Stash;
use PHPUnit\Framework\TestCase;
class StashTests extends TestCase
{
public function testLoadFail()
{
$pool = Mockery::mock('Stash\Pool');
$item = Mockery::mock('Stash\Item');
$item->shouldReceive('get')->once()->andReturn(null);
$item->shouldReceive('isMiss')->once()->andReturn(true);
$pool->shouldReceive('getItem')->once()->andReturn($item);
$cache = new Stash($pool);
$cache->load();
$this->assertFalse($cache->isComplete('', false));
}
public function testLoadSuccess()
{
$response = json_encode([[], ['' => true]]);
$pool = Mockery::mock('Stash\Pool');
$item = Mockery::mock('Stash\Item');
$item->shouldReceive('get')->once()->andReturn($response);
$item->shouldReceive('isMiss')->once()->andReturn(false);
$pool->shouldReceive('getItem')->once()->andReturn($item);
$cache = new Stash($pool);
$cache->load();
$this->assertTrue($cache->isComplete('', false));
}
public function testSave()
{
$response = json_encode([[], []]);
$pool = Mockery::mock('Stash\Pool');
$item = Mockery::mock('Stash\Item');
$item->shouldReceive('set')->once()->andReturn($response);
$pool->shouldReceive('getItem')->once()->andReturn($item);
$cache = new Stash($pool);
$cache->save();
}
}