no message
This commit is contained in:
parent
7fc94a0751
commit
756fa28b6a
|
@ -1,2 +1 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitf79aa87d3682b4ff439571b93fac530f::getLoader();
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../symfony/var-dumper/Resources/bin" && pwd)
|
||||
|
||||
if [ -d /proc/cygdrive ]; then
|
||||
case $(which php) in
|
||||
$(readlink -n /proc/cygdrive)/*)
|
||||
# We are in Cygwin using Windows php, so the path must be translated
|
||||
dir=$(cygpath -m "$dir");
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
"${dir}/var-dump-server" "$@"
|
|
@ -0,0 +1,4 @@
|
|||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/../symfony/var-dumper/Resources/bin/var-dump-server
|
||||
php "%BIN_TARGET%" %*
|
|
@ -0,0 +1,445 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see http://www.php-fig.org/psr/psr-0/
|
||||
* @see http://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
|
||||
'538ca81a9a966a6716601ecf48f4eaef' => $vendorDir . '/opis/closure/functions.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
|
||||
);
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'' => array($baseDir . '/extend'),
|
||||
);
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'think\\view\\driver\\' => array($vendorDir . '/topthink/think-view/src'),
|
||||
'think\\trace\\' => array($vendorDir . '/topthink/think-trace/src'),
|
||||
'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),
|
||||
'think\\app\\' => array($vendorDir . '/topthink/think-multi-app/src'),
|
||||
'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-template/src'),
|
||||
'app\\' => array($baseDir . '/app'),
|
||||
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
|
||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
|
||||
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
|
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
|
||||
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
||||
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
|
||||
'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'),
|
||||
'League\\Flysystem\\Cached\\' => array($vendorDir . '/league/flysystem-cached-adapter/src'),
|
||||
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
|
||||
);
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitf79aa87d3682b4ff439571b93fac530f
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitf79aa87d3682b4ff439571b93fac530f', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitf79aa87d3682b4ff439571b93fac530f', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require_once __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitf79aa87d3682b4ff439571b93fac530f::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInitf79aa87d3682b4ff439571b93fac530f::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequiref79aa87d3682b4ff439571b93fac530f($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequiref79aa87d3682b4ff439571b93fac530f($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitf79aa87d3682b4ff439571b93fac530f
|
||||
{
|
||||
public static $files = array (
|
||||
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
|
||||
'538ca81a9a966a6716601ecf48f4eaef' => __DIR__ . '/..' . '/opis/closure/functions.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
't' =>
|
||||
array (
|
||||
'think\\view\\driver\\' => 18,
|
||||
'think\\trace\\' => 12,
|
||||
'think\\captcha\\' => 14,
|
||||
'think\\app\\' => 10,
|
||||
'think\\' => 6,
|
||||
),
|
||||
'a' =>
|
||||
array (
|
||||
'app\\' => 4,
|
||||
),
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Php72\\' => 23,
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
'Symfony\\Component\\VarDumper\\' => 28,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\SimpleCache\\' => 16,
|
||||
'Psr\\Log\\' => 8,
|
||||
'Psr\\Container\\' => 14,
|
||||
'Psr\\Cache\\' => 10,
|
||||
'PHPMailer\\PHPMailer\\' => 20,
|
||||
),
|
||||
'O' =>
|
||||
array (
|
||||
'Opis\\Closure\\' => 13,
|
||||
),
|
||||
'L' =>
|
||||
array (
|
||||
'League\\Flysystem\\Cached\\' => 24,
|
||||
'League\\Flysystem\\' => 17,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'think\\view\\driver\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/topthink/think-view/src',
|
||||
),
|
||||
'think\\trace\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/topthink/think-trace/src',
|
||||
),
|
||||
'think\\captcha\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/topthink/think-captcha/src',
|
||||
),
|
||||
'think\\app\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/topthink/think-multi-app/src',
|
||||
),
|
||||
'think\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/topthink/framework/src/think',
|
||||
1 => __DIR__ . '/..' . '/topthink/think-helper/src',
|
||||
2 => __DIR__ . '/..' . '/topthink/think-orm/src',
|
||||
3 => __DIR__ . '/..' . '/topthink/think-template/src',
|
||||
),
|
||||
'app\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/app',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php72\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
|
||||
),
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||
),
|
||||
'Symfony\\Component\\VarDumper\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/var-dumper',
|
||||
),
|
||||
'Psr\\SimpleCache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
|
||||
),
|
||||
'Psr\\Log\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
|
||||
),
|
||||
'Psr\\Container\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/container/src',
|
||||
),
|
||||
'Psr\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/cache/src',
|
||||
),
|
||||
'PHPMailer\\PHPMailer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
|
||||
),
|
||||
'Opis\\Closure\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/opis/closure/src',
|
||||
),
|
||||
'League\\Flysystem\\Cached\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/flysystem-cached-adapter/src',
|
||||
),
|
||||
'League\\Flysystem\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/flysystem/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $fallbackDirsPsr0 = array (
|
||||
0 => __DIR__ . '/../..' . '/extend',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitf79aa87d3682b4ff439571b93fac530f::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitf79aa87d3682b4ff439571b93fac530f::$prefixDirsPsr4;
|
||||
$loader->fallbackDirsPsr0 = ComposerStaticInitf79aa87d3682b4ff439571b93fac530f::$fallbackDirsPsr0;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
|||
; top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
; Unix-style newlines
|
||||
[*]
|
||||
end_of_line = LF
|
||||
|
||||
[*.php]
|
||||
indent_style = space
|
||||
indent_size = 4
|
|
@ -0,0 +1,4 @@
|
|||
coverage
|
||||
coverage.xml
|
||||
composer.lock
|
||||
vendor
|
|
@ -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/'));
|
|
@ -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]
|
|
@ -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'
|
|
@ -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.
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
suites:
|
||||
cached_adapter_suite:
|
||||
namespace: League\Flysystem\Cached
|
||||
psr4_prefix: League\Flysystem\Cached
|
||||
formatter.name: pretty
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
include __DIR__.'/vendor/autoload.php';
|
|
@ -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>
|
|
@ -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/)
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2013-2019 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.
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"name": "league/flysystem",
|
||||
"description": "Filesystem abstraction: Many filesystems, one API.",
|
||||
"keywords": [
|
||||
"filesystem", "filesystems", "files", "storage", "dropbox", "aws",
|
||||
"abstraction", "s3", "ftp", "sftp", "remote", "webdav",
|
||||
"file systems", "cloud", "cloud files", "rackspace", "copy.com"
|
||||
],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frank de Jonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"ext-fileinfo": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^3.4",
|
||||
"phpunit/phpunit": "^5.7.10"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\Stub\\": "stub/"
|
||||
},
|
||||
"files": [
|
||||
"tests/PHPUnitHacks.php"
|
||||
]
|
||||
},
|
||||
"suggest": {
|
||||
"ext-fileinfo": "Required for MimeType",
|
||||
"league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
|
||||
"league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
|
||||
"league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
|
||||
"league/flysystem-webdav": "Allows you to use WebDAV storage",
|
||||
"league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
|
||||
"league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
|
||||
"spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
|
||||
"srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications",
|
||||
"league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
|
||||
"ext-ftp": "Allows you to use FTP server storage",
|
||||
"ext-openssl": "Allows you to use FTPS server storage",
|
||||
"league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
|
||||
"league/flysystem-ziparchive": "Allows you to use ZipArchive adapter"
|
||||
},
|
||||
"conflict": {
|
||||
"league/flysystem-sftp": "<1.0.6"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1-dev"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"phpstan": "php phpstan.php"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
# Deprecations
|
||||
|
||||
This document lists all the planned deprecations.
|
||||
|
||||
## Handlers will be removed in 2.0
|
||||
|
||||
The `Handler` type and associated calls will be removed in version 2.0.
|
||||
|
||||
### Upgrade path
|
||||
|
||||
You should create your own implementation for handling OOP usage,
|
||||
but it's recommended to move away from using an OOP-style wrapper entirely.
|
||||
|
||||
The reason for this is that it's too easy for implementation details (for
|
||||
your application this is Flysystem) to leak into the application. The most
|
||||
important part for Flysystem is that it improves portability and creates a
|
||||
solid boundary between your application core and the infrastructure you use.
|
||||
The OOP-style handling breaks this principle, therefore I want to stop
|
||||
promoting it.
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
use League\Flysystem\AdapterInterface;
|
||||
|
||||
abstract class AbstractAdapter implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* @var string|null path prefix
|
||||
*/
|
||||
protected $pathPrefix;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $pathSeparator = '/';
|
||||
|
||||
/**
|
||||
* Set the path prefix.
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPathPrefix($prefix)
|
||||
{
|
||||
$prefix = (string) $prefix;
|
||||
|
||||
if ($prefix === '') {
|
||||
$this->pathPrefix = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pathPrefix = rtrim($prefix, '\\/') . $this->pathSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path prefix.
|
||||
*
|
||||
* @return string|null path prefix or null if pathPrefix is empty
|
||||
*/
|
||||
public function getPathPrefix()
|
||||
{
|
||||
return $this->pathPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix a path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string prefixed path
|
||||
*/
|
||||
public function applyPathPrefix($path)
|
||||
{
|
||||
return $this->getPathPrefix() . ltrim($path, '\\/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a path prefix.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string path without the prefix
|
||||
*/
|
||||
public function removePathPrefix($path)
|
||||
{
|
||||
return substr($path, strlen($this->getPathPrefix()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,693 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
use DateTime;
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use League\Flysystem\Config;
|
||||
use League\Flysystem\NotSupportedException;
|
||||
use League\Flysystem\SafeStorage;
|
||||
use RuntimeException;
|
||||
|
||||
abstract class AbstractFtpAdapter extends AbstractAdapter
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $port = 21;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $ssl = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $timeout = 90;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $passive = true;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $separator = '/';
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $permPublic = 0744;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $permPrivate = 0700;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $configurable = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $systemType;
|
||||
|
||||
/**
|
||||
* @var SafeStorage
|
||||
*/
|
||||
protected $safeStorage;
|
||||
|
||||
/**
|
||||
* True to enable timestamps for FTP servers that return unix-style listings.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enableTimestampsOnUnixListings = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->safeStorage = new SafeStorage();
|
||||
$this->setConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the config.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setConfig(array $config)
|
||||
{
|
||||
foreach ($this->configurable as $setting) {
|
||||
if ( ! isset($config[$setting])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method = 'set' . ucfirst($setting);
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
$this->$method($config[$setting]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the host.
|
||||
*
|
||||
* @param string $host
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHost($host)
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the public permission value.
|
||||
*
|
||||
* @param int $permPublic
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPermPublic($permPublic)
|
||||
{
|
||||
$this->permPublic = $permPublic;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the private permission value.
|
||||
*
|
||||
* @param int $permPrivate
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPermPrivate($permPrivate)
|
||||
{
|
||||
$this->permPrivate = $permPrivate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ftp port.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPort()
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root folder to work from.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRoot()
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ftp port.
|
||||
*
|
||||
* @param int|string $port
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPort($port)
|
||||
{
|
||||
$this->port = (int) $port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the root folder to work from.
|
||||
*
|
||||
* @param string $root
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRoot($root)
|
||||
{
|
||||
$this->root = rtrim($root, '\\/') . $this->separator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ftp username.
|
||||
*
|
||||
* @return string username
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
$username = $this->safeStorage->retrieveSafely('username');
|
||||
|
||||
return $username !== null ? $username : 'anonymous';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ftp username.
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->safeStorage->storeSafely('username', $username);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password.
|
||||
*
|
||||
* @return string password
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->safeStorage->retrieveSafely('password');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ftp password.
|
||||
*
|
||||
* @param string $password
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->safeStorage->storeSafely('password', $password);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of seconds before the connection will timeout.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeout()
|
||||
{
|
||||
return $this->timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of seconds before the connection should timeout.
|
||||
*
|
||||
* @param int $timeout
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
$this->timeout = (int) $timeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the FTP system type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSystemType()
|
||||
{
|
||||
return $this->systemType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the FTP system type (windows or unix).
|
||||
*
|
||||
* @param string $systemType
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSystemType($systemType)
|
||||
{
|
||||
$this->systemType = strtolower($systemType);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* True to enable timestamps for FTP servers that return unix-style listings.
|
||||
*
|
||||
* @param bool $bool
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEnableTimestampsOnUnixListings($bool = false)
|
||||
{
|
||||
$this->enableTimestampsOnUnixListings = $bool;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false)
|
||||
{
|
||||
return $this->listDirectoryContents($directory, $recursive);
|
||||
}
|
||||
|
||||
abstract protected function listDirectoryContents($directory, $recursive = false);
|
||||
|
||||
/**
|
||||
* Normalize a directory listing.
|
||||
*
|
||||
* @param array $listing
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return array directory listing
|
||||
*/
|
||||
protected function normalizeListing(array $listing, $prefix = '')
|
||||
{
|
||||
$base = $prefix;
|
||||
$result = [];
|
||||
$listing = $this->removeDotDirectories($listing);
|
||||
|
||||
while ($item = array_shift($listing)) {
|
||||
if (preg_match('#^.*:$#', $item)) {
|
||||
$base = preg_replace('~^\./*|:$~', '', $item);
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = $this->normalizeObject($item, $base);
|
||||
}
|
||||
|
||||
return $this->sortListing($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort a directory listing.
|
||||
*
|
||||
* @param array $result
|
||||
*
|
||||
* @return array sorted listing
|
||||
*/
|
||||
protected function sortListing(array $result)
|
||||
{
|
||||
$compare = function ($one, $two) {
|
||||
return strnatcmp($one['path'], $two['path']);
|
||||
};
|
||||
|
||||
usort($result, $compare);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a file entry.
|
||||
*
|
||||
* @param string $item
|
||||
* @param string $base
|
||||
*
|
||||
* @return array normalized file array
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
protected function normalizeObject($item, $base)
|
||||
{
|
||||
$systemType = $this->systemType ?: $this->detectSystemType($item);
|
||||
|
||||
if ($systemType === 'unix') {
|
||||
return $this->normalizeUnixObject($item, $base);
|
||||
} elseif ($systemType === 'windows') {
|
||||
return $this->normalizeWindowsObject($item, $base);
|
||||
}
|
||||
|
||||
throw NotSupportedException::forFtpSystemType($systemType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a Unix file entry.
|
||||
*
|
||||
* Given $item contains:
|
||||
* '-rw-r--r-- 1 ftp ftp 409 Aug 19 09:01 file1.txt'
|
||||
*
|
||||
* This function will return:
|
||||
* [
|
||||
* 'type' => 'file',
|
||||
* 'path' => 'file1.txt',
|
||||
* 'visibility' => 'public',
|
||||
* 'size' => 409,
|
||||
* 'timestamp' => 1566205260
|
||||
* ]
|
||||
*
|
||||
* @param string $item
|
||||
* @param string $base
|
||||
*
|
||||
* @return array normalized file array
|
||||
*/
|
||||
protected function normalizeUnixObject($item, $base)
|
||||
{
|
||||
$item = preg_replace('#\s+#', ' ', trim($item), 7);
|
||||
|
||||
if (count(explode(' ', $item, 9)) !== 9) {
|
||||
throw new RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
|
||||
}
|
||||
|
||||
list($permissions, /* $number */, /* $owner */, /* $group */, $size, $month, $day, $timeOrYear, $name) = explode(' ', $item, 9);
|
||||
$type = $this->detectType($permissions);
|
||||
$path = $base === '' ? $name : $base . $this->separator . $name;
|
||||
|
||||
if ($type === 'dir') {
|
||||
return compact('type', 'path');
|
||||
}
|
||||
|
||||
$permissions = $this->normalizePermissions($permissions);
|
||||
$visibility = $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE;
|
||||
$size = (int) $size;
|
||||
|
||||
$result = compact('type', 'path', 'visibility', 'size');
|
||||
if ($this->enableTimestampsOnUnixListings) {
|
||||
$timestamp = $this->normalizeUnixTimestamp($month, $day, $timeOrYear);
|
||||
$result += compact('timestamp');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only accurate to the minute (current year), or to the day.
|
||||
*
|
||||
* Inadequacies in timestamp accuracy are due to limitations of the FTP 'LIST' command
|
||||
*
|
||||
* Note: The 'MLSD' command is a machine-readable replacement for 'LIST'
|
||||
* but many FTP servers do not support it :(
|
||||
*
|
||||
* @param string $month e.g. 'Aug'
|
||||
* @param string $day e.g. '19'
|
||||
* @param string $timeOrYear e.g. '09:01' OR '2015'
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function normalizeUnixTimestamp($month, $day, $timeOrYear)
|
||||
{
|
||||
if (is_numeric($timeOrYear)) {
|
||||
$year = $timeOrYear;
|
||||
$hour = '00';
|
||||
$minute = '00';
|
||||
$seconds = '00';
|
||||
} else {
|
||||
$year = date('Y');
|
||||
list($hour, $minute) = explode(':', $timeOrYear);
|
||||
$seconds = '00';
|
||||
}
|
||||
$dateTime = DateTime::createFromFormat('Y-M-j-G:i:s', "{$year}-{$month}-{$day}-{$hour}:{$minute}:{$seconds}");
|
||||
|
||||
return $dateTime->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a Windows/DOS file entry.
|
||||
*
|
||||
* @param string $item
|
||||
* @param string $base
|
||||
*
|
||||
* @return array normalized file array
|
||||
*/
|
||||
protected function normalizeWindowsObject($item, $base)
|
||||
{
|
||||
$item = preg_replace('#\s+#', ' ', trim($item), 3);
|
||||
|
||||
if (count(explode(' ', $item, 4)) !== 4) {
|
||||
throw new RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
|
||||
}
|
||||
|
||||
list($date, $time, $size, $name) = explode(' ', $item, 4);
|
||||
$path = $base === '' ? $name : $base . $this->separator . $name;
|
||||
|
||||
// Check for the correct date/time format
|
||||
$format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i';
|
||||
$dt = DateTime::createFromFormat($format, $date . $time);
|
||||
$timestamp = $dt ? $dt->getTimestamp() : (int) strtotime("$date $time");
|
||||
|
||||
if ($size === '<DIR>') {
|
||||
$type = 'dir';
|
||||
|
||||
return compact('type', 'path', 'timestamp');
|
||||
}
|
||||
|
||||
$type = 'file';
|
||||
$visibility = AdapterInterface::VISIBILITY_PUBLIC;
|
||||
$size = (int) $size;
|
||||
|
||||
return compact('type', 'path', 'visibility', 'size', 'timestamp');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system type from a listing item.
|
||||
*
|
||||
* @param string $item
|
||||
*
|
||||
* @return string the system type
|
||||
*/
|
||||
protected function detectSystemType($item)
|
||||
{
|
||||
return preg_match('/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/', $item) ? 'windows' : 'unix';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file type from the permissions.
|
||||
*
|
||||
* @param string $permissions
|
||||
*
|
||||
* @return string file type
|
||||
*/
|
||||
protected function detectType($permissions)
|
||||
{
|
||||
return substr($permissions, 0, 1) === 'd' ? 'dir' : 'file';
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a permissions string.
|
||||
*
|
||||
* @param string $permissions
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function normalizePermissions($permissions)
|
||||
{
|
||||
// remove the type identifier
|
||||
$permissions = substr($permissions, 1);
|
||||
|
||||
// map the string rights to the numeric counterparts
|
||||
$map = ['-' => '0', 'r' => '4', 'w' => '2', 'x' => '1'];
|
||||
$permissions = strtr($permissions, $map);
|
||||
|
||||
// split up the permission groups
|
||||
$parts = str_split($permissions, 3);
|
||||
|
||||
// convert the groups
|
||||
$mapper = function ($part) {
|
||||
return array_sum(str_split($part));
|
||||
};
|
||||
|
||||
// converts to decimal number
|
||||
return octdec(implode('', array_map($mapper, $parts)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out dot-directories.
|
||||
*
|
||||
* @param array $list
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function removeDotDirectories(array $list)
|
||||
{
|
||||
$filter = function ($line) {
|
||||
return $line !== '' && ! preg_match('#.* \.(\.)?$|^total#', $line);
|
||||
};
|
||||
|
||||
return array_filter($list, $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
return $this->getMetadata($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
return $this->getMetadata($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getVisibility($path)
|
||||
{
|
||||
return $this->getMetadata($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a directory exists.
|
||||
*
|
||||
* @param string $dirname
|
||||
*/
|
||||
public function ensureDirectory($dirname)
|
||||
{
|
||||
$dirname = (string) $dirname;
|
||||
|
||||
if ($dirname !== '' && ! $this->has($dirname)) {
|
||||
$this->createDir($dirname, new Config());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
$tries = 0;
|
||||
|
||||
while ( ! $this->isConnected() && $tries < 3) {
|
||||
$tries++;
|
||||
$this->disconnect();
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the public permission value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPermPublic()
|
||||
{
|
||||
return $this->permPublic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the private permission value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPermPrivate()
|
||||
{
|
||||
return $this->permPrivate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect on destruction.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a connection.
|
||||
*/
|
||||
abstract public function connect();
|
||||
|
||||
/**
|
||||
* Close the connection.
|
||||
*/
|
||||
abstract public function disconnect();
|
||||
|
||||
/**
|
||||
* Check if a connection is active.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isConnected();
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
/**
|
||||
* Adapters that implement this interface let the Filesystem know that files can be overwritten using the write
|
||||
* functions and don't need the update function to be called. This can help improve performance when asserts are disabled.
|
||||
*/
|
||||
interface CanOverwriteFiles
|
||||
{
|
||||
}
|
|
@ -0,0 +1,572 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
use ErrorException;
|
||||
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use League\Flysystem\Config;
|
||||
use League\Flysystem\Util;
|
||||
use League\Flysystem\Util\MimeType;
|
||||
use RuntimeException;
|
||||
|
||||
class Ftp extends AbstractFtpAdapter
|
||||
{
|
||||
use StreamedCopyTrait;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $transferMode = FTP_BINARY;
|
||||
|
||||
/**
|
||||
* @var null|bool
|
||||
*/
|
||||
protected $ignorePassiveAddress = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $recurseManually = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $utf8 = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $configurable = [
|
||||
'host',
|
||||
'port',
|
||||
'username',
|
||||
'password',
|
||||
'ssl',
|
||||
'timeout',
|
||||
'root',
|
||||
'permPrivate',
|
||||
'permPublic',
|
||||
'passive',
|
||||
'transferMode',
|
||||
'systemType',
|
||||
'ignorePassiveAddress',
|
||||
'recurseManually',
|
||||
'utf8',
|
||||
'enableTimestampsOnUnixListings',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isPureFtpd;
|
||||
|
||||
/**
|
||||
* Set the transfer mode.
|
||||
*
|
||||
* @param int $mode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTransferMode($mode)
|
||||
{
|
||||
$this->transferMode = $mode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if Ssl is enabled.
|
||||
*
|
||||
* @param bool $ssl
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSsl($ssl)
|
||||
{
|
||||
$this->ssl = (bool) $ssl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if passive mode should be used.
|
||||
*
|
||||
* @param bool $passive
|
||||
*/
|
||||
public function setPassive($passive = true)
|
||||
{
|
||||
$this->passive = $passive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $ignorePassiveAddress
|
||||
*/
|
||||
public function setIgnorePassiveAddress($ignorePassiveAddress)
|
||||
{
|
||||
$this->ignorePassiveAddress = $ignorePassiveAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $recurseManually
|
||||
*/
|
||||
public function setRecurseManually($recurseManually)
|
||||
{
|
||||
$this->recurseManually = $recurseManually;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $utf8
|
||||
*/
|
||||
public function setUtf8($utf8)
|
||||
{
|
||||
$this->utf8 = (bool) $utf8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the FTP server.
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if ($this->ssl) {
|
||||
$this->connection = ftp_ssl_connect($this->getHost(), $this->getPort(), $this->getTimeout());
|
||||
} else {
|
||||
$this->connection = ftp_connect($this->getHost(), $this->getPort(), $this->getTimeout());
|
||||
}
|
||||
|
||||
if ( ! $this->connection) {
|
||||
throw new RuntimeException('Could not connect to host: ' . $this->getHost() . ', port:' . $this->getPort());
|
||||
}
|
||||
|
||||
$this->login();
|
||||
$this->setUtf8Mode();
|
||||
$this->setConnectionPassiveMode();
|
||||
$this->setConnectionRoot();
|
||||
$this->isPureFtpd = $this->isPureFtpdServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection to UTF-8 mode.
|
||||
*/
|
||||
protected function setUtf8Mode()
|
||||
{
|
||||
if ($this->utf8) {
|
||||
$response = ftp_raw($this->connection, "OPTS UTF8 ON");
|
||||
if (substr($response[0], 0, 3) !== '200') {
|
||||
throw new RuntimeException(
|
||||
'Could not set UTF-8 mode for connection: ' . $this->getHost() . '::' . $this->getPort()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connections to passive mode.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function setConnectionPassiveMode()
|
||||
{
|
||||
if (is_bool($this->ignorePassiveAddress) && defined('FTP_USEPASVADDRESS')) {
|
||||
ftp_set_option($this->connection, FTP_USEPASVADDRESS, ! $this->ignorePassiveAddress);
|
||||
}
|
||||
|
||||
if ( ! ftp_pasv($this->connection, $this->passive)) {
|
||||
throw new RuntimeException(
|
||||
'Could not set passive mode for connection: ' . $this->getHost() . '::' . $this->getPort()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection root.
|
||||
*/
|
||||
protected function setConnectionRoot()
|
||||
{
|
||||
$root = $this->getRoot();
|
||||
$connection = $this->connection;
|
||||
|
||||
if ($root && ! ftp_chdir($connection, $root)) {
|
||||
throw new RuntimeException('Root is invalid or does not exist: ' . $this->getRoot());
|
||||
}
|
||||
|
||||
// Store absolute path for further reference.
|
||||
// This is needed when creating directories and
|
||||
// initial root was a relative path, else the root
|
||||
// would be relative to the chdir'd path.
|
||||
$this->root = ftp_pwd($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function login()
|
||||
{
|
||||
set_error_handler(function () {
|
||||
});
|
||||
$isLoggedIn = ftp_login(
|
||||
$this->connection,
|
||||
$this->getUsername(),
|
||||
$this->getPassword()
|
||||
);
|
||||
restore_error_handler();
|
||||
|
||||
if ( ! $isLoggedIn) {
|
||||
$this->disconnect();
|
||||
throw new RuntimeException(
|
||||
'Could not login with connection: ' . $this->getHost() . '::' . $this->getPort(
|
||||
) . ', username: ' . $this->getUsername()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the FTP server.
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
if (is_resource($this->connection)) {
|
||||
ftp_close($this->connection);
|
||||
}
|
||||
|
||||
$this->connection = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function write($path, $contents, Config $config)
|
||||
{
|
||||
$stream = fopen('php://temp', 'w+b');
|
||||
fwrite($stream, $contents);
|
||||
rewind($stream);
|
||||
$result = $this->writeStream($path, $stream, $config);
|
||||
fclose($stream);
|
||||
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result['contents'] = $contents;
|
||||
$result['mimetype'] = $config->get('mimetype') ?: Util::guessMimeType($path, $contents);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function writeStream($path, $resource, Config $config)
|
||||
{
|
||||
$this->ensureDirectory(Util::dirname($path));
|
||||
|
||||
if ( ! ftp_fput($this->getConnection(), $path, $resource, $this->transferMode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($visibility = $config->get('visibility')) {
|
||||
$this->setVisibility($path, $visibility);
|
||||
}
|
||||
|
||||
$type = 'file';
|
||||
|
||||
return compact('type', 'path', 'visibility');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function update($path, $contents, Config $config)
|
||||
{
|
||||
return $this->write($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function updateStream($path, $resource, Config $config)
|
||||
{
|
||||
return $this->writeStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
return ftp_rename($this->getConnection(), $path, $newpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
return ftp_delete($this->getConnection(), $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function deleteDir($dirname)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$contents = array_reverse($this->listDirectoryContents($dirname, false));
|
||||
|
||||
foreach ($contents as $object) {
|
||||
if ($object['type'] === 'file') {
|
||||
if ( ! ftp_delete($connection, $object['path'])) {
|
||||
return false;
|
||||
}
|
||||
} elseif ( ! $this->deleteDir($object['path'])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return ftp_rmdir($connection, $dirname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function createDir($dirname, Config $config)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$directories = explode('/', $dirname);
|
||||
|
||||
foreach ($directories as $directory) {
|
||||
if (false === $this->createActualDirectory($directory, $connection)) {
|
||||
$this->setConnectionRoot();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ftp_chdir($connection, $directory);
|
||||
}
|
||||
|
||||
$this->setConnectionRoot();
|
||||
|
||||
return ['type' => 'dir', 'path' => $dirname];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param resource $connection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function createActualDirectory($directory, $connection)
|
||||
{
|
||||
// List the current directory
|
||||
$listing = ftp_nlist($connection, '.') ?: [];
|
||||
|
||||
foreach ($listing as $key => $item) {
|
||||
if (preg_match('~^\./.*~', $item)) {
|
||||
$listing[$key] = substr($item, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($directory, $listing, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (boolean) ftp_mkdir($connection, $directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
if ($path === '') {
|
||||
return ['type' => 'dir', 'path' => ''];
|
||||
}
|
||||
|
||||
if (@ftp_chdir($this->getConnection(), $path) === true) {
|
||||
$this->setConnectionRoot();
|
||||
|
||||
return ['type' => 'dir', 'path' => $path];
|
||||
}
|
||||
|
||||
$listing = $this->ftpRawlist('-A', str_replace('*', '\\*', $path));
|
||||
|
||||
if (empty($listing) || in_array('total 0', $listing, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/.* not found/', $listing[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/^total [0-9]*$/', $listing[0])) {
|
||||
array_shift($listing);
|
||||
}
|
||||
|
||||
return $this->normalizeObject($listing[0], '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
if ( ! $metadata = $this->getMetadata($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$metadata['mimetype'] = MimeType::detectByFilename($path);
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
$timestamp = ftp_mdtm($this->getConnection(), $path);
|
||||
|
||||
return ($timestamp !== -1) ? ['path' => $path, 'timestamp' => $timestamp] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
if ( ! $object = $this->readStream($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$object['contents'] = stream_get_contents($object['stream']);
|
||||
fclose($object['stream']);
|
||||
unset($object['stream']);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
$stream = fopen('php://temp', 'w+b');
|
||||
$result = ftp_fget($this->getConnection(), $stream, $path, $this->transferMode);
|
||||
rewind($stream);
|
||||
|
||||
if ( ! $result) {
|
||||
fclose($stream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return ['type' => 'file', 'path' => $path, 'stream' => $stream];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setVisibility($path, $visibility)
|
||||
{
|
||||
$mode = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? $this->getPermPublic() : $this->getPermPrivate();
|
||||
|
||||
if ( ! ftp_chmod($this->getConnection(), $mode, $path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return compact('path', 'visibility');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param string $directory
|
||||
*/
|
||||
protected function listDirectoryContents($directory, $recursive = true)
|
||||
{
|
||||
$directory = str_replace('*', '\\*', $directory);
|
||||
|
||||
if ($recursive && $this->recurseManually) {
|
||||
return $this->listDirectoryContentsRecursive($directory);
|
||||
}
|
||||
|
||||
$options = $recursive ? '-alnR' : '-aln';
|
||||
$listing = $this->ftpRawlist($options, $directory);
|
||||
|
||||
return $listing ? $this->normalizeListing($listing, $directory) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param string $directory
|
||||
*/
|
||||
protected function listDirectoryContentsRecursive($directory)
|
||||
{
|
||||
$listing = $this->normalizeListing($this->ftpRawlist('-aln', $directory) ?: [], $directory);
|
||||
$output = [];
|
||||
|
||||
foreach ($listing as $item) {
|
||||
$output[] = $item;
|
||||
if ($item['type'] !== 'dir') {
|
||||
continue;
|
||||
}
|
||||
$output = array_merge($output, $this->listDirectoryContentsRecursive($item['path']));
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the connection is open.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws ErrorException
|
||||
*/
|
||||
public function isConnected()
|
||||
{
|
||||
try {
|
||||
return is_resource($this->connection) && ftp_rawlist($this->connection, $this->getRoot()) !== false;
|
||||
} catch (ErrorException $e) {
|
||||
if (strpos($e->getMessage(), 'ftp_rawlist') === false) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function isPureFtpdServer()
|
||||
{
|
||||
$response = ftp_raw($this->connection, 'HELP');
|
||||
|
||||
return stripos(implode(' ', $response), 'Pure-FTPd') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ftp_rawlist function with optional escaping.
|
||||
*
|
||||
* @param string $options
|
||||
* @param string $path
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function ftpRawlist($options, $path)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
|
||||
if ($this->isPureFtpd) {
|
||||
$path = str_replace(' ', '\ ', $path);
|
||||
}
|
||||
|
||||
return ftp_rawlist($connection, $options . ' ' . $path);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
class Ftpd extends Ftp
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
if ($path === '') {
|
||||
return ['type' => 'dir', 'path' => ''];
|
||||
}
|
||||
if (@ftp_chdir($this->getConnection(), $path) === true) {
|
||||
$this->setConnectionRoot();
|
||||
|
||||
return ['type' => 'dir', 'path' => $path];
|
||||
}
|
||||
|
||||
if ( ! ($object = ftp_raw($this->getConnection(), 'STAT ' . $path)) || count($object) < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (substr($object[1], 0, 5) === "ftpd:") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->normalizeObject($object[1], '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function listDirectoryContents($directory, $recursive = true)
|
||||
{
|
||||
$listing = ftp_rawlist($this->getConnection(), $directory, $recursive);
|
||||
|
||||
if ($listing === false || ( ! empty($listing) && substr($listing[0], 0, 5) === "ftpd:")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->normalizeListing($listing, $directory);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,528 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
use DirectoryIterator;
|
||||
use FilesystemIterator;
|
||||
use finfo as Finfo;
|
||||
use League\Flysystem\Config;
|
||||
use League\Flysystem\Exception;
|
||||
use League\Flysystem\NotSupportedException;
|
||||
use League\Flysystem\UnreadableFileException;
|
||||
use League\Flysystem\Util;
|
||||
use LogicException;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use SplFileInfo;
|
||||
|
||||
class Local extends AbstractAdapter
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
const SKIP_LINKS = 0001;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
const DISALLOW_LINKS = 0002;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $permissions = [
|
||||
'file' => [
|
||||
'public' => 0644,
|
||||
'private' => 0600,
|
||||
],
|
||||
'dir' => [
|
||||
'public' => 0755,
|
||||
'private' => 0700,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $pathSeparator = DIRECTORY_SEPARATOR;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $permissionMap;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $writeFlags;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $linkHandling;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $root
|
||||
* @param int $writeFlags
|
||||
* @param int $linkHandling
|
||||
* @param array $permissions
|
||||
*
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function __construct($root, $writeFlags = LOCK_EX, $linkHandling = self::DISALLOW_LINKS, array $permissions = [])
|
||||
{
|
||||
$root = is_link($root) ? realpath($root) : $root;
|
||||
$this->permissionMap = array_replace_recursive(static::$permissions, $permissions);
|
||||
$this->ensureDirectory($root);
|
||||
|
||||
if ( ! is_dir($root) || ! is_readable($root)) {
|
||||
throw new LogicException('The root path ' . $root . ' is not readable.');
|
||||
}
|
||||
|
||||
$this->setPathPrefix($root);
|
||||
$this->writeFlags = $writeFlags;
|
||||
$this->linkHandling = $linkHandling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the root directory exists.
|
||||
*
|
||||
* @param string $root root directory path
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws Exception in case the root directory can not be created
|
||||
*/
|
||||
protected function ensureDirectory($root)
|
||||
{
|
||||
if ( ! is_dir($root)) {
|
||||
$umask = umask(0);
|
||||
|
||||
if ( ! @mkdir($root, $this->permissionMap['dir']['public'], true)) {
|
||||
$mkdirError = error_get_last();
|
||||
}
|
||||
|
||||
umask($umask);
|
||||
clearstatcache(false, $root);
|
||||
|
||||
if ( ! is_dir($root)) {
|
||||
$errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : '';
|
||||
throw new Exception(sprintf('Impossible to create the root directory "%s". %s', $root, $errorMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
|
||||
return file_exists($location);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function write($path, $contents, Config $config)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$this->ensureDirectory(dirname($location));
|
||||
|
||||
if (($size = file_put_contents($location, $contents, $this->writeFlags)) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = 'file';
|
||||
$result = compact('contents', 'type', 'size', 'path');
|
||||
|
||||
if ($visibility = $config->get('visibility')) {
|
||||
$result['visibility'] = $visibility;
|
||||
$this->setVisibility($path, $visibility);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function writeStream($path, $resource, Config $config)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$this->ensureDirectory(dirname($location));
|
||||
$stream = fopen($location, 'w+b');
|
||||
|
||||
if ( ! $stream || stream_copy_to_stream($resource, $stream) === false || ! fclose($stream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = 'file';
|
||||
$result = compact('type', 'path');
|
||||
|
||||
if ($visibility = $config->get('visibility')) {
|
||||
$this->setVisibility($path, $visibility);
|
||||
$result['visibility'] = $visibility;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$stream = fopen($location, 'rb');
|
||||
|
||||
return ['type' => 'file', 'path' => $path, 'stream' => $stream];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function updateStream($path, $resource, Config $config)
|
||||
{
|
||||
return $this->writeStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function update($path, $contents, Config $config)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$size = file_put_contents($location, $contents, $this->writeFlags);
|
||||
|
||||
if ($size === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = 'file';
|
||||
|
||||
$result = compact('type', 'path', 'size', 'contents');
|
||||
|
||||
if ($mimetype = $config->get('mimetype') ?: Util::guessMimeType($path, $contents)) {
|
||||
$result['mimetype'] = $mimetype;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$contents = @file_get_contents($location);
|
||||
|
||||
if ($contents === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ['type' => 'file', 'path' => $path, 'contents' => $contents];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$destination = $this->applyPathPrefix($newpath);
|
||||
$parentDirectory = $this->applyPathPrefix(Util::dirname($newpath));
|
||||
$this->ensureDirectory($parentDirectory);
|
||||
|
||||
return rename($location, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function copy($path, $newpath)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$destination = $this->applyPathPrefix($newpath);
|
||||
$this->ensureDirectory(dirname($destination));
|
||||
|
||||
return copy($location, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
|
||||
return @unlink($location);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false)
|
||||
{
|
||||
$result = [];
|
||||
$location = $this->applyPathPrefix($directory);
|
||||
|
||||
if ( ! is_dir($location)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$iterator = $recursive ? $this->getRecursiveDirectoryIterator($location) : $this->getDirectoryIterator($location);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
$path = $this->getFilePath($file);
|
||||
|
||||
if (preg_match('#(^|/|\\\\)\.{1,2}$#', $path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = $this->normalizeFileInfo($file);
|
||||
}
|
||||
|
||||
return array_filter($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
clearstatcache(false, $location);
|
||||
$info = new SplFileInfo($location);
|
||||
|
||||
return $this->normalizeFileInfo($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
return $this->getMetadata($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$finfo = new Finfo(FILEINFO_MIME_TYPE);
|
||||
$mimetype = $finfo->file($location);
|
||||
|
||||
if (in_array($mimetype, ['application/octet-stream', 'inode/x-empty', 'application/x-empty'])) {
|
||||
$mimetype = Util\MimeType::detectByFilename($location);
|
||||
}
|
||||
|
||||
return ['path' => $path, 'type' => 'file', 'mimetype' => $mimetype];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
return $this->getMetadata($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getVisibility($path)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
clearstatcache(false, $location);
|
||||
$permissions = octdec(substr(sprintf('%o', fileperms($location)), -4));
|
||||
$type = is_dir($location) ? 'dir' : 'file';
|
||||
|
||||
foreach ($this->permissionMap[$type] as $visibility => $visibilityPermissions) {
|
||||
if ($visibilityPermissions == $permissions) {
|
||||
return compact('path', 'visibility');
|
||||
}
|
||||
}
|
||||
|
||||
$visibility = substr(sprintf('%o', fileperms($location)), -4);
|
||||
|
||||
return compact('path', 'visibility');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setVisibility($path, $visibility)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
$type = is_dir($location) ? 'dir' : 'file';
|
||||
$success = chmod($location, $this->permissionMap[$type][$visibility]);
|
||||
|
||||
if ($success === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return compact('path', 'visibility');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function createDir($dirname, Config $config)
|
||||
{
|
||||
$location = $this->applyPathPrefix($dirname);
|
||||
$umask = umask(0);
|
||||
$visibility = $config->get('visibility', 'public');
|
||||
$return = ['path' => $dirname, 'type' => 'dir'];
|
||||
|
||||
if ( ! is_dir($location)) {
|
||||
if (false === @mkdir($location, $this->permissionMap['dir'][$visibility], true)
|
||||
|| false === is_dir($location)) {
|
||||
$return = false;
|
||||
}
|
||||
}
|
||||
|
||||
umask($umask);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function deleteDir($dirname)
|
||||
{
|
||||
$location = $this->applyPathPrefix($dirname);
|
||||
|
||||
if ( ! is_dir($location)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contents = $this->getRecursiveDirectoryIterator($location, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
|
||||
/** @var SplFileInfo $file */
|
||||
foreach ($contents as $file) {
|
||||
$this->guardAgainstUnreadableFileInfo($file);
|
||||
$this->deleteFileInfoObject($file);
|
||||
}
|
||||
|
||||
return rmdir($location);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplFileInfo $file
|
||||
*/
|
||||
protected function deleteFileInfoObject(SplFileInfo $file)
|
||||
{
|
||||
switch ($file->getType()) {
|
||||
case 'dir':
|
||||
rmdir($file->getRealPath());
|
||||
break;
|
||||
case 'link':
|
||||
unlink($file->getPathname());
|
||||
break;
|
||||
default:
|
||||
unlink($file->getRealPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the file info.
|
||||
*
|
||||
* @param SplFileInfo $file
|
||||
*
|
||||
* @return array|void
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
protected function normalizeFileInfo(SplFileInfo $file)
|
||||
{
|
||||
if ( ! $file->isLink()) {
|
||||
return $this->mapFileInfo($file);
|
||||
}
|
||||
|
||||
if ($this->linkHandling & self::DISALLOW_LINKS) {
|
||||
throw NotSupportedException::forLink($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the normalized path from a SplFileInfo object.
|
||||
*
|
||||
* @param SplFileInfo $file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFilePath(SplFileInfo $file)
|
||||
{
|
||||
$location = $file->getPathname();
|
||||
$path = $this->removePathPrefix($location);
|
||||
|
||||
return trim(str_replace('\\', '/', $path), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param int $mode
|
||||
*
|
||||
* @return RecursiveIteratorIterator
|
||||
*/
|
||||
protected function getRecursiveDirectoryIterator($path, $mode = RecursiveIteratorIterator::SELF_FIRST)
|
||||
{
|
||||
return new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
|
||||
$mode
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return DirectoryIterator
|
||||
*/
|
||||
protected function getDirectoryIterator($path)
|
||||
{
|
||||
$iterator = new DirectoryIterator($path);
|
||||
|
||||
return $iterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplFileInfo $file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function mapFileInfo(SplFileInfo $file)
|
||||
{
|
||||
$normalized = [
|
||||
'type' => $file->getType(),
|
||||
'path' => $this->getFilePath($file),
|
||||
];
|
||||
|
||||
$normalized['timestamp'] = $file->getMTime();
|
||||
|
||||
if ($normalized['type'] === 'file') {
|
||||
$normalized['size'] = $file->getSize();
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplFileInfo $file
|
||||
*
|
||||
* @throws UnreadableFileException
|
||||
*/
|
||||
protected function guardAgainstUnreadableFileInfo(SplFileInfo $file)
|
||||
{
|
||||
if ( ! $file->isReadable()) {
|
||||
throw UnreadableFileException::forFileInfo($file);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
|
||||
use League\Flysystem\Adapter\Polyfill\StreamedTrait;
|
||||
use League\Flysystem\Config;
|
||||
|
||||
class NullAdapter extends AbstractAdapter
|
||||
{
|
||||
use StreamedTrait;
|
||||
use StreamedCopyTrait;
|
||||
|
||||
/**
|
||||
* Check whether a file is present.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function write($path, $contents, Config $config)
|
||||
{
|
||||
$type = 'file';
|
||||
$result = compact('contents', 'type', 'path');
|
||||
|
||||
if ($visibility = $config->get('visibility')) {
|
||||
$result['visibility'] = $visibility;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function update($path, $contents, Config $config)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function delete($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setVisibility($path, $visibility)
|
||||
{
|
||||
return compact('visibility');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function createDir($dirname, Config $config)
|
||||
{
|
||||
return ['path' => $dirname, 'type' => 'dir'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function deleteDir($dirname)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
33
vendor/league/flysystem/src/Adapter/Polyfill/NotSupportingVisibilityTrait.php
vendored
Normal file
33
vendor/league/flysystem/src/Adapter/Polyfill/NotSupportingVisibilityTrait.php
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter\Polyfill;
|
||||
|
||||
use LogicException;
|
||||
|
||||
trait NotSupportingVisibilityTrait
|
||||
{
|
||||
/**
|
||||
* Get the visibility of a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function getVisibility($path)
|
||||
{
|
||||
throw new LogicException(get_class($this) . ' does not support visibility. Path: ' . $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visibility for a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $visibility
|
||||
*
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function setVisibility($path, $visibility)
|
||||
{
|
||||
throw new LogicException(get_class($this) . ' does not support visibility. Path: ' . $path . ', visibility: ' . $visibility);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter\Polyfill;
|
||||
|
||||
use League\Flysystem\Config;
|
||||
|
||||
trait StreamedCopyTrait
|
||||
{
|
||||
/**
|
||||
* Copy a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($path, $newpath)
|
||||
{
|
||||
$response = $this->readStream($path);
|
||||
|
||||
if ($response === false || ! is_resource($response['stream'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->writeStream($newpath, $response['stream'], new Config());
|
||||
|
||||
if ($result !== false && is_resource($response['stream'])) {
|
||||
fclose($response['stream']);
|
||||
}
|
||||
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
// Required abstract method
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
abstract public function readStream($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param Config $config
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
abstract public function writeStream($path, $resource, Config $config);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter\Polyfill;
|
||||
|
||||
/**
|
||||
* A helper for adapters that only handle strings to provide read streams.
|
||||
*/
|
||||
trait StreamedReadingTrait
|
||||
{
|
||||
/**
|
||||
* Reads a file as a stream.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*
|
||||
* @see League\Flysystem\ReadInterface::readStream()
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
if ( ! $data = $this->read($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$stream = fopen('php://temp', 'w+b');
|
||||
fwrite($stream, $data['contents']);
|
||||
rewind($stream);
|
||||
$data['stream'] = $stream;
|
||||
unset($data['contents']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*
|
||||
* @see League\Flysystem\ReadInterface::read()
|
||||
*/
|
||||
abstract public function read($path);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter\Polyfill;
|
||||
|
||||
trait StreamedTrait
|
||||
{
|
||||
use StreamedReadingTrait;
|
||||
use StreamedWritingTrait;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter\Polyfill;
|
||||
|
||||
use League\Flysystem\Config;
|
||||
use League\Flysystem\Util;
|
||||
|
||||
trait StreamedWritingTrait
|
||||
{
|
||||
/**
|
||||
* Stream fallback delegator.
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param Config $config
|
||||
* @param string $fallback
|
||||
*
|
||||
* @return mixed fallback result
|
||||
*/
|
||||
protected function stream($path, $resource, Config $config, $fallback)
|
||||
{
|
||||
Util::rewindStream($resource);
|
||||
$contents = stream_get_contents($resource);
|
||||
$fallbackCall = [$this, $fallback];
|
||||
|
||||
return call_user_func($fallbackCall, $path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write using a stream.
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param Config $config
|
||||
*
|
||||
* @return mixed false or file metadata
|
||||
*/
|
||||
public function writeStream($path, $resource, Config $config)
|
||||
{
|
||||
return $this->stream($path, $resource, $config, 'write');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a file using a stream.
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param Config $config Config object or visibility setting
|
||||
*
|
||||
* @return mixed false of file metadata
|
||||
*/
|
||||
public function updateStream($path, $resource, Config $config)
|
||||
{
|
||||
return $this->stream($path, $resource, $config, 'update');
|
||||
}
|
||||
|
||||
// Required abstract methods
|
||||
abstract public function write($pash, $contents, Config $config);
|
||||
abstract public function update($pash, $contents, Config $config);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
class SynologyFtp extends Ftpd
|
||||
{
|
||||
// This class merely exists because of BC.
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
interface AdapterInterface extends ReadInterface
|
||||
{
|
||||
/**
|
||||
* @const VISIBILITY_PUBLIC public visibility
|
||||
*/
|
||||
const VISIBILITY_PUBLIC = 'public';
|
||||
|
||||
/**
|
||||
* @const VISIBILITY_PRIVATE private visibility
|
||||
*/
|
||||
const VISIBILITY_PRIVATE = 'private';
|
||||
|
||||
/**
|
||||
* Write a new file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param Config $config Config object
|
||||
*
|
||||
* @return array|false false on failure file meta data on success
|
||||
*/
|
||||
public function write($path, $contents, Config $config);
|
||||
|
||||
/**
|
||||
* Write a new file using a stream.
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param Config $config Config object
|
||||
*
|
||||
* @return array|false false on failure file meta data on success
|
||||
*/
|
||||
public function writeStream($path, $resource, Config $config);
|
||||
|
||||
/**
|
||||
* Update a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param Config $config Config object
|
||||
*
|
||||
* @return array|false false on failure file meta data on success
|
||||
*/
|
||||
public function update($path, $contents, Config $config);
|
||||
|
||||
/**
|
||||
* Update a file using a stream.
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param Config $config Config object
|
||||
*
|
||||
* @return array|false false on failure file meta data on success
|
||||
*/
|
||||
public function updateStream($path, $resource, Config $config);
|
||||
|
||||
/**
|
||||
* Rename a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rename($path, $newpath);
|
||||
|
||||
/**
|
||||
* Copy a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($path, $newpath);
|
||||
|
||||
/**
|
||||
* Delete a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($path);
|
||||
|
||||
/**
|
||||
* Delete a directory.
|
||||
*
|
||||
* @param string $dirname
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteDir($dirname);
|
||||
|
||||
/**
|
||||
* Create a directory.
|
||||
*
|
||||
* @param string $dirname directory name
|
||||
* @param Config $config
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function createDir($dirname, Config $config);
|
||||
|
||||
/**
|
||||
* Set the visibility for a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $visibility
|
||||
*
|
||||
* @return array|false file meta data
|
||||
*/
|
||||
public function setVisibility($path, $visibility);
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
class Config
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = [];
|
||||
|
||||
/**
|
||||
* @var Config|null
|
||||
*/
|
||||
protected $fallback;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $settings
|
||||
*/
|
||||
public function __construct(array $settings = [])
|
||||
{
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a setting.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed config setting or default when not found
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if ( ! array_key_exists($key, $this->settings)) {
|
||||
return $this->getDefault($key, $default);
|
||||
}
|
||||
|
||||
return $this->settings[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an item exists by key.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
if (array_key_exists($key, $this->settings)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->fallback instanceof Config
|
||||
? $this->fallback->has($key)
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to retrieve a default setting from a config fallback.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed config setting or default when not found
|
||||
*/
|
||||
protected function getDefault($key, $default)
|
||||
{
|
||||
if ( ! $this->fallback) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->fallback->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a setting.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->settings[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fallback.
|
||||
*
|
||||
* @param Config $fallback
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFallback(Config $fallback)
|
||||
{
|
||||
$this->fallback = $fallback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait ConfigAwareTrait
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Set the config.
|
||||
*
|
||||
* @param Config|array|null $config
|
||||
*/
|
||||
protected function setConfig($config)
|
||||
{
|
||||
$this->config = $config ? Util::ensureConfig($config) : new Config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Config.
|
||||
*
|
||||
* @return Config config object
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a config array to a Config object with the correct fallback.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
protected function prepareConfig(array $config)
|
||||
{
|
||||
$config = new Config($config);
|
||||
$config->setFallback($this->getConfig());
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class Directory extends Handler
|
||||
{
|
||||
/**
|
||||
* Delete the directory.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
return $this->filesystem->deleteDir($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the directory contents.
|
||||
*
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|bool directory contents or false
|
||||
*/
|
||||
public function getContents($recursive = false)
|
||||
{
|
||||
return $this->filesystem->listContents($this->path, $recursive);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class File extends Handler
|
||||
{
|
||||
/**
|
||||
* Check whether the file exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
return $this->filesystem->has($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the file.
|
||||
*
|
||||
* @return string|false file contents
|
||||
*/
|
||||
public function read()
|
||||
{
|
||||
return $this->filesystem->read($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the file as a stream.
|
||||
*
|
||||
* @return resource|false file stream
|
||||
*/
|
||||
public function readStream()
|
||||
{
|
||||
return $this->filesystem->readStream($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the new file.
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return bool success boolean
|
||||
*/
|
||||
public function write($content)
|
||||
{
|
||||
return $this->filesystem->write($this->path, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the new file using a stream.
|
||||
*
|
||||
* @param resource $resource
|
||||
*
|
||||
* @return bool success boolean
|
||||
*/
|
||||
public function writeStream($resource)
|
||||
{
|
||||
return $this->filesystem->writeStream($this->path, $resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the file contents.
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return bool success boolean
|
||||
*/
|
||||
public function update($content)
|
||||
{
|
||||
return $this->filesystem->update($this->path, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the file contents with a stream.
|
||||
*
|
||||
* @param resource $resource
|
||||
*
|
||||
* @return bool success boolean
|
||||
*/
|
||||
public function updateStream($resource)
|
||||
{
|
||||
return $this->filesystem->updateStream($this->path, $resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the file or update if exists.
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return bool success boolean
|
||||
*/
|
||||
public function put($content)
|
||||
{
|
||||
return $this->filesystem->put($this->path, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the file or update if exists using a stream.
|
||||
*
|
||||
* @param resource $resource
|
||||
*
|
||||
* @return bool success boolean
|
||||
*/
|
||||
public function putStream($resource)
|
||||
{
|
||||
return $this->filesystem->putStream($this->path, $resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the file.
|
||||
*
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return bool success boolean
|
||||
*/
|
||||
public function rename($newpath)
|
||||
{
|
||||
if ($this->filesystem->rename($this->path, $newpath)) {
|
||||
$this->path = $newpath;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the file.
|
||||
*
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return File|false new file or false
|
||||
*/
|
||||
public function copy($newpath)
|
||||
{
|
||||
if ($this->filesystem->copy($this->path, $newpath)) {
|
||||
return new File($this->filesystem, $newpath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file's timestamp.
|
||||
*
|
||||
* @return string|false The timestamp or false on failure.
|
||||
*/
|
||||
public function getTimestamp()
|
||||
{
|
||||
return $this->filesystem->getTimestamp($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file's mimetype.
|
||||
*
|
||||
* @return string|false The file mime-type or false on failure.
|
||||
*/
|
||||
public function getMimetype()
|
||||
{
|
||||
return $this->filesystem->getMimetype($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file's visibility.
|
||||
*
|
||||
* @return string|false The visibility (public|private) or false on failure.
|
||||
*/
|
||||
public function getVisibility()
|
||||
{
|
||||
return $this->filesystem->getVisibility($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file's metadata.
|
||||
*
|
||||
* @return array|false The file metadata or false on failure.
|
||||
*/
|
||||
public function getMetadata()
|
||||
{
|
||||
return $this->filesystem->getMetadata($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file size.
|
||||
*
|
||||
* @return int|false The file size or false on failure.
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return $this->filesystem->getSize($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the file.
|
||||
*
|
||||
* @return bool success boolean
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
return $this->filesystem->delete($this->path);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use Exception as BaseException;
|
||||
|
||||
class FileExistsException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $code
|
||||
* @param BaseException $previous
|
||||
*/
|
||||
public function __construct($path, $code = 0, BaseException $previous = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
parent::__construct('File already exists at path: ' . $this->getPath(), $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path which was found.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use Exception as BaseException;
|
||||
|
||||
class FileNotFoundException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $code
|
||||
* @param \Exception $previous
|
||||
*/
|
||||
public function __construct($path, $code = 0, BaseException $previous = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
parent::__construct('File not found at path: ' . $this->getPath(), $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path which was not found.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,408 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use League\Flysystem\Adapter\CanOverwriteFiles;
|
||||
use League\Flysystem\Plugin\PluggableTrait;
|
||||
use League\Flysystem\Util\ContentListingFormatter;
|
||||
|
||||
/**
|
||||
* @method array getWithMetadata(string $path, array $metadata)
|
||||
* @method bool forceCopy(string $path, string $newpath)
|
||||
* @method bool forceRename(string $path, string $newpath)
|
||||
* @method array listFiles(string $path = '', boolean $recursive = false)
|
||||
* @method array listPaths(string $path = '', boolean $recursive = false)
|
||||
* @method array listWith(array $keys = [], $directory = '', $recursive = false)
|
||||
*/
|
||||
class Filesystem implements FilesystemInterface
|
||||
{
|
||||
use PluggableTrait;
|
||||
use ConfigAwareTrait;
|
||||
|
||||
/**
|
||||
* @var AdapterInterface
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param AdapterInterface $adapter
|
||||
* @param Config|array $config
|
||||
*/
|
||||
public function __construct(AdapterInterface $adapter, $config = null)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->setConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Adapter.
|
||||
*
|
||||
* @return AdapterInterface adapter
|
||||
*/
|
||||
public function getAdapter()
|
||||
{
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
|
||||
return strlen($path) === 0 ? false : (bool) $this->getAdapter()->has($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function write($path, $contents, array $config = [])
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertAbsent($path);
|
||||
$config = $this->prepareConfig($config);
|
||||
|
||||
return (bool) $this->getAdapter()->write($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function writeStream($path, $resource, array $config = [])
|
||||
{
|
||||
if ( ! is_resource($resource)) {
|
||||
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
|
||||
}
|
||||
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertAbsent($path);
|
||||
$config = $this->prepareConfig($config);
|
||||
|
||||
Util::rewindStream($resource);
|
||||
|
||||
return (bool) $this->getAdapter()->writeStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function put($path, $contents, array $config = [])
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$config = $this->prepareConfig($config);
|
||||
|
||||
if ( ! $this->getAdapter() instanceof CanOverwriteFiles && $this->has($path)) {
|
||||
return (bool) $this->getAdapter()->update($path, $contents, $config);
|
||||
}
|
||||
|
||||
return (bool) $this->getAdapter()->write($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function putStream($path, $resource, array $config = [])
|
||||
{
|
||||
if ( ! is_resource($resource)) {
|
||||
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
|
||||
}
|
||||
|
||||
$path = Util::normalizePath($path);
|
||||
$config = $this->prepareConfig($config);
|
||||
Util::rewindStream($resource);
|
||||
|
||||
if ( ! $this->getAdapter() instanceof CanOverwriteFiles && $this->has($path)) {
|
||||
return (bool) $this->getAdapter()->updateStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
return (bool) $this->getAdapter()->writeStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function readAndDelete($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
$contents = $this->read($path);
|
||||
|
||||
if ($contents === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->delete($path);
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function update($path, $contents, array $config = [])
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$config = $this->prepareConfig($config);
|
||||
|
||||
$this->assertPresent($path);
|
||||
|
||||
return (bool) $this->getAdapter()->update($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function updateStream($path, $resource, array $config = [])
|
||||
{
|
||||
if ( ! is_resource($resource)) {
|
||||
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
|
||||
}
|
||||
|
||||
$path = Util::normalizePath($path);
|
||||
$config = $this->prepareConfig($config);
|
||||
$this->assertPresent($path);
|
||||
Util::rewindStream($resource);
|
||||
|
||||
return (bool) $this->getAdapter()->updateStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
if ( ! ($object = $this->getAdapter()->read($path))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $object['contents'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
if ( ! $object = $this->getAdapter()->readStream($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $object['stream'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$newpath = Util::normalizePath($newpath);
|
||||
$this->assertPresent($path);
|
||||
$this->assertAbsent($newpath);
|
||||
|
||||
return (bool) $this->getAdapter()->rename($path, $newpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function copy($path, $newpath)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$newpath = Util::normalizePath($newpath);
|
||||
$this->assertPresent($path);
|
||||
$this->assertAbsent($newpath);
|
||||
|
||||
return $this->getAdapter()->copy($path, $newpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
return $this->getAdapter()->delete($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function deleteDir($dirname)
|
||||
{
|
||||
$dirname = Util::normalizePath($dirname);
|
||||
|
||||
if ($dirname === '') {
|
||||
throw new RootViolationException('Root directories can not be deleted.');
|
||||
}
|
||||
|
||||
return (bool) $this->getAdapter()->deleteDir($dirname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function createDir($dirname, array $config = [])
|
||||
{
|
||||
$dirname = Util::normalizePath($dirname);
|
||||
$config = $this->prepareConfig($config);
|
||||
|
||||
return (bool) $this->getAdapter()->createDir($dirname, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false)
|
||||
{
|
||||
$directory = Util::normalizePath($directory);
|
||||
$contents = $this->getAdapter()->listContents($directory, $recursive);
|
||||
|
||||
return (new ContentListingFormatter($directory, $recursive, $this->config->get('case_sensitive', true)))
|
||||
->formatListing($contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
if (( ! $object = $this->getAdapter()->getMimetype($path)) || ! array_key_exists('mimetype', $object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $object['mimetype'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
if (( ! $object = $this->getAdapter()->getTimestamp($path)) || ! array_key_exists('timestamp', $object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $object['timestamp'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getVisibility($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
if (( ! $object = $this->getAdapter()->getVisibility($path)) || ! array_key_exists('visibility', $object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $object['visibility'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
if (( ! $object = $this->getAdapter()->getSize($path)) || ! array_key_exists('size', $object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int) $object['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setVisibility($path, $visibility)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
return (bool) $this->getAdapter()->setVisibility($path, $visibility);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
$this->assertPresent($path);
|
||||
|
||||
return $this->getAdapter()->getMetadata($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function get($path, Handler $handler = null)
|
||||
{
|
||||
$path = Util::normalizePath($path);
|
||||
|
||||
if ( ! $handler) {
|
||||
$metadata = $this->getMetadata($path);
|
||||
$handler = $metadata['type'] === 'file' ? new File($this, $path) : new Directory($this, $path);
|
||||
}
|
||||
|
||||
$handler->setPath($path);
|
||||
$handler->setFilesystem($this);
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a file is present.
|
||||
*
|
||||
* @param string $path path to file
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function assertPresent($path)
|
||||
{
|
||||
if ($this->config->get('disable_asserts', false) === false && ! $this->has($path)) {
|
||||
throw new FileNotFoundException($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a file is absent.
|
||||
*
|
||||
* @param string $path path to file
|
||||
*
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function assertAbsent($path)
|
||||
{
|
||||
if ($this->config->get('disable_asserts', false) === false && $this->has($path)) {
|
||||
throw new FileExistsException($path);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
interface FilesystemInterface
|
||||
{
|
||||
/**
|
||||
* Check whether a file exists.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($path);
|
||||
|
||||
/**
|
||||
* Read a file.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The file contents or false on failure.
|
||||
*/
|
||||
public function read($path);
|
||||
|
||||
/**
|
||||
* Retrieves a read-stream for a path.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return resource|false The path resource or false on failure.
|
||||
*/
|
||||
public function readStream($path);
|
||||
|
||||
/**
|
||||
* List contents of a directory.
|
||||
*
|
||||
* @param string $directory The directory to list.
|
||||
* @param bool $recursive Whether to list recursively.
|
||||
*
|
||||
* @return array A list of file metadata.
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false);
|
||||
|
||||
/**
|
||||
* Get a file's metadata.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return array|false The file metadata or false on failure.
|
||||
*/
|
||||
public function getMetadata($path);
|
||||
|
||||
/**
|
||||
* Get a file's size.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return int|false The file size or false on failure.
|
||||
*/
|
||||
public function getSize($path);
|
||||
|
||||
/**
|
||||
* Get a file's mime-type.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The file mime-type or false on failure.
|
||||
*/
|
||||
public function getMimetype($path);
|
||||
|
||||
/**
|
||||
* Get a file's timestamp.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The timestamp or false on failure.
|
||||
*/
|
||||
public function getTimestamp($path);
|
||||
|
||||
/**
|
||||
* Get a file's visibility.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The visibility (public|private) or false on failure.
|
||||
*/
|
||||
public function getVisibility($path);
|
||||
|
||||
/**
|
||||
* Write a new file.
|
||||
*
|
||||
* @param string $path The path of the new file.
|
||||
* @param string $contents The file contents.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function write($path, $contents, array $config = []);
|
||||
|
||||
/**
|
||||
* Write a new file using a stream.
|
||||
*
|
||||
* @param string $path The path of the new file.
|
||||
* @param resource $resource The file handle.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws InvalidArgumentException If $resource is not a file handle.
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function writeStream($path, $resource, array $config = []);
|
||||
|
||||
/**
|
||||
* Update an existing file.
|
||||
*
|
||||
* @param string $path The path of the existing file.
|
||||
* @param string $contents The file contents.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function update($path, $contents, array $config = []);
|
||||
|
||||
/**
|
||||
* Update an existing file using a stream.
|
||||
*
|
||||
* @param string $path The path of the existing file.
|
||||
* @param resource $resource The file handle.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws InvalidArgumentException If $resource is not a file handle.
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function updateStream($path, $resource, array $config = []);
|
||||
|
||||
/**
|
||||
* Rename a file.
|
||||
*
|
||||
* @param string $path Path to the existing file.
|
||||
* @param string $newpath The new path of the file.
|
||||
*
|
||||
* @throws FileExistsException Thrown if $newpath exists.
|
||||
* @throws FileNotFoundException Thrown if $path does not exist.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function rename($path, $newpath);
|
||||
|
||||
/**
|
||||
* Copy a file.
|
||||
*
|
||||
* @param string $path Path to the existing file.
|
||||
* @param string $newpath The new path of the file.
|
||||
*
|
||||
* @throws FileExistsException Thrown if $newpath exists.
|
||||
* @throws FileNotFoundException Thrown if $path does not exist.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function copy($path, $newpath);
|
||||
|
||||
/**
|
||||
* Delete a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function delete($path);
|
||||
|
||||
/**
|
||||
* Delete a directory.
|
||||
*
|
||||
* @param string $dirname
|
||||
*
|
||||
* @throws RootViolationException Thrown if $dirname is empty.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function deleteDir($dirname);
|
||||
|
||||
/**
|
||||
* Create a directory.
|
||||
*
|
||||
* @param string $dirname The name of the new directory.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function createDir($dirname, array $config = []);
|
||||
|
||||
/**
|
||||
* Set the visibility for a file.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
* @param string $visibility One of 'public' or 'private'.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function setVisibility($path, $visibility);
|
||||
|
||||
/**
|
||||
* Create a file or update if exists.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
* @param string $contents The file contents.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function put($path, $contents, array $config = []);
|
||||
|
||||
/**
|
||||
* Create a file or update if exists.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
* @param resource $resource The file handle.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws InvalidArgumentException Thrown if $resource is not a resource.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function putStream($path, $resource, array $config = []);
|
||||
|
||||
/**
|
||||
* Read and delete a file.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The file contents, or false on failure.
|
||||
*/
|
||||
public function readAndDelete($path);
|
||||
|
||||
/**
|
||||
* Get a file/directory handler.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
* @param Handler $handler An optional existing handler to populate.
|
||||
*
|
||||
* @return Handler Either a file or directory handler.
|
||||
*/
|
||||
public function get($path, Handler $handler = null);
|
||||
|
||||
/**
|
||||
* Register a plugin.
|
||||
*
|
||||
* @param PluginInterface $plugin The plugin to register.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addPlugin(PluginInterface $plugin);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* Thrown when the MountManager cannot find a filesystem.
|
||||
*/
|
||||
class FilesystemNotFoundException extends LogicException
|
||||
{
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use BadMethodCallException;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
abstract class Handler
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var FilesystemInterface
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param FilesystemInterface $filesystem
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct(FilesystemInterface $filesystem = null, $path = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->filesystem = $filesystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the entree is a directory.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDir()
|
||||
{
|
||||
return $this->getType() === 'dir';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the entree is a file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFile()
|
||||
{
|
||||
return $this->getType() === 'file';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the entree type (file|dir).
|
||||
*
|
||||
* @return string file or dir
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
$metadata = $this->filesystem->getMetadata($this->path);
|
||||
|
||||
return $metadata['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Filesystem object.
|
||||
*
|
||||
* @param FilesystemInterface $filesystem
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFilesystem(FilesystemInterface $filesystem)
|
||||
{
|
||||
$this->filesystem = $filesystem;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Filesystem object.
|
||||
*
|
||||
* @return FilesystemInterface
|
||||
*/
|
||||
public function getFilesystem()
|
||||
{
|
||||
return $this->filesystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entree path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the entree path.
|
||||
*
|
||||
* @return string path
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugins pass-through.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, array $arguments)
|
||||
{
|
||||
array_unshift($arguments, $this->path);
|
||||
$callback = [$this->filesystem, $method];
|
||||
|
||||
try {
|
||||
return call_user_func_array($callback, $arguments);
|
||||
} catch (BadMethodCallException $e) {
|
||||
throw new BadMethodCallException(
|
||||
'Call to undefined method '
|
||||
. get_called_class()
|
||||
. '::' . $method
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,648 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use League\Flysystem\Plugin\PluggableTrait;
|
||||
use League\Flysystem\Plugin\PluginNotFoundException;
|
||||
|
||||
/**
|
||||
* Class MountManager.
|
||||
*
|
||||
* Proxies methods to Filesystem (@see __call):
|
||||
*
|
||||
* @method AdapterInterface getAdapter($prefix)
|
||||
* @method Config getConfig($prefix)
|
||||
* @method array listFiles($directory = '', $recursive = false)
|
||||
* @method array listPaths($directory = '', $recursive = false)
|
||||
* @method array getWithMetadata($path, array $metadata)
|
||||
* @method Filesystem flushCache()
|
||||
* @method void assertPresent($path)
|
||||
* @method void assertAbsent($path)
|
||||
* @method Filesystem addPlugin(PluginInterface $plugin)
|
||||
*/
|
||||
class MountManager implements FilesystemInterface
|
||||
{
|
||||
use PluggableTrait;
|
||||
|
||||
/**
|
||||
* @var FilesystemInterface[]
|
||||
*/
|
||||
protected $filesystems = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param FilesystemInterface[] $filesystems [:prefix => Filesystem,]
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $filesystems = [])
|
||||
{
|
||||
$this->mountFilesystems($filesystems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount filesystems.
|
||||
*
|
||||
* @param FilesystemInterface[] $filesystems [:prefix => Filesystem,]
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function mountFilesystems(array $filesystems)
|
||||
{
|
||||
foreach ($filesystems as $prefix => $filesystem) {
|
||||
$this->mountFilesystem($prefix, $filesystem);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount filesystems.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param FilesystemInterface $filesystem
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function mountFilesystem($prefix, FilesystemInterface $filesystem)
|
||||
{
|
||||
if ( ! is_string($prefix)) {
|
||||
throw new InvalidArgumentException(__METHOD__ . ' expects argument #1 to be a string.');
|
||||
}
|
||||
|
||||
$this->filesystems[$prefix] = $filesystem;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filesystem with the corresponding prefix.
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @throws FilesystemNotFoundException
|
||||
*
|
||||
* @return FilesystemInterface
|
||||
*/
|
||||
public function getFilesystem($prefix)
|
||||
{
|
||||
if ( ! isset($this->filesystems[$prefix])) {
|
||||
throw new FilesystemNotFoundException('No filesystem mounted with prefix ' . $prefix);
|
||||
}
|
||||
|
||||
return $this->filesystems[$prefix];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the prefix from an arguments array.
|
||||
*
|
||||
* @param array $arguments
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return array [:prefix, :arguments]
|
||||
*/
|
||||
public function filterPrefix(array $arguments)
|
||||
{
|
||||
if (empty($arguments)) {
|
||||
throw new InvalidArgumentException('At least one argument needed');
|
||||
}
|
||||
|
||||
$path = array_shift($arguments);
|
||||
|
||||
if ( ! is_string($path)) {
|
||||
throw new InvalidArgumentException('First argument should be a string');
|
||||
}
|
||||
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
array_unshift($arguments, $path);
|
||||
|
||||
return [$prefix, $arguments];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws FilesystemNotFoundException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false)
|
||||
{
|
||||
list($prefix, $directory) = $this->getPrefixAndPath($directory);
|
||||
$filesystem = $this->getFilesystem($prefix);
|
||||
$result = $filesystem->listContents($directory, $recursive);
|
||||
|
||||
foreach ($result as &$file) {
|
||||
$file['filesystem'] = $prefix;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call forwarder.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws FilesystemNotFoundException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
list($prefix, $arguments) = $this->filterPrefix($arguments);
|
||||
|
||||
return $this->invokePluginOnFilesystem($method, $arguments, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param array $config
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws FilesystemNotFoundException
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($from, $to, array $config = [])
|
||||
{
|
||||
list($prefixFrom, $from) = $this->getPrefixAndPath($from);
|
||||
|
||||
$buffer = $this->getFilesystem($prefixFrom)->readStream($from);
|
||||
|
||||
if ($buffer === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list($prefixTo, $to) = $this->getPrefixAndPath($to);
|
||||
|
||||
$result = $this->getFilesystem($prefixTo)->writeStream($to, $buffer, $config);
|
||||
|
||||
if (is_resource($buffer)) {
|
||||
fclose($buffer);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* List with plugin adapter.
|
||||
*
|
||||
* @param array $keys
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws FilesystemNotFoundException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listWith(array $keys = [], $directory = '', $recursive = false)
|
||||
{
|
||||
list($prefix, $directory) = $this->getPrefixAndPath($directory);
|
||||
$arguments = [$keys, $directory, $recursive];
|
||||
|
||||
return $this->invokePluginOnFilesystem('listWith', $arguments, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a file.
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param array $config
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws FilesystemNotFoundException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function move($from, $to, array $config = [])
|
||||
{
|
||||
list($prefixFrom, $pathFrom) = $this->getPrefixAndPath($from);
|
||||
list($prefixTo, $pathTo) = $this->getPrefixAndPath($to);
|
||||
|
||||
if ($prefixFrom === $prefixTo) {
|
||||
$filesystem = $this->getFilesystem($prefixFrom);
|
||||
$renamed = $filesystem->rename($pathFrom, $pathTo);
|
||||
|
||||
if ($renamed && isset($config['visibility'])) {
|
||||
return $filesystem->setVisibility($pathTo, $config['visibility']);
|
||||
}
|
||||
|
||||
return $renamed;
|
||||
}
|
||||
|
||||
$copied = $this->copy($from, $to, $config);
|
||||
|
||||
if ($copied) {
|
||||
return $this->delete($from);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a plugin on a filesystem mounted on a given prefix.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @param string $prefix
|
||||
*
|
||||
* @throws FilesystemNotFoundException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function invokePluginOnFilesystem($method, $arguments, $prefix)
|
||||
{
|
||||
$filesystem = $this->getFilesystem($prefix);
|
||||
|
||||
try {
|
||||
return $this->invokePlugin($method, $arguments, $filesystem);
|
||||
} catch (PluginNotFoundException $e) {
|
||||
// Let it pass, it's ok, don't panic.
|
||||
}
|
||||
|
||||
$callback = [$filesystem, $method];
|
||||
|
||||
return call_user_func_array($callback, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return string[] [:prefix, :path]
|
||||
*/
|
||||
protected function getPrefixAndPath($path)
|
||||
{
|
||||
if (strpos($path, '://') < 1) {
|
||||
throw new InvalidArgumentException('No prefix detected in path: ' . $path);
|
||||
}
|
||||
|
||||
return explode('://', $path, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a file exists.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->has($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The file contents or false on failure.
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->read($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read-stream for a path.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return resource|false The path resource or false on failure.
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->readStream($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file's metadata.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return array|false The file metadata or false on failure.
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->getMetadata($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file's size.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return int|false The file size or false on failure.
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->getSize($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file's mime-type.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The file mime-type or false on failure.
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->getMimetype($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file's timestamp.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The timestamp or false on failure.
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->getTimestamp($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file's visibility.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The visibility (public|private) or false on failure.
|
||||
*/
|
||||
public function getVisibility($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->getVisibility($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a new file.
|
||||
*
|
||||
* @param string $path The path of the new file.
|
||||
* @param string $contents The file contents.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function write($path, $contents, array $config = [])
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->write($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a new file using a stream.
|
||||
*
|
||||
* @param string $path The path of the new file.
|
||||
* @param resource $resource The file handle.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws InvalidArgumentException If $resource is not a file handle.
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function writeStream($path, $resource, array $config = [])
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->writeStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing file.
|
||||
*
|
||||
* @param string $path The path of the existing file.
|
||||
* @param string $contents The file contents.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function update($path, $contents, array $config = [])
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->update($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing file using a stream.
|
||||
*
|
||||
* @param string $path The path of the existing file.
|
||||
* @param resource $resource The file handle.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws InvalidArgumentException If $resource is not a file handle.
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function updateStream($path, $resource, array $config = [])
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->updateStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a file.
|
||||
*
|
||||
* @param string $path Path to the existing file.
|
||||
* @param string $newpath The new path of the file.
|
||||
*
|
||||
* @throws FileExistsException Thrown if $newpath exists.
|
||||
* @throws FileNotFoundException Thrown if $path does not exist.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->rename($path, $newpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->delete($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a directory.
|
||||
*
|
||||
* @param string $dirname
|
||||
*
|
||||
* @throws RootViolationException Thrown if $dirname is empty.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function deleteDir($dirname)
|
||||
{
|
||||
list($prefix, $dirname) = $this->getPrefixAndPath($dirname);
|
||||
|
||||
return $this->getFilesystem($prefix)->deleteDir($dirname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory.
|
||||
*
|
||||
* @param string $dirname The name of the new directory.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function createDir($dirname, array $config = [])
|
||||
{
|
||||
list($prefix, $dirname) = $this->getPrefixAndPath($dirname);
|
||||
|
||||
return $this->getFilesystem($prefix)->createDir($dirname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visibility for a file.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
* @param string $visibility One of 'public' or 'private'.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function setVisibility($path, $visibility)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->setVisibility($path, $visibility);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file or update if exists.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
* @param string $contents The file contents.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function put($path, $contents, array $config = [])
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->put($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file or update if exists.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
* @param resource $resource The file handle.
|
||||
* @param array $config An optional configuration array.
|
||||
*
|
||||
* @throws InvalidArgumentException Thrown if $resource is not a resource.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function putStream($path, $resource, array $config = [])
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->putStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and delete a file.
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false The file contents, or false on failure.
|
||||
*/
|
||||
public function readAndDelete($path)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->readAndDelete($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file/directory handler.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @param string $path The path to the file.
|
||||
* @param Handler $handler An optional existing handler to populate.
|
||||
*
|
||||
* @return Handler Either a file or directory handler.
|
||||
*/
|
||||
public function get($path, Handler $handler = null)
|
||||
{
|
||||
list($prefix, $path) = $this->getPrefixAndPath($path);
|
||||
|
||||
return $this->getFilesystem($prefix)->get($path);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use RuntimeException;
|
||||
use SplFileInfo;
|
||||
|
||||
class NotSupportedException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Create a new exception for a link.
|
||||
*
|
||||
* @param SplFileInfo $file
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function forLink(SplFileInfo $file)
|
||||
{
|
||||
$message = 'Links are not supported, encountered link at ';
|
||||
|
||||
return new static($message . $file->getPathname());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new exception for a link.
|
||||
*
|
||||
* @param string $systemType
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function forFtpSystemType($systemType)
|
||||
{
|
||||
$message = "The FTP system type '$systemType' is currently not supported.";
|
||||
|
||||
return new static($message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
use League\Flysystem\FilesystemInterface;
|
||||
use League\Flysystem\PluginInterface;
|
||||
|
||||
abstract class AbstractPlugin implements PluginInterface
|
||||
{
|
||||
/**
|
||||
* @var FilesystemInterface
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Set the Filesystem object.
|
||||
*
|
||||
* @param FilesystemInterface $filesystem
|
||||
*/
|
||||
public function setFilesystem(FilesystemInterface $filesystem)
|
||||
{
|
||||
$this->filesystem = $filesystem;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
class EmptyDir extends AbstractPlugin
|
||||
{
|
||||
/**
|
||||
* Get the method name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'emptyDir';
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty a directory's contents.
|
||||
*
|
||||
* @param string $dirname
|
||||
*/
|
||||
public function handle($dirname)
|
||||
{
|
||||
$listing = $this->filesystem->listContents($dirname, false);
|
||||
|
||||
foreach ($listing as $item) {
|
||||
if ($item['type'] === 'dir') {
|
||||
$this->filesystem->deleteDir($item['path']);
|
||||
} else {
|
||||
$this->filesystem->delete($item['path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
use League\Flysystem\FileExistsException;
|
||||
use League\Flysystem\FileNotFoundException;
|
||||
|
||||
class ForcedCopy extends AbstractPlugin
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'forceCopy';
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file, overwriting any existing files.
|
||||
*
|
||||
* @param string $path Path to the existing file.
|
||||
* @param string $newpath The new path of the file.
|
||||
*
|
||||
* @throws FileExistsException
|
||||
* @throws FileNotFoundException Thrown if $path does not exist.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function handle($path, $newpath)
|
||||
{
|
||||
try {
|
||||
$deleted = $this->filesystem->delete($newpath);
|
||||
} catch (FileNotFoundException $e) {
|
||||
// The destination path does not exist. That's ok.
|
||||
$deleted = true;
|
||||
}
|
||||
|
||||
if ($deleted) {
|
||||
return $this->filesystem->copy($path, $newpath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
use League\Flysystem\FileExistsException;
|
||||
use League\Flysystem\FileNotFoundException;
|
||||
|
||||
class ForcedRename extends AbstractPlugin
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'forceRename';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a file, overwriting the destination if it exists.
|
||||
*
|
||||
* @param string $path Path to the existing file.
|
||||
* @param string $newpath The new path of the file.
|
||||
*
|
||||
* @throws FileNotFoundException Thrown if $path does not exist.
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function handle($path, $newpath)
|
||||
{
|
||||
try {
|
||||
$deleted = $this->filesystem->delete($newpath);
|
||||
} catch (FileNotFoundException $e) {
|
||||
// The destination path does not exist. That's ok.
|
||||
$deleted = true;
|
||||
}
|
||||
|
||||
if ($deleted) {
|
||||
return $this->filesystem->rename($path, $newpath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use League\Flysystem\FileNotFoundException;
|
||||
|
||||
class GetWithMetadata extends AbstractPlugin
|
||||
{
|
||||
/**
|
||||
* Get the method name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'getWithMetadata';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata for an object with required metadata.
|
||||
*
|
||||
* @param string $path path to file
|
||||
* @param array $metadata metadata keys
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return array|false metadata
|
||||
*/
|
||||
public function handle($path, array $metadata)
|
||||
{
|
||||
$object = $this->filesystem->getMetadata($path);
|
||||
|
||||
if ( ! $object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$keys = array_diff($metadata, array_keys($object));
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if ( ! method_exists($this->filesystem, $method = 'get' . ucfirst($key))) {
|
||||
throw new InvalidArgumentException('Could not fetch metadata: ' . $key);
|
||||
}
|
||||
|
||||
$object[$key] = $this->filesystem->{$method}($path);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
class ListFiles extends AbstractPlugin
|
||||
{
|
||||
/**
|
||||
* Get the method name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'listFiles';
|
||||
}
|
||||
|
||||
/**
|
||||
* List all files in the directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function handle($directory = '', $recursive = false)
|
||||
{
|
||||
$contents = $this->filesystem->listContents($directory, $recursive);
|
||||
|
||||
$filter = function ($object) {
|
||||
return $object['type'] === 'file';
|
||||
};
|
||||
|
||||
return array_values(array_filter($contents, $filter));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
class ListPaths extends AbstractPlugin
|
||||
{
|
||||
/**
|
||||
* Get the method name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'listPaths';
|
||||
}
|
||||
|
||||
/**
|
||||
* List all paths.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array paths
|
||||
*/
|
||||
public function handle($directory = '', $recursive = false)
|
||||
{
|
||||
$result = [];
|
||||
$contents = $this->filesystem->listContents($directory, $recursive);
|
||||
|
||||
foreach ($contents as $object) {
|
||||
$result[] = $object['path'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
class ListWith extends AbstractPlugin
|
||||
{
|
||||
/**
|
||||
* Get the method name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'listWith';
|
||||
}
|
||||
|
||||
/**
|
||||
* List contents with metadata.
|
||||
*
|
||||
* @param array $keys
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array listing with metadata
|
||||
*/
|
||||
public function handle(array $keys = [], $directory = '', $recursive = false)
|
||||
{
|
||||
$contents = $this->filesystem->listContents($directory, $recursive);
|
||||
|
||||
foreach ($contents as $index => $object) {
|
||||
if ($object['type'] === 'file') {
|
||||
$missingKeys = array_diff($keys, array_keys($object));
|
||||
$contents[$index] = array_reduce($missingKeys, [$this, 'getMetadataByName'], $object);
|
||||
}
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a meta-data value by key name.
|
||||
*
|
||||
* @param array $object
|
||||
* @param string $key
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getMetadataByName(array $object, $key)
|
||||
{
|
||||
$method = 'get' . ucfirst($key);
|
||||
|
||||
if ( ! method_exists($this->filesystem, $method)) {
|
||||
throw new \InvalidArgumentException('Could not get meta-data for key: ' . $key);
|
||||
}
|
||||
|
||||
$object[$key] = $this->filesystem->{$method}($object['path']);
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
use BadMethodCallException;
|
||||
use League\Flysystem\FilesystemInterface;
|
||||
use League\Flysystem\PluginInterface;
|
||||
use LogicException;
|
||||
|
||||
trait PluggableTrait
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $plugins = [];
|
||||
|
||||
/**
|
||||
* Register a plugin.
|
||||
*
|
||||
* @param PluginInterface $plugin
|
||||
*
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addPlugin(PluginInterface $plugin)
|
||||
{
|
||||
if ( ! method_exists($plugin, 'handle')) {
|
||||
throw new LogicException(get_class($plugin) . ' does not have a handle method.');
|
||||
}
|
||||
|
||||
$this->plugins[$plugin->getMethod()] = $plugin;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific plugin.
|
||||
*
|
||||
* @param string $method
|
||||
*
|
||||
* @throws PluginNotFoundException
|
||||
*
|
||||
* @return PluginInterface
|
||||
*/
|
||||
protected function findPlugin($method)
|
||||
{
|
||||
if ( ! isset($this->plugins[$method])) {
|
||||
throw new PluginNotFoundException('Plugin not found for method: ' . $method);
|
||||
}
|
||||
|
||||
return $this->plugins[$method];
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a plugin by method name.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @param FilesystemInterface $filesystem
|
||||
*
|
||||
* @throws PluginNotFoundException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function invokePlugin($method, array $arguments, FilesystemInterface $filesystem)
|
||||
{
|
||||
$plugin = $this->findPlugin($method);
|
||||
$plugin->setFilesystem($filesystem);
|
||||
$callback = [$plugin, 'handle'];
|
||||
|
||||
return call_user_func_array($callback, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugins pass-through.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
*
|
||||
* @throws BadMethodCallException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, array $arguments)
|
||||
{
|
||||
try {
|
||||
return $this->invokePlugin($method, $arguments, $this);
|
||||
} catch (PluginNotFoundException $e) {
|
||||
throw new BadMethodCallException(
|
||||
'Call to undefined method '
|
||||
. get_class($this)
|
||||
. '::' . $method
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Plugin;
|
||||
|
||||
use LogicException;
|
||||
|
||||
class PluginNotFoundException extends LogicException
|
||||
{
|
||||
// This exception doesn't require additional information.
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
interface PluginInterface
|
||||
{
|
||||
/**
|
||||
* Get the method name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod();
|
||||
|
||||
/**
|
||||
* Set the Filesystem object.
|
||||
*
|
||||
* @param FilesystemInterface $filesystem
|
||||
*/
|
||||
public function setFilesystem(FilesystemInterface $filesystem);
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
interface ReadInterface
|
||||
{
|
||||
/**
|
||||
* Check whether a file exists.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function has($path);
|
||||
|
||||
/**
|
||||
* Read a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function read($path);
|
||||
|
||||
/**
|
||||
* Read a file as a stream.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function readStream($path);
|
||||
|
||||
/**
|
||||
* List contents of a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false);
|
||||
|
||||
/**
|
||||
* Get all the meta data of a file or directory.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function getMetadata($path);
|
||||
|
||||
/**
|
||||
* Get the size of a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function getSize($path);
|
||||
|
||||
/**
|
||||
* Get the mimetype of a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function getMimetype($path);
|
||||
|
||||
/**
|
||||
* Get the last modified time of a file as a timestamp.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function getTimestamp($path);
|
||||
|
||||
/**
|
||||
* Get the visibility of a file.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function getVisibility($path);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use LogicException;
|
||||
|
||||
class RootViolationException extends LogicException
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
final class SafeStorage
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $hash;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $safeStorage = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->hash = spl_object_hash($this);
|
||||
static::$safeStorage[$this->hash] = [];
|
||||
}
|
||||
|
||||
public function storeSafely($key, $value)
|
||||
{
|
||||
static::$safeStorage[$this->hash][$key] = $value;
|
||||
}
|
||||
|
||||
public function retrieveSafely($key)
|
||||
{
|
||||
if (array_key_exists($key, static::$safeStorage[$this->hash])) {
|
||||
return static::$safeStorage[$this->hash][$key];
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
unset(static::$safeStorage[$this->hash]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use SplFileInfo;
|
||||
|
||||
class UnreadableFileException extends Exception
|
||||
{
|
||||
public static function forFileInfo(SplFileInfo $fileInfo)
|
||||
{
|
||||
return new static(
|
||||
sprintf(
|
||||
'Unreadable file encountered: %s',
|
||||
$fileInfo->getRealPath()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,349 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem;
|
||||
|
||||
use League\Flysystem\Util\MimeType;
|
||||
use LogicException;
|
||||
|
||||
class Util
|
||||
{
|
||||
/**
|
||||
* Get normalized pathinfo.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array pathinfo
|
||||
*/
|
||||
public static function pathinfo($path)
|
||||
{
|
||||
$pathinfo = compact('path');
|
||||
|
||||
if ('' !== $dirname = dirname($path)) {
|
||||
$pathinfo['dirname'] = static::normalizeDirname($dirname);
|
||||
}
|
||||
|
||||
$pathinfo['basename'] = static::basename($path);
|
||||
|
||||
$pathinfo += pathinfo($pathinfo['basename']);
|
||||
|
||||
return $pathinfo + ['dirname' => ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a dirname return value.
|
||||
*
|
||||
* @param string $dirname
|
||||
*
|
||||
* @return string normalized dirname
|
||||
*/
|
||||
public static function normalizeDirname($dirname)
|
||||
{
|
||||
return $dirname === '.' ? '' : $dirname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a normalized dirname from a path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string dirname
|
||||
*/
|
||||
public static function dirname($path)
|
||||
{
|
||||
return static::normalizeDirname(dirname($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map result arrays.
|
||||
*
|
||||
* @param array $object
|
||||
* @param array $map
|
||||
*
|
||||
* @return array mapped result
|
||||
*/
|
||||
public static function map(array $object, array $map)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($map as $from => $to) {
|
||||
if ( ! isset($object[$from])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$to] = $object[$from];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function normalizePath($path)
|
||||
{
|
||||
return static::normalizeRelativePath($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize relative directories in a path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function normalizeRelativePath($path)
|
||||
{
|
||||
$path = str_replace('\\', '/', $path);
|
||||
$path = static::removeFunkyWhiteSpace($path);
|
||||
|
||||
$parts = [];
|
||||
|
||||
foreach (explode('/', $path) as $part) {
|
||||
switch ($part) {
|
||||
case '':
|
||||
case '.':
|
||||
break;
|
||||
|
||||
case '..':
|
||||
if (empty($parts)) {
|
||||
throw new LogicException(
|
||||
'Path is outside of the defined root, path: [' . $path . ']'
|
||||
);
|
||||
}
|
||||
array_pop($parts);
|
||||
break;
|
||||
|
||||
default:
|
||||
$parts[] = $part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return implode('/', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes unprintable characters and invalid unicode characters.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string $path
|
||||
*/
|
||||
protected static function removeFunkyWhiteSpace($path)
|
||||
{
|
||||
// We do this check in a loop, since removing invalid unicode characters
|
||||
// can lead to new characters being created.
|
||||
while (preg_match('#\p{C}+|^\./#u', $path)) {
|
||||
$path = preg_replace('#\p{C}+|^\./#u', '', $path);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize prefix.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $separator
|
||||
*
|
||||
* @return string normalized path
|
||||
*/
|
||||
public static function normalizePrefix($prefix, $separator)
|
||||
{
|
||||
return rtrim($prefix, $separator) . $separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content size.
|
||||
*
|
||||
* @param string $contents
|
||||
*
|
||||
* @return int content size
|
||||
*/
|
||||
public static function contentSize($contents)
|
||||
{
|
||||
return defined('MB_OVERLOAD_STRING') ? mb_strlen($contents, '8bit') : strlen($contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess MIME Type based on the path of the file and it's content.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string|resource $content
|
||||
*
|
||||
* @return string|null MIME Type or NULL if no extension detected
|
||||
*/
|
||||
public static function guessMimeType($path, $content)
|
||||
{
|
||||
$mimeType = MimeType::detectByContent($content);
|
||||
|
||||
if ( ! (empty($mimeType) || in_array($mimeType, ['application/x-empty', 'text/plain', 'text/x-asm']))) {
|
||||
return $mimeType;
|
||||
}
|
||||
|
||||
return MimeType::detectByFilename($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate directories.
|
||||
*
|
||||
* @param array $listing
|
||||
*
|
||||
* @return array listing with emulated directories
|
||||
*/
|
||||
public static function emulateDirectories(array $listing)
|
||||
{
|
||||
$directories = [];
|
||||
$listedDirectories = [];
|
||||
|
||||
foreach ($listing as $object) {
|
||||
list($directories, $listedDirectories) = static::emulateObjectDirectories($object, $directories, $listedDirectories);
|
||||
}
|
||||
|
||||
$directories = array_diff(array_unique($directories), array_unique($listedDirectories));
|
||||
|
||||
foreach ($directories as $directory) {
|
||||
$listing[] = static::pathinfo($directory) + ['type' => 'dir'];
|
||||
}
|
||||
|
||||
return $listing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a Config instance.
|
||||
*
|
||||
* @param null|array|Config $config
|
||||
*
|
||||
* @return Config config instance
|
||||
*
|
||||
* @throw LogicException
|
||||
*/
|
||||
public static function ensureConfig($config)
|
||||
{
|
||||
if ($config === null) {
|
||||
return new Config();
|
||||
}
|
||||
|
||||
if ($config instanceof Config) {
|
||||
return $config;
|
||||
}
|
||||
|
||||
if (is_array($config)) {
|
||||
return new Config($config);
|
||||
}
|
||||
|
||||
throw new LogicException('A config should either be an array or a Flysystem\Config object.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind a stream.
|
||||
*
|
||||
* @param resource $resource
|
||||
*/
|
||||
public static function rewindStream($resource)
|
||||
{
|
||||
if (ftell($resource) !== 0 && static::isSeekableStream($resource)) {
|
||||
rewind($resource);
|
||||
}
|
||||
}
|
||||
|
||||
public static function isSeekableStream($resource)
|
||||
{
|
||||
$metadata = stream_get_meta_data($resource);
|
||||
|
||||
return $metadata['seekable'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a stream.
|
||||
*
|
||||
* @param resource $resource
|
||||
*
|
||||
* @return int stream size
|
||||
*/
|
||||
public static function getStreamSize($resource)
|
||||
{
|
||||
$stat = fstat($resource);
|
||||
|
||||
return $stat['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate the directories of a single object.
|
||||
*
|
||||
* @param array $object
|
||||
* @param array $directories
|
||||
* @param array $listedDirectories
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function emulateObjectDirectories(array $object, array $directories, array $listedDirectories)
|
||||
{
|
||||
if ($object['type'] === 'dir') {
|
||||
$listedDirectories[] = $object['path'];
|
||||
}
|
||||
|
||||
if (empty($object['dirname'])) {
|
||||
return [$directories, $listedDirectories];
|
||||
}
|
||||
|
||||
$parent = $object['dirname'];
|
||||
|
||||
while ( ! empty($parent) && ! in_array($parent, $directories)) {
|
||||
$directories[] = $parent;
|
||||
$parent = static::dirname($parent);
|
||||
}
|
||||
|
||||
if (isset($object['type']) && $object['type'] === 'dir') {
|
||||
$listedDirectories[] = $object['path'];
|
||||
|
||||
return [$directories, $listedDirectories];
|
||||
}
|
||||
|
||||
return [$directories, $listedDirectories];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the trailing name component of the path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function basename($path)
|
||||
{
|
||||
$separators = DIRECTORY_SEPARATOR === '/' ? '/' : '\/';
|
||||
|
||||
$path = rtrim($path, $separators);
|
||||
|
||||
$basename = preg_replace('#.*?([^' . preg_quote($separators, '#') . ']+$)#', '$1', $path);
|
||||
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
return $basename;
|
||||
}
|
||||
// @codeCoverageIgnoreStart
|
||||
// Extra Windows path munging. This is tested via AppVeyor, but code
|
||||
// coverage is not reported.
|
||||
|
||||
// Handle relative paths with drive letters. c:file.txt.
|
||||
while (preg_match('#^[a-zA-Z]{1}:[^\\\/]#', $basename)) {
|
||||
$basename = substr($basename, 2);
|
||||
}
|
||||
|
||||
// Remove colon for standalone drive letter names.
|
||||
if (preg_match('#^[a-zA-Z]{1}:$#', $basename)) {
|
||||
$basename = rtrim($basename, ':');
|
||||
}
|
||||
|
||||
return $basename;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Util;
|
||||
|
||||
use League\Flysystem\Util;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ContentListingFormatter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $directory;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $recursive;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $caseSensitive;
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*/
|
||||
public function __construct($directory, $recursive, $caseSensitive = true)
|
||||
{
|
||||
$this->directory = rtrim($directory, '/');
|
||||
$this->recursive = $recursive;
|
||||
$this->caseSensitive = $caseSensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format contents listing.
|
||||
*
|
||||
* @param array $listing
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function formatListing(array $listing)
|
||||
{
|
||||
$listing = array_filter(array_map([$this, 'addPathInfo'], $listing), [$this, 'isEntryOutOfScope']);
|
||||
|
||||
return $this->sortListing(array_values($listing));
|
||||
}
|
||||
|
||||
private function addPathInfo(array $entry)
|
||||
{
|
||||
return $entry + Util::pathinfo($entry['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the entry is out of scope.
|
||||
*
|
||||
* @param array $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isEntryOutOfScope(array $entry)
|
||||
{
|
||||
if (empty($entry['path']) && $entry['path'] !== '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->recursive) {
|
||||
return $this->residesInDirectory($entry);
|
||||
}
|
||||
|
||||
return $this->isDirectChild($entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the entry resides within the parent directory.
|
||||
*
|
||||
* @param array $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function residesInDirectory(array $entry)
|
||||
{
|
||||
if ($this->directory === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->caseSensitive
|
||||
? strpos($entry['path'], $this->directory . '/') === 0
|
||||
: stripos($entry['path'], $this->directory . '/') === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the entry is a direct child of the directory.
|
||||
*
|
||||
* @param array $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isDirectChild(array $entry)
|
||||
{
|
||||
return $this->caseSensitive
|
||||
? $entry['dirname'] === $this->directory
|
||||
: strcasecmp($this->directory, $entry['dirname']) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $listing
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function sortListing(array $listing)
|
||||
{
|
||||
usort($listing, function ($a, $b) {
|
||||
return strcasecmp($a['path'], $b['path']);
|
||||
});
|
||||
|
||||
return $listing;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Util;
|
||||
|
||||
use ErrorException;
|
||||
use finfo;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class MimeType
|
||||
{
|
||||
protected static $extensionToMimeTypeMap = [
|
||||
'hqx' => 'application/mac-binhex40',
|
||||
'cpt' => 'application/mac-compactpro',
|
||||
'csv' => 'text/csv',
|
||||
'bin' => 'application/octet-stream',
|
||||
'dms' => 'application/octet-stream',
|
||||
'lha' => 'application/octet-stream',
|
||||
'lzh' => 'application/octet-stream',
|
||||
'exe' => 'application/octet-stream',
|
||||
'class' => 'application/octet-stream',
|
||||
'psd' => 'application/x-photoshop',
|
||||
'so' => 'application/octet-stream',
|
||||
'sea' => 'application/octet-stream',
|
||||
'dll' => 'application/octet-stream',
|
||||
'oda' => 'application/oda',
|
||||
'pdf' => 'application/pdf',
|
||||
'ai' => 'application/pdf',
|
||||
'eps' => 'application/postscript',
|
||||
'epub' => 'application/epub+zip',
|
||||
'ps' => 'application/postscript',
|
||||
'smi' => 'application/smil',
|
||||
'smil' => 'application/smil',
|
||||
'mif' => 'application/vnd.mif',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'xlt' => 'application/vnd.ms-excel',
|
||||
'xla' => 'application/vnd.ms-excel',
|
||||
'ppt' => 'application/powerpoint',
|
||||
'pot' => 'application/vnd.ms-powerpoint',
|
||||
'pps' => 'application/vnd.ms-powerpoint',
|
||||
'ppa' => 'application/vnd.ms-powerpoint',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
|
||||
'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
|
||||
'wbxml' => 'application/wbxml',
|
||||
'wmlc' => 'application/wmlc',
|
||||
'dcr' => 'application/x-director',
|
||||
'dir' => 'application/x-director',
|
||||
'dxr' => 'application/x-director',
|
||||
'dvi' => 'application/x-dvi',
|
||||
'gtar' => 'application/x-gtar',
|
||||
'gz' => 'application/x-gzip',
|
||||
'gzip' => 'application/x-gzip',
|
||||
'php' => 'application/x-httpd-php',
|
||||
'php4' => 'application/x-httpd-php',
|
||||
'php3' => 'application/x-httpd-php',
|
||||
'phtml' => 'application/x-httpd-php',
|
||||
'phps' => 'application/x-httpd-php-source',
|
||||
'js' => 'application/javascript',
|
||||
'swf' => 'application/x-shockwave-flash',
|
||||
'sit' => 'application/x-stuffit',
|
||||
'tar' => 'application/x-tar',
|
||||
'tgz' => 'application/x-tar',
|
||||
'z' => 'application/x-compress',
|
||||
'xhtml' => 'application/xhtml+xml',
|
||||
'xht' => 'application/xhtml+xml',
|
||||
'rdf' => 'application/rdf+xml',
|
||||
'zip' => 'application/x-zip',
|
||||
'rar' => 'application/x-rar',
|
||||
'mid' => 'audio/midi',
|
||||
'midi' => 'audio/midi',
|
||||
'mpga' => 'audio/mpeg',
|
||||
'mp2' => 'audio/mpeg',
|
||||
'mp3' => 'audio/mpeg',
|
||||
'aif' => 'audio/x-aiff',
|
||||
'aiff' => 'audio/x-aiff',
|
||||
'aifc' => 'audio/x-aiff',
|
||||
'ram' => 'audio/x-pn-realaudio',
|
||||
'rm' => 'audio/x-pn-realaudio',
|
||||
'rpm' => 'audio/x-pn-realaudio-plugin',
|
||||
'ra' => 'audio/x-realaudio',
|
||||
'rv' => 'video/vnd.rn-realvideo',
|
||||
'wav' => 'audio/x-wav',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'jpe' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'gif' => 'image/gif',
|
||||
'bmp' => 'image/bmp',
|
||||
'tiff' => 'image/tiff',
|
||||
'tif' => 'image/tiff',
|
||||
'svg' => 'image/svg+xml',
|
||||
'css' => 'text/css',
|
||||
'html' => 'text/html',
|
||||
'htm' => 'text/html',
|
||||
'shtml' => 'text/html',
|
||||
'txt' => 'text/plain',
|
||||
'text' => 'text/plain',
|
||||
'log' => 'text/plain',
|
||||
'rtx' => 'text/richtext',
|
||||
'rtf' => 'text/rtf',
|
||||
'xml' => 'application/xml',
|
||||
'xsl' => 'application/xml',
|
||||
'dmn' => 'application/octet-stream',
|
||||
'bpmn' => 'application/octet-stream',
|
||||
'mpeg' => 'video/mpeg',
|
||||
'mpg' => 'video/mpeg',
|
||||
'mpe' => 'video/mpeg',
|
||||
'qt' => 'video/quicktime',
|
||||
'mov' => 'video/quicktime',
|
||||
'avi' => 'video/x-msvideo',
|
||||
'movie' => 'video/x-sgi-movie',
|
||||
'doc' => 'application/msword',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'docm' => 'application/vnd.ms-word.template.macroEnabled.12',
|
||||
'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
|
||||
'dot' => 'application/msword',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||
'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
|
||||
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
|
||||
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'word' => 'application/msword',
|
||||
'xl' => 'application/excel',
|
||||
'eml' => 'message/rfc822',
|
||||
'json' => 'application/json',
|
||||
'pem' => 'application/x-x509-user-cert',
|
||||
'p10' => 'application/x-pkcs10',
|
||||
'p12' => 'application/x-pkcs12',
|
||||
'p7a' => 'application/x-pkcs7-signature',
|
||||
'p7c' => 'application/pkcs7-mime',
|
||||
'p7m' => 'application/pkcs7-mime',
|
||||
'p7r' => 'application/x-pkcs7-certreqresp',
|
||||
'p7s' => 'application/pkcs7-signature',
|
||||
'crt' => 'application/x-x509-ca-cert',
|
||||
'crl' => 'application/pkix-crl',
|
||||
'der' => 'application/x-x509-ca-cert',
|
||||
'kdb' => 'application/octet-stream',
|
||||
'pgp' => 'application/pgp',
|
||||
'gpg' => 'application/gpg-keys',
|
||||
'sst' => 'application/octet-stream',
|
||||
'csr' => 'application/octet-stream',
|
||||
'rsa' => 'application/x-pkcs7',
|
||||
'cer' => 'application/pkix-cert',
|
||||
'3g2' => 'video/3gpp2',
|
||||
'3gp' => 'video/3gp',
|
||||
'mp4' => 'video/mp4',
|
||||
'm4a' => 'audio/x-m4a',
|
||||
'f4v' => 'video/mp4',
|
||||
'webm' => 'video/webm',
|
||||
'aac' => 'audio/x-acc',
|
||||
'm4u' => 'application/vnd.mpegurl',
|
||||
'm3u' => 'text/plain',
|
||||
'xspf' => 'application/xspf+xml',
|
||||
'vlc' => 'application/videolan',
|
||||
'wmv' => 'video/x-ms-wmv',
|
||||
'au' => 'audio/x-au',
|
||||
'ac3' => 'audio/ac3',
|
||||
'flac' => 'audio/x-flac',
|
||||
'ogg' => 'audio/ogg',
|
||||
'kmz' => 'application/vnd.google-earth.kmz',
|
||||
'kml' => 'application/vnd.google-earth.kml+xml',
|
||||
'ics' => 'text/calendar',
|
||||
'zsh' => 'text/x-scriptzsh',
|
||||
'7zip' => 'application/x-7z-compressed',
|
||||
'cdr' => 'application/cdr',
|
||||
'wma' => 'audio/x-ms-wma',
|
||||
'jar' => 'application/java-archive',
|
||||
'tex' => 'application/x-tex',
|
||||
'latex' => 'application/x-latex',
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
||||
'odg' => 'application/vnd.oasis.opendocument.graphics',
|
||||
'odc' => 'application/vnd.oasis.opendocument.chart',
|
||||
'odf' => 'application/vnd.oasis.opendocument.formula',
|
||||
'odi' => 'application/vnd.oasis.opendocument.image',
|
||||
'odm' => 'application/vnd.oasis.opendocument.text-master',
|
||||
'odb' => 'application/vnd.oasis.opendocument.database',
|
||||
'ott' => 'application/vnd.oasis.opendocument.text-template',
|
||||
];
|
||||
|
||||
/**
|
||||
* Detects MIME Type based on given content.
|
||||
*
|
||||
* @param mixed $content
|
||||
*
|
||||
* @return string|null MIME Type or NULL if no mime type detected
|
||||
*/
|
||||
public static function detectByContent($content)
|
||||
{
|
||||
if ( ! class_exists('finfo') || ! is_string($content)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||
|
||||
return $finfo->buffer($content) ?: null;
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (ErrorException $e) {
|
||||
// This is caused by an array to string conversion error.
|
||||
}
|
||||
} // @codeCoverageIgnoreEnd
|
||||
|
||||
/**
|
||||
* Detects MIME Type based on file extension.
|
||||
*
|
||||
* @param string $extension
|
||||
*
|
||||
* @return string|null MIME Type or NULL if no extension detected
|
||||
*/
|
||||
public static function detectByFileExtension($extension)
|
||||
{
|
||||
return isset(static::$extensionToMimeTypeMap[$extension])
|
||||
? static::$extensionToMimeTypeMap[$extension]
|
||||
: 'text/plain';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
*
|
||||
* @return string|null MIME Type or NULL if no extension detected
|
||||
*/
|
||||
public static function detectByFilename($filename)
|
||||
{
|
||||
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
|
||||
return empty($extension) ? 'text/plain' : static::detectByFileExtension($extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Map of file extension to MIME Type
|
||||
*/
|
||||
public static function getExtensionToMimeTypeMap()
|
||||
{
|
||||
return static::$extensionToMimeTypeMap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace League\Flysystem\Util;
|
||||
|
||||
class StreamHasher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $algo;
|
||||
|
||||
/**
|
||||
* StreamHasher constructor.
|
||||
*
|
||||
* @param string $algo
|
||||
*/
|
||||
public function __construct($algo)
|
||||
{
|
||||
$this->algo = $algo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $resource
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function hash($resource)
|
||||
{
|
||||
rewind($resource);
|
||||
$context = hash_init($this->algo);
|
||||
hash_update_stream($context, $resource);
|
||||
fclose($resource);
|
||||
|
||||
return hash_final($context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
CHANGELOG
|
||||
---------
|
||||
|
||||
### v3.4.1, 2019.10.19
|
||||
|
||||
- Fixed a [bug](https://github.com/opis/closure/issues/40) that prevented serialization to work correctly.
|
||||
|
||||
### v3.4.0, 2019.09.03
|
||||
|
||||
- Added `createClosure` static method in `Opis\Closure\SerializableClosure`.
|
||||
This method creates a new closure from arbitrary code, emulating `create_function`,
|
||||
but without using eval
|
||||
|
||||
### v3.3.1, 2019.07.10
|
||||
|
||||
- Use `sha1` instead of `md5` for hashing file names in `Opis\Closure\ReflectionClosure` class
|
||||
|
||||
### v3.3.0, 2019.05.31
|
||||
|
||||
- Fixed a bug that prevented signed closures to properly work when the serialized string
|
||||
contains invalid UTF-8 chars. Starting with this version `json_encode` is no longer used
|
||||
when signing a closure. Backward compatibility is maintained and all closures that were
|
||||
previously signed using the old method will continue to work.
|
||||
|
||||
### v3.2.0, 2019.05.05
|
||||
|
||||
- Since an unsigned closure can be unserialized when no security provider is set,
|
||||
there is no reason to treat differently a signed closure in the same situation.
|
||||
Therefore, the `Opis\Closure\SecurityException` exception is no longer thrown when
|
||||
unserializing a signed closure, if no security provider is set.
|
||||
|
||||
### v3.1.6, 2019.02.22
|
||||
|
||||
- Fixed a bug that occurred when trying to set properties of classes that were not defined in user-land.
|
||||
Those properties are now ignored.
|
||||
|
||||
### v3.1.5, 2019.01.14
|
||||
|
||||
- Improved parser
|
||||
|
||||
### v3.1.4, 2019.01.14
|
||||
|
||||
- Added support for static methods that are named using PHP keywords or magic constants.
|
||||
Ex: `A::new()`, `A::use()`, `A::if()`, `A::function()`, `A::__DIR__()`, etc.
|
||||
- Used `@internal` to mark classes & methods that are for internal use only and
|
||||
backward compatibility is not guaranteed.
|
||||
|
||||
### v3.1.3, 2019.01.07
|
||||
|
||||
- Fixed a bug that prevented traits to be correctly resolved when used by an
|
||||
anonymous class
|
||||
- Fixed a bug that occurred when `$this` keyword was used inside an anonymous class
|
||||
|
||||
### v3.1.2, 2018.12.16
|
||||
|
||||
* Fixed a bug regarding comma trail in group-use statements. See [issue 23](https://github.com/opis/closure/issues/23)
|
||||
|
||||
### v3.1.1, 2018.10.02
|
||||
|
||||
* Fixed a bug where `parent` keyword was treated like a class-name and scope was not added to the
|
||||
serialized closure
|
||||
* Fixed a bug where return type was not properly handled for nested closures
|
||||
* Support for anonymous classes was improved
|
||||
|
||||
### v3.1.0, 2018.09.20
|
||||
|
||||
* Added `transformUseVariables` and `resolveUseVariables` to
|
||||
`Opis\Closure\SerializableClosure` class.
|
||||
* Added `removeSecurityProvider` static method to
|
||||
`Opis\Closure\SerializableClosure` class.
|
||||
* Fixed some security related issues where a user was able to unserialize an unsigned
|
||||
closure, even when a security provider was in use.
|
||||
|
||||
### v3.0.12, 2018.02.23
|
||||
|
||||
* Bugfix. See [issue 20](https://github.com/opis/closure/issues/20)
|
||||
|
||||
### v3.0.11, 2018.01.22
|
||||
|
||||
* Bugfix. See [issue 18](https://github.com/opis/closure/issues/18)
|
||||
|
||||
### v3.0.10, 2018.01.04
|
||||
|
||||
* Improved support for PHP 7.1 & 7.2
|
||||
|
||||
### v3.0.9, 2018.01.04
|
||||
|
||||
* Fixed a bug where the return type was not properly resolved.
|
||||
See [issue 17](https://github.com/opis/closure/issues/17)
|
||||
* Added more tests
|
||||
|
||||
### v3.0.8, 2017.12.18
|
||||
|
||||
* Fixed a bug. See [issue 16](https://github.com/opis/closure/issues/16)
|
||||
|
||||
### v3.0.7, 2017.10.31
|
||||
|
||||
* Bugfix: static properties are ignored now, since they are not serializable
|
||||
|
||||
### v3.0.6, 2017.10.06
|
||||
|
||||
* Fixed a bug introduced by accident in 3.0.5
|
||||
|
||||
### v3.0.5, 2017.09.18
|
||||
|
||||
* Fixed a bug related to nested references
|
||||
|
||||
### v3.0.4, 2017.09.18
|
||||
|
||||
* \[*internal*\] Refactored `SerializableClosure::mapPointers` method
|
||||
* \[*internal*\] Added a new optional argument to `SerializableClosure::unwrapClosures`
|
||||
* \[*internal*\] Removed `SerializableClosure::getClosurePointer` method
|
||||
* Fixed various bugs
|
||||
|
||||
### v3.0.3, 2017.09.06
|
||||
|
||||
* Fixed a bug related to nested object references
|
||||
* \[*internal*\] `Opis\Closure\ClosureScope` now extends `SplObjectStorage`
|
||||
* \[*internal*\] The `storage` property was removed from `Opis\Closure\ClosureScope`
|
||||
* \[*internal*\] The `instances` and `objects` properties were removed from `Opis\Closure\ClosureContext`
|
||||
|
||||
### v3.0.2, 2017.08.28
|
||||
|
||||
* Fixed a bug where `$this` object was not handled properly inside the
|
||||
`SerializableClosre::serialize` method.
|
||||
|
||||
### v3.0.1, 2017.04.13
|
||||
|
||||
* Fixed a bug in 'ignore_next' state
|
||||
|
||||
### v3.0.0, 2017.04.07
|
||||
|
||||
* Dropped PHP 5.3 support
|
||||
* Moved source files from `lib` to `src` folder
|
||||
* Removed second parameter from `Opis\Closure\SerializableClosure::from` method and from constructor
|
||||
* Removed `Opis\Closure\{SecurityProviderInterface, DefaultSecurityProvider, SecureClosure}` classes
|
||||
* Refactored how signed closures were handled
|
||||
* Added `wrapClosures` and `unwrapClosures` static methods to `Opis\Closure\SerializableClosure` class
|
||||
* Added `Opis\Colosure\serialize` and `Opis\Closure\unserialize` functions
|
||||
* Improved serialization. You can now serialize arbitrary objects and the library will automatically wrap all closures
|
||||
|
||||
### v2.4.0, 2016.12.16
|
||||
|
||||
* The parser was refactored and improved
|
||||
* Refactored `Opis\Closure\SerializableClosure::__invoke` method
|
||||
* `Opis\Closure\{ISecurityProvider, SecurityProvider}` were added
|
||||
* `Opis\Closure\{SecurityProviderInterface, DefaultSecurityProvider, SecureClosure}` were deprecated
|
||||
and they will be removed in the next major version
|
||||
* `setSecretKey` and `addSecurityProvider` static methods were added to `Opis\Closure\SerializableClosure`
|
||||
|
||||
### v2.3.2, 2016.12.15
|
||||
|
||||
* Fixed a bug that prevented namespace resolution to be done properly
|
||||
|
||||
### v2.3.1, 2016.12.13
|
||||
|
||||
* Hotfix. See [PR](https://github.com/opis/closure/pull/7)
|
||||
|
||||
### v2.3.0, 2016.11.17
|
||||
|
||||
* Added `isBindingRequired` and `isScopeRequired` to the `Opis\Closure\ReflectionClosure` class
|
||||
* Automatically detects when the scope and/or the bound object of a closure needs to be serialized.
|
||||
|
||||
### v2.2.1, 2016.08.20
|
||||
|
||||
* Fixed a bug in `Opis\Closure\ReflectionClosure::fetchItems`
|
||||
|
||||
### v2.2.0, 2016.07.26
|
||||
|
||||
* Fixed CS
|
||||
* `Opis\Closure\ClosureContext`, `Opis\Closure\ClosureScope`, `Opis\Closure\SelfReference`
|
||||
and `Opis\Closure\SecurityException` classes were moved into separate files
|
||||
* Added support for PHP7 syntax
|
||||
* Fixed some bugs in `Opis\Closure\ReflectionClosure` class
|
||||
* Improved closure parser
|
||||
* Added an analyzer for SuperClosure library
|
||||
|
||||
### v2.1.0, 2015.09.30
|
||||
|
||||
* Added support for the missing `__METHOD__`, `__FUNCTION__` and `__TRAIT__` magic constants
|
||||
* Added some security related classes and interfaces: `Opis\Closure\SecurityProviderInterface`,
|
||||
`Opis\Closure\DefaultSecurityProvider`, `Opis\Closure\SecureClosure`, `Opis\Closure\SecurityException`.
|
||||
* Fiexed a bug in `Opis\Closure\ReflectionClosure::getClasses` method
|
||||
* Other minor bugfixes
|
||||
* Added support for static closures
|
||||
* Added public `isStatic` method to `Opis\Closure\ReflectionClosure` class
|
||||
|
||||
|
||||
### v2.0.1, 2015.09.23
|
||||
|
||||
* Removed `branch-alias` property from `composer.json`
|
||||
* Bugfix. See [issue #6](https://github.com/opis/closure/issues/6)
|
||||
|
||||
### v2.0.0, 2015.07.31
|
||||
|
||||
* The closure parser was improved
|
||||
* Class names are now automatically resolved
|
||||
* Added support for the `#trackme` directive which allows tracking closure's residing source
|
||||
|
||||
### v1.3.0, 2014.10.18
|
||||
|
||||
* Added autoload file
|
||||
* Changed README file
|
||||
|
||||
### Opis Closure 1.2.2
|
||||
|
||||
* Started changelog
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2019 Zindex Software
|
||||
|
||||
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.
|
|
@ -0,0 +1,9 @@
|
|||
Opis Closure
|
||||
Copyright 2018-2019 Zindex Software
|
||||
|
||||
This product includes software developed at
|
||||
Zindex Software (http://zindex.software).
|
||||
|
||||
This software was originally developed by Marius Sarca and Sorin Sarca
|
||||
(Copyright 2014-2018). The copyright info was changed with the permission
|
||||
of the original authors.
|
|
@ -0,0 +1,98 @@
|
|||
Opis Closure
|
||||
====================
|
||||
[![Build Status](https://travis-ci.org/opis/closure.png)](https://travis-ci.org/opis/closure)
|
||||
[![Latest Stable Version](https://poser.pugx.org/opis/closure/v/stable.png)](https://packagist.org/packages/opis/closure)
|
||||
[![Latest Unstable Version](https://poser.pugx.org/opis/closure/v/unstable.png)](https://packagist.org/packages/opis/closure)
|
||||
[![License](https://poser.pugx.org/opis/closure/license.png)](https://packagist.org/packages/opis/closure)
|
||||
|
||||
Serializable closures
|
||||
---------------------
|
||||
**Opis Closure** is a library that aims to overcome PHP's limitations regarding closure
|
||||
serialization by providing a wrapper that will make all closures serializable.
|
||||
|
||||
**The library's key features:**
|
||||
|
||||
- Serialize any closure
|
||||
- Serialize arbitrary objects
|
||||
- Doesn't use `eval` for closure serialization or unserialization
|
||||
- Works with any PHP version that has support for closures
|
||||
- Supports PHP 7 syntax
|
||||
- Handles all variables referenced/imported in `use()` and automatically wraps all referenced/imported closures for
|
||||
proper serialization
|
||||
- Handles recursive closures
|
||||
- Handles magic constants like `__FILE__`, `__DIR__`, `__LINE__`, `__NAMESPACE__`, `__CLASS__`,
|
||||
`__TRAIT__`, `__METHOD__` and `__FUNCTION__`.
|
||||
- Automatically resolves all class names, function names and constant names used inside the closure
|
||||
- Track closure's residing source by using the `#trackme` directive
|
||||
- Simple and very fast parser
|
||||
- Any error or exception, that might occur when executing an unserialized closure, can be caught and treated properly
|
||||
- You can serialize/unserialize any closure unlimited times, even those previously unserialized
|
||||
(this is possible because `eval()` is not used for unserialization)
|
||||
- Handles static closures
|
||||
- Supports cryptographically signed closures
|
||||
- Provides a reflector that can give you information about the serialized closure
|
||||
- Provides an analyzer for *SuperClosure* library
|
||||
- Automatically detects when the scope and/or the bound object of a closure needs to be serialized
|
||||
in order for the closure to work after deserialization
|
||||
|
||||
### Documentation
|
||||
|
||||
The full documentation for this library can be found [here][documentation].
|
||||
|
||||
### License
|
||||
|
||||
**Opis Closure** is licensed under the [MIT License (MIT)][license].
|
||||
|
||||
### Requirements
|
||||
|
||||
* PHP ^5.4 || ^7.0
|
||||
|
||||
## Installation
|
||||
|
||||
**Opis Closure** is available on [Packagist] and it can be installed from a
|
||||
command line interface by using [Composer].
|
||||
|
||||
```bash
|
||||
composer require opis/closure
|
||||
```
|
||||
|
||||
Or you could directly reference it into your `composer.json` file as a dependency
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"opis/closure": "^3.4"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Migrating from 2.x
|
||||
|
||||
If your project needs to support PHP 5.3 you can continue using the `2.x` version
|
||||
of **Opis Closure**. Otherwise, assuming you are not using one of the removed/refactored classes or features(see
|
||||
[CHANGELOG]), migrating to version `3.x` is simply a matter of updating your `composer.json` file.
|
||||
|
||||
### Semantic versioning
|
||||
|
||||
**Opis Closure** follows [semantic versioning][SemVer] specifications.
|
||||
|
||||
### Arbitrary object serialization
|
||||
|
||||
This feature was primarily introduced in order to support serializing an object bound
|
||||
to a closure and available via `$this`. The implementation is far from being perfect
|
||||
and it's really hard to make it work flawless. I will try to improve this, but I can
|
||||
not guarantee anything. So my advice regarding the `Opis\Closure\serialize|unserialize`
|
||||
functions is to use them with caution.
|
||||
|
||||
### SuperClosure support
|
||||
|
||||
**Opis Closure** is shipped with an analyzer(`Opis\Closure\Analyzer`) which
|
||||
aims to provide *Opis Closure*'s parsing precision and speed to [SuperClosure].
|
||||
|
||||
[documentation]: https://www.opis.io/closure "Opis Closure"
|
||||
[license]: http://opensource.org/licenses/MIT "MIT License"
|
||||
[Packagist]: https://packagist.org/packages/opis/closure "Packagist"
|
||||
[Composer]: https://getcomposer.org "Composer"
|
||||
[SuperClosure]: https://github.com/jeremeamia/super_closure "SuperClosure"
|
||||
[SemVer]: http://semver.org/ "Semantic versioning"
|
||||
[CHANGELOG]: https://github.com/opis/closure/blob/master/CHANGELOG.md "Changelog"
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
/* ===========================================================================
|
||||
* Copyright (c) 2018-2019 Zindex Software
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
* =========================================================================== */
|
||||
|
||||
require_once 'functions.php';
|
||||
|
||||
spl_autoload_register(function($class){
|
||||
|
||||
$class = ltrim($class, '\\');
|
||||
$dir = __DIR__ . '/src';
|
||||
$namespace = 'Opis\Closure';
|
||||
|
||||
if(strpos($class, $namespace) === 0)
|
||||
{
|
||||
$class = substr($class, strlen($namespace));
|
||||
$path = '';
|
||||
if(($pos = strripos($class, '\\')) !== FALSE)
|
||||
{
|
||||
$path = str_replace('\\', '/', substr($class, 0, $pos)) . '/';
|
||||
$class = substr($class, $pos + 1);
|
||||
}
|
||||
$path .= str_replace('_', '/', $class) . '.php';
|
||||
$dir .= '/' . $path;
|
||||
|
||||
if(file_exists($dir))
|
||||
{
|
||||
include $dir;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue