Seditio Source
Root |
./othercms/dle15_0/upload/engine/classes/composer/vendor/league/flysystem-aws-s3-v3/AwsS3V3Adapter.php
<?php

declare(strict_types=1);

namespace
League\Flysystem\AwsS3V3;

use
Aws\Api\DateTimeResult;
use
Aws\S3\S3ClientInterface;
use
Generator;
use
League\Flysystem\Config;
use
League\Flysystem\DirectoryAttributes;
use
League\Flysystem\FileAttributes;
use
League\Flysystem\FilesystemAdapter;
use
League\Flysystem\FilesystemOperationFailed;
use
League\Flysystem\PathPrefixer;
use
League\Flysystem\StorageAttributes;
use
League\Flysystem\UnableToCheckFileExistence;
use
League\Flysystem\UnableToCopyFile;
use
League\Flysystem\UnableToDeleteFile;
use
League\Flysystem\UnableToMoveFile;
use
League\Flysystem\UnableToReadFile;
use
League\Flysystem\UnableToRetrieveMetadata;
use
League\Flysystem\UnableToSetVisibility;
use
League\Flysystem\UnableToWriteFile;
use
League\Flysystem\Visibility;
use
League\MimeTypeDetection\FinfoMimeTypeDetector;
use
League\MimeTypeDetection\MimeTypeDetector;
use
Psr\Http\Message\StreamInterface;
use
Throwable;

class
AwsS3V3Adapter implements FilesystemAdapter
{
   
/**
     * @var string[]
     */
   
public const AVAILABLE_OPTIONS = [
       
'ACL',
       
'CacheControl',
       
'ContentDisposition',
       
'ContentEncoding',
       
'ContentLength',
       
'ContentMD5',
       
'ContentType',
       
'Expires',
       
'GrantFullControl',
       
'GrantRead',
       
'GrantReadACP',
       
'GrantWriteACP',
       
'Metadata',
       
'MetadataDirective',
       
'RequestPayer',
       
'SSECustomerAlgorithm',
       
'SSECustomerKey',
       
'SSECustomerKeyMD5',
       
'SSEKMSKeyId',
       
'ServerSideEncryption',
       
'StorageClass',
       
'Tagging',
       
'WebsiteRedirectLocation',
    ];

   
/**
     * @var string[]
     */
   
private const EXTRA_METADATA_FIELDS = [
       
'Metadata',
       
'StorageClass',
       
'ETag',
       
'VersionId',
    ];

   
/**
     * @var S3ClientInterface
     */
   
private $client;

   
/**
     * @var PathPrefixer
     */
   
private $prefixer;

   
/**
     * @var string
     */
   
private $bucket;

   
/**
     * @var VisibilityConverter
     */
   
private $visibility;

   
/**
     * @var MimeTypeDetector
     */
   
private $mimeTypeDetector;

   
/**
     * @var array
     */
   
private $options;

   
/**
     * @var bool
     */
   
private $streamReads;

    public function
__construct(
       
S3ClientInterface $client,
       
string $bucket,
       
string $prefix = '',
       
VisibilityConverter $visibility = null,
       
MimeTypeDetector $mimeTypeDetector = null,
        array
$options = [],
       
bool $streamReads = true
   
) {
       
$this->client = $client;
       
$this->prefixer = new PathPrefixer($prefix);
       
$this->bucket = $bucket;
       
$this->visibility = $visibility ?: new PortableVisibilityConverter();
       
$this->mimeTypeDetector = $mimeTypeDetector ?: new FinfoMimeTypeDetector();
       
$this->options = $options;
       
$this->streamReads = $streamReads;
    }

    public function
fileExists(string $path): bool
   
{
        try {
            return
$this->client->doesObjectExist($this->bucket, $this->prefixer->prefixPath($path), $this->options);
        } catch (
Throwable $exception) {
            throw
UnableToCheckFileExistence::forLocation($path, $exception);
        }
    }

    public function
write(string $path, string $contents, Config $config): void
   
{
       
$this->upload($path, $contents, $config);
    }

   
/**
     * @param string          $path
     * @param string|resource $body
     * @param Config          $config
     */
   
private function upload(string $path, $body, Config $config): void
   
{
       
$key = $this->prefixer->prefixPath($path);
       
$options = $this->createOptionsFromConfig($config);
       
$acl = $options['ACL'] ?? $this->determineAcl($config);
       
$shouldDetermineMimetype = $body !== '' && ! array_key_exists('ContentType', $options);

        if (
$shouldDetermineMimetype && $mimeType = $this->mimeTypeDetector->detectMimeType($key, $body)) {
           
$options['ContentType'] = $mimeType;
        }

        try {
           
$this->client->upload($this->bucket, $key, $body, $acl, ['params' => $options]);
        } catch (
Throwable $exception) {
            throw
UnableToWriteFile::atLocation($path, '', $exception);
        }
    }

    private function
determineAcl(Config $config): string
   
{
       
$visibility = (string) $config->get(Config::OPTION_VISIBILITY, Visibility::PRIVATE);

        return
$this->visibility->visibilityToAcl($visibility);
    }

    private function
createOptionsFromConfig(Config $config): array
    {
       
$options = [];

        foreach (static::
AVAILABLE_OPTIONS as $option) {
           
$value = $config->get($option, '__NOT_SET__');

            if (
$value !== '__NOT_SET__') {
               
$options[$option] = $value;
            }
        }

        return
$options + $this->options;
    }

    public function
writeStream(string $path, $contents, Config $config): void
   
{
       
$this->upload($path, $contents, $config);
    }

    public function
read(string $path): string
   
{
       
$body = $this->readObject($path, false);

        return (string)
$body->getContents();
    }

    public function
readStream(string $path)
    {
       
/** @var resource $resource */
       
$resource = $this->readObject($path, true)->detach();

        return
$resource;
    }

    public function
delete(string $path): void
   
{
       
$arguments = ['Bucket' => $this->bucket, 'Key' => $this->prefixer->prefixPath($path)];
       
$command = $this->client->getCommand('DeleteObject', $arguments);

        try {
           
$this->client->execute($command);
        } catch (
Throwable $exception) {
            throw
UnableToDeleteFile::atLocation($path, '', $exception);
        }
    }

    public function
deleteDirectory(string $path): void
   
{
       
$prefix = $this->prefixer->prefixPath($path);
       
$prefix = ltrim(rtrim($prefix, '/') . '/', '/');
       
$this->client->deleteMatchingObjects($this->bucket, $prefix);
    }

    public function
createDirectory(string $path, Config $config): void
   
{
       
$config = $config->withDefaults(['visibility' => $this->visibility->defaultForDirectories()]);
       
$this->upload(rtrim($path, '/') . '/', '', $config);
    }

    public function
setVisibility(string $path, string $visibility): void
   
{
       
$arguments = [
           
'Bucket' => $this->bucket,
           
'Key' => $this->prefixer->prefixPath($path),
           
'ACL' => $this->visibility->visibilityToAcl($visibility),
        ];
       
$command = $this->client->getCommand('PutObjectAcl', $arguments);

        try {
           
$this->client->execute($command);
        } catch (
Throwable $exception) {
            throw
UnableToSetVisibility::atLocation($path, '', $exception);
        }
    }

    public function
visibility(string $path): FileAttributes
   
{
       
$arguments = ['Bucket' => $this->bucket, 'Key' => $this->prefixer->prefixPath($path)];
       
$command = $this->client->getCommand('GetObjectAcl', $arguments);

        try {
           
$result = $this->client->execute($command);
        } catch (
Throwable $exception) {
            throw
UnableToRetrieveMetadata::visibility($path, '', $exception);
        }

       
$visibility = $this->visibility->aclToVisibility((array) $result->get('Grants'));

        return new
FileAttributes($path, null, $visibility);
    }

    private function
fetchFileMetadata(string $path, string $type): FileAttributes
   
{
       
$arguments = ['Bucket' => $this->bucket, 'Key' => $this->prefixer->prefixPath($path)];
       
$command = $this->client->getCommand('HeadObject', $arguments);

        try {
           
$result = $this->client->execute($command);
        } catch (
Throwable $exception) {
            throw
UnableToRetrieveMetadata::create($path, $type, '', $exception);
        }

       
$attributes = $this->mapS3ObjectMetadata($result->toArray(), $path);

        if ( !
$attributes instanceof FileAttributes) {
            throw
UnableToRetrieveMetadata::create($path, $type, '');
        }

        return
$attributes;
    }

    private function
mapS3ObjectMetadata(array $metadata, string $path = null): StorageAttributes
   
{
        if (
$path === null) {
           
$path = $this->prefixer->stripPrefix($metadata['Key'] ?? $metadata['Prefix']);
        }

        if (
substr($path, -1) === '/') {
            return new
DirectoryAttributes(rtrim($path, '/'));
        }

       
$mimetype = $metadata['ContentType'] ?? null;
       
$fileSize = $metadata['ContentLength'] ?? $metadata['Size'] ?? null;
       
$fileSize = $fileSize === null ? null : (int) $fileSize;
       
$dateTime = $metadata['LastModified'] ?? null;
       
$lastModified = $dateTime instanceof DateTimeResult ? $dateTime->getTimeStamp() : null;

        return new
FileAttributes(
           
$path,
           
$fileSize,
           
null,
           
$lastModified,
           
$mimetype,
           
$this->extractExtraMetadata($metadata)
        );
    }

    private function
extractExtraMetadata(array $metadata): array
    {
       
$extracted = [];

        foreach (static::
EXTRA_METADATA_FIELDS as $field) {
            if (isset(
$metadata[$field]) && $metadata[$field] !== '') {
               
$extracted[$field] = $metadata[$field];
            }
        }

        return
$extracted;
    }

    public function
mimeType(string $path): FileAttributes
   
{
       
$attributes = $this->fetchFileMetadata($path, FileAttributes::ATTRIBUTE_MIME_TYPE);

        if (
$attributes->mimeType() === null) {
            throw
UnableToRetrieveMetadata::mimeType($path);
        }

        return
$attributes;
    }

    public function
lastModified(string $path): FileAttributes
   
{
       
$attributes = $this->fetchFileMetadata($path, FileAttributes::ATTRIBUTE_LAST_MODIFIED);

        if (
$attributes->lastModified() === null) {
            throw
UnableToRetrieveMetadata::lastModified($path);
        }

        return
$attributes;
    }

    public function
fileSize(string $path): FileAttributes
   
{
       
$attributes = $this->fetchFileMetadata($path, FileAttributes::ATTRIBUTE_FILE_SIZE);

        if (
$attributes->fileSize() === null) {
            throw
UnableToRetrieveMetadata::fileSize($path);
        }

        return
$attributes;
    }

    public function
listContents(string $path, bool $deep): iterable
   
{
       
$prefix = trim($this->prefixer->prefixPath($path), '/');
       
$prefix = empty($prefix) ? '' : $prefix . '/';
       
$options = ['Bucket' => $this->bucket, 'Prefix' => $prefix];

        if (
$deep === false) {
           
$options['Delimiter'] = '/';
        }

       
$listing = $this->retrievePaginatedListing($options);

        foreach (
$listing as $item) {
            yield
$this->mapS3ObjectMetadata($item);
        }
    }

    private function
retrievePaginatedListing(array $options): Generator
   
{
       
$resultPaginator = $this->client->getPaginator('ListObjects', $options + $this->options);

        foreach (
$resultPaginator as $result) {
            yield from (
$result->get('CommonPrefixes') ?: []);
            yield from (
$result->get('Contents') ?: []);
        }
    }

    public function
move(string $source, string $destination, Config $config): void
   
{
        try {
           
$this->copy($source, $destination, $config);
           
$this->delete($source);
        } catch (
FilesystemOperationFailed $exception) {
            throw
UnableToMoveFile::fromLocationTo($source, $destination, $exception);
        }
    }

    public function
copy(string $source, string $destination, Config $config): void
   
{
        try {
           
/** @var string $visibility */
           
$visibility = $this->visibility($source)->visibility();
        } catch (
Throwable $exception) {
            throw
UnableToCopyFile::fromLocationTo(
               
$source,
               
$destination,
               
$exception
           
);
        }

        try {
           
$this->client->copy(
               
$this->bucket,
               
$this->prefixer->prefixPath($source),
               
$this->bucket,
               
$this->prefixer->prefixPath($destination),
               
$this->visibility->visibilityToAcl($visibility),
               
$this->createOptionsFromConfig($config)
            );
        } catch (
Throwable $exception) {
            throw
UnableToCopyFile::fromLocationTo($source, $destination, $exception);
        }
    }

    private function
readObject(string $path, bool $wantsStream): StreamInterface
   
{
       
$options = ['Bucket' => $this->bucket, 'Key' => $this->prefixer->prefixPath($path)];

        if (
$wantsStream && $this->streamReads && ! isset($this->options['@http']['stream'])) {
           
$options['@http']['stream'] = true;
        }

       
$command = $this->client->getCommand('GetObject', $options + $this->options);

        try {
            return
$this->client->execute($command)->get('Body');
        } catch (
Throwable $exception) {
            throw
UnableToReadFile::fromLocation($path, '', $exception);
        }
    }
}