Primo Committ

This commit is contained in:
paoloar77
2024-05-07 12:17:25 +02:00
commit e73d0e5113
7204 changed files with 884387 additions and 0 deletions

View File

@@ -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((string) $this->getPathPrefix()));
}
}

View File

@@ -0,0 +1,705 @@
<?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') {
$result = compact('type', 'path');
if ($this->enableTimestampsOnUnixListings) {
$timestamp = $this->normalizeUnixTimestamp($month, $day, $timeOrYear);
$result += compact('timestamp');
}
return $result;
}
$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}/', trim($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)
{
if (is_numeric($permissions)) {
return ((int) $permissions) & 0777;
}
// 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()
{
if ( ! $this->isConnected()) {
$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();
protected function escapePath($path)
{
return str_replace(['*', '[', ']'], ['\\*', '\\[', '\\]'], $path);
}
}

View File

@@ -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
{
}

View File

@@ -0,0 +1,584 @@
<?php
namespace League\Flysystem\Adapter;
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
use League\Flysystem\ConnectionErrorException;
use League\Flysystem\ConnectionRuntimeException;
use League\Flysystem\InvalidRootException;
use League\Flysystem\Util;
use League\Flysystem\Util\MimeType;
use function in_array;
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()
{
$tries = 3;
start_connecting:
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) {
$tries--;
if ($tries > 0) goto start_connecting;
throw new ConnectionRuntimeException('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 (!in_array(substr($response[0], 0, 3), ['200', '202'])) {
throw new ConnectionRuntimeException(
'Could not set UTF-8 mode for connection: ' . $this->getHost() . '::' . $this->getPort()
);
}
}
}
/**
* Set the connections to passive mode.
*
* @throws ConnectionRuntimeException
*/
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 ConnectionRuntimeException(
'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 InvalidRootException('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 ConnectionRuntimeException
*/
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 ConnectionRuntimeException(
'Could not login with connection: ' . $this->getHost() . '::' . $this->getPort(
) . ', username: ' . $this->getUsername()
);
}
}
/**
* Disconnect from the FTP server.
*/
public function disconnect()
{
if ($this->hasFtpConnection()) {
@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', $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)
{
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 ConnectionErrorException
*/
public function isConnected()
{
return $this->hasFtpConnection() && $this->getRawExecResponseCode('NOOP') === 200;
}
/**
* @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 . ' ' . $this->escapePath($path));
}
private function getRawExecResponseCode($command)
{
$response = @ftp_raw($this->connection, trim($command));
return (int) preg_replace('/\D/', '', implode(' ', $response));
}
private function hasFtpConnection(): bool
{
return is_resource($this->connection) || $this->connection instanceof \FTP\Connection;
}
}

View File

@@ -0,0 +1,48 @@
<?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];
}
$object = ftp_raw($this->getConnection(), 'STAT ' . $this->escapePath($path));
if ( ! $object || 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(), $this->escapePath($directory), $recursive);
if ($listing === false || ( ! empty($listing) && substr($listing[0], 0, 5) === "ftpd:")) {
return [];
}
return $this->normalizeListing($listing, $directory);
}
}

View File

@@ -0,0 +1,533 @@
<?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 ($visibility = $config->get('visibility')) {
$this->setVisibility($path, $visibility);
$result['visibility'] = $visibility;
}
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);
}
unset($iterator);
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);
}
unset($contents);
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);
}
}
}

View 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;
}
}

View 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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -0,0 +1,9 @@
<?php
namespace League\Flysystem\Adapter\Polyfill;
trait StreamedTrait
{
use StreamedReadingTrait;
use StreamedWritingTrait;
}

View File

@@ -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);
}

View File

@@ -0,0 +1,8 @@
<?php
namespace League\Flysystem\Adapter;
class SynologyFtp extends Ftpd
{
// This class merely exists because of BC.
}