From 64faf0778a8e5b5c4d14ac3ef81e4380021ffb4e Mon Sep 17 00:00:00 2001 From: Danil Lytvyn Date: Wed, 8 Feb 2023 10:13:42 +0200 Subject: [PATCH] DP-460 Upgrade to PHP 8.1 / Laravel 9 - Update dependencies - Get rid of global helper functions - Alter error handling - Change the metadata retrieval logic - Get rid of use adapter pattern directly, use the FilesystemOperator instead - Change mime type detector - Get rid of old info normalizers - Fix Cache-Control headers - Refactor blob streaming in order to support file caching - Lock WebDAV adaptor version We captured the version of the WebDAV adaptor because we found a problem with the directory creation. WebDav v3.1.1 does not have this problem. Need to unlock the version after fixing this bug in the adapter. --- composer.json | 7 +- src/Components/BaseFlysystem.php | 471 ++++++++++++++++++---------- src/Components/DFSftpAdapter.php | 32 +- src/Components/DfFtpAdapter.php | 76 ++--- src/Components/DfWebDavAdapter.php | 11 - src/Components/FTPFileSystem.php | 21 ++ src/Components/LocalFileSystem.php | 27 +- src/Components/RemoteFileSystem.php | 99 +++++- src/Components/SFTPFileSystem.php | 52 +-- src/Components/WebDAVFileSystem.php | 51 +-- src/Services/BaseFileService.php | 28 +- src/Services/FTPFileService.php | 3 +- src/Services/LocalFileService.php | 3 +- src/Services/SFTPFileService.php | 3 +- src/Services/WebDAVFileService.php | 19 +- 15 files changed, 480 insertions(+), 423 deletions(-) diff --git a/composer.json b/composer.json index 4f6b45f..d8e5139 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,10 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "dreamfactory/df-core": "~0.25", - "league/flysystem-sftp": "^1.0", - "league/flysystem-webdav": "^1.0" + "dreamfactory/df-core": "~1.0", + "league/flysystem-ftp": "^3.0", + "league/flysystem-sftp-v3": "^3.0", + "league/flysystem-webdav": "3.1.1" }, "require-dev": { "phpunit/phpunit": "@stable" diff --git a/src/Components/BaseFlysystem.php b/src/Components/BaseFlysystem.php index 214fdc2..f713d76 100644 --- a/src/Components/BaseFlysystem.php +++ b/src/Components/BaseFlysystem.php @@ -3,13 +3,31 @@ namespace DreamFactory\Core\File\Components; use DreamFactory\Core\Contracts\FileSystemInterface; -use DreamFactory\Core\Exceptions\InternalServerErrorException; -use DreamFactory\Core\Exceptions\NotImplementedException; -use DreamFactory\Core\Exceptions\NotFoundException; use DreamFactory\Core\Utility\FileUtilities; -use League\Flysystem\Config; -use DreamFactory\Core\Exceptions\BadRequestException; use Log; +use DreamFactory\Core\Exceptions\ { + InternalServerErrorException, + NotImplementedException, + BadRequestException, + NotFoundException, +}; +use League\Flysystem\ { + DirectoryAttributes, + StorageAttributes, + FileAttributes, + Config, +}; +use League\Flysystem\ { + UnableToCheckFileExistence, + UnableToCheckExistence, + UnableToCreateDirectory, + UnableToDeleteDirectory, + FilesystemException, + UnableToDeleteFile, + UnableToWriteFile, + UnableToCopyFile, + UnableToReadFile, +}; abstract class BaseFlysystem implements FileSystemInterface { @@ -19,9 +37,14 @@ abstract class BaseFlysystem implements FileSystemInterface protected $config; /** - * @var \League\Flysystem\AdapterInterface + * @var \League\Flysystem\FilesystemAdapter */ - protected $adapter; + protected \League\Flysystem\FilesystemAdapter $adapter; + + /** + * @var \League\Flysystem\FilesystemOperator + */ + private \League\Flysystem\FilesystemOperator $fileSystem; /** * FileSystem constructor. @@ -32,6 +55,7 @@ public function __construct($config) { $this->config = $config; $this->setAdapter($config); + $this->fileSystem = new \League\Flysystem\Filesystem($this->adapter); } /** @@ -116,7 +140,11 @@ public function deleteContainers($containers, $force = false) */ public function folderExists($container, $path) { - return (!$this->adapter->has($path)) ? false : true; + try { + return empty($path) || $this->fileSystem->directoryExists($path); + } catch (FilesystemException | UnableToCheckExistence $exception) { + throw new InternalServerErrorException('Failed to retrieve folder properties.'); + } } /** @@ -126,67 +154,110 @@ public function getFolder($container, $path, $include_files = true, $include_fol { if ($this->folderExists($container, $path)) { $path = rtrim($path, '/'); - $this->adapter->setRecurseManually($full_tree); - $contents = $this->adapter->listContents($path, $full_tree); - foreach ($contents as $key => $content) { - if (strtolower(array_get($content, 'type')) === 'dir') { - $this->normalizeFolderInfo($content, $path); - } else { - $this->normalizeFileInfo($content, $path); - } - $contents[$key] = $content; - } - - return $contents; + $listing = $this->fileSystem->listContents($path, $full_tree) + ->filter(fn ($item) => + $item->isDir() && $include_folders || + $item->isFile() && $include_files + ) + ->map(fn ($item) => $this->mapAttributesToInfoArray($item, $path)) + ->toArray(); + return $listing; } else { throw new NotFoundException("Folder '$path' does not exist in storage."); } } /** - * @param array $folder - * @param string $localizer - * - * @throws \DreamFactory\Core\Exceptions\InternalServerErrorException + * Create an array with metadata for provided file or folder + * + * @param \League\Flysystem\StorageAttributes $item File or folder to retrieve metadata from + * @param string $parentFolder Base path to exclude from item name + * + * @return array */ - protected function normalizeFolderInfo(array & $folder, $localizer) + protected function mapAttributesToInfoArray(StorageAttributes $item, string $parentFolder): array { - if (strtolower(array_get($folder, 'type')) !== 'dir') { - throw new InternalServerErrorException('Fatal error. Invalid folder info provided for normalization.'); + $info = ['path' => $item->path()]; + $this->normalizeName($info, $parentFolder); + if ($this->includeLastModified($item)) { + $info['last_modified'] = $this->lastModified($item); } - $path = array_get($folder, 'path'); - $folder['path'] = FileUtilities::fixFolderPath($path); - $folder['name'] = trim(substr($path, strlen($localizer)), '/'); - if (empty($folder['name'])) { - $folder['name'] = basename($path); + if ($item instanceof \League\Flysystem\FileAttributes) { + $info['type'] = 'file'; + $info['content_type'] = $this->mimeType($item); + $info['content_length'] = $item->fileSize(); + } elseif ($item instanceof \League\Flysystem\DirectoryAttributes) { + $info['type'] = 'folder'; + $info['path'] = FileUtilities::fixFolderPath($info['path']); } - $folder['type'] = 'folder'; + + return $info; } /** - * @param array $file - * @param string $localizer - * - * @throws \DreamFactory\Core\Exceptions\InternalServerErrorException + * Exclude base path from item name. Modifies input `$info` array + * + * @param array &$info Item metadata array with `name` and `path` keys + * @param string $localizer Base path to exclude + * + * @return void */ - protected function normalizeFileInfo(array &$file, $localizer) + private function normalizeName(array & $info, string $localizer) { - if (strtolower(array_get($file, 'type')) !== 'file') { - throw new InternalServerErrorException('Fatal error. Invalid file info provided for normalization.'); + $path = $info['path']; + $info['name'] = trim(substr($path, strlen($localizer)), '/'); + if (empty($info['name'])) { + $info['name'] = basename($path); } + } - unset($file['visibility']); - $path = array_get($file, 'path'); - $timestamp = $this->adapter->getTimestamp($path); - $file['name'] = trim(substr($path, strlen($localizer)), '/'); - if (empty($file['name'])) { - $file['name'] = basename($path); - } - $file['last_modified'] = gmdate('D, d M Y H:i:s \G\M\T', array_get($timestamp, 'timestamp', 0)); - $file['content_type'] = array_get($this->adapter->getMimetype($path), 'mimetype'); - $file['content_length'] = array_get($file, 'size'); - unset($file['size']); + /** + * Determine whether the entity includes lastModified metadata + * + * @param \League\Flysystem\StorageAttributes $item The entity from which to read metadata + * + * @return bool + */ + protected function includeLastModified(StorageAttributes $item): bool + { + return !empty($item->lastModified()); + } + + /** + * Get lastModified file metadata in GMT time zone + * + * @param \League\Flysystem\StorageAttributes $item The entity from which to read metadata + * + * @return string + */ + protected function lastModified(StorageAttributes $item): string + { + return gmdate('D, d M Y H:i:s \G\M\T', $item->lastModified() ?: 0); + } + + /** + * Get file MIME type + * + * @param \League\Flysystem\FileAttributes $file The file from which to read metadata + * + * @return string + */ + protected function mimeType(FileAttributes $file) : string + { + return $file->mimeType() ?: $this->detectMimeType($file->path()); + } + + /** + * Determine the MIME type of an entity + * + * @param string $path + * + * @return string + */ + protected function detectMimeType(string $path) : string + { + return $this->fileSystem->mimeType($path); } /** @@ -195,17 +266,15 @@ protected function normalizeFileInfo(array &$file, $localizer) public function getFolderProperties($container, $path) { $path = rtrim($path, '/'); - $meta = $this->adapter->getMetadata($path); - if ($meta === false) { - throw new NotFoundException("Specified folder '" . $path . "' not found."); - } - if (array_get($meta, 'type') === 'dir') { - $this->normalizeFolderInfo($meta, $path); - unset($meta['type'], $meta['size'], $meta['visibility']); - - return $meta; + if ($this->folderExists($container, $path)) { + $props = $this->mapAttributesToInfoArray( + $this->getCommonMetadata($path), + $path, + ); + unset($props['type']); + return $props; } else { - throw new InternalServerErrorException('Fatal error. Invalid folder path provided.'); + throw new NotFoundException("Specified folder '" . $path . "' not found."); } } @@ -221,15 +290,12 @@ public function createFolder($container, $path, $properties = []) throw new BadRequestException("Folder '" . $path . "' already exists."); } $path = rtrim($path, '/'); - $result = $this->adapter->createDir($path, new Config()); - if ($result === false) { + try { + $this->adapter->createDirectory($path, new Config()); + } catch (FilesystemException | UnableToCreateDirectory $exception) { throw new InternalServerErrorException("Failed to create folder '" . $path . "'."); } - - $this->normalizeFolderInfo($result, $path); - - return $result; } /** @@ -271,18 +337,25 @@ public function copyFolder($container, $dest_path, $src_container, $src_path, $c */ public function copyTree($src, $dst) { - $meta = $this->adapter->getMetadata($src); - if ($meta['type'] === 'dir' && $this->folderExists('', $src)) { - $this->adapter->ensureDirectory($dst); - $files = $this->adapter->listContents($src); + $meta = $this->getCommonMetadata($src); + $this->copyTreeRecursively($meta, $dst); + } + + /** + * @param StorageAttributes $src + * @param string $dst + */ + protected function copyTreeRecursively(StorageAttributes $src, string $dst) + { + if ($src->isFile()) { + $this->fileSystem->copy($src->path(), $dst); + } else { + $relevantRoot = FileUtilities::fixFolderPath($dst) . basename($src->path()); + $this->fileSystem->createDirectory($relevantRoot); + $files = $this->fileSystem->listContents($src->path(), false); foreach ($files as $file) { - $path = array_get($file, 'path'); - $srcFile = $path; - $dstFile = FileUtilities::fixFolderPath($dst) . basename($path); - $this->copyTree($srcFile, $dstFile); + $this->copyTreeRecursively($file, $relevantRoot . basename($file->path())); } - } elseif ($meta['type'] === 'file' && $this->fileExists('', $src)) { - $this->adapter->copy($src, $dst); } } @@ -300,10 +373,8 @@ public function deleteFolder($container, $path, $force = false, $content_only = $this->deleteTree($path, !$content_only); } else { try { - if (!$this->adapter->deleteDir($path)) { - throw new InternalServerErrorException("Failed to delete folder '" . $path . "'"); - } - } catch (\Exception $e) { + $this->adapter->deleteDirectory($path); + } catch (FilesystemException | UnableToDeleteDirectory $exception) { throw new InternalServerErrorException("Directory not empty, can not delete without force option."); } } @@ -316,18 +387,15 @@ public function deleteFolder($container, $path, $force = false, $content_only = public function deleteTree($path, $delete_self = true) { $path = rtrim($path, '/'); - $meta = $this->adapter->getMetadata($path); - - if ($meta['type'] === 'dir') { - $contents = $this->adapter->listContents($path); - foreach ($contents as $content) { - $this->deleteTree($content['path']); - } - if ($delete_self) { - $this->adapter->deleteDir($path); + $meta = $this->getCommonMetadata($path); + + if ($meta->isDir()) { + $this->fileSystem->deleteDirectory($path); + if ( ! $delete_self) { + $this->fileSystem->createDirectory($path); } - } elseif ($meta['type'] === 'file') { - $this->adapter->delete($path); + } else { + $this->deleteFile(null, $path, true); } } @@ -344,7 +412,11 @@ public function deleteFolders($container, $folders, $root = '', $force = false) */ public function fileExists($container, $path) { - return (!$this->adapter->has($path)) ? false : true; + try { + return $this->adapter->fileExists($path); + } catch (FilesystemException | UnableToCheckFileExistence $exception) { + throw new InternalServerErrorException('Failed to retrieve file properties.'); + } } /** @@ -361,26 +433,19 @@ public function getFileContent($container, $path, $local_file = null, $content_a public function getFileProperties($container, $path, $include_content = false, $content_as_base = true) { $path = rtrim($path, '/'); - $meta = $this->adapter->getMetadata($path); - if ($meta === false) { - throw new NotFoundException("Specified file '" . $path . "' does not exist."); - } else { - $this->normalizeFileInfo($meta, $path); - } + $fileAttributes = $this->getFileAttributes($path); + $meta = $this->mapAttributesToInfoArray($fileAttributes, $path); if ($include_content) { - $streamObj = $this->adapter->readStream($path); - if ($streamObj !== false) { - $stream = array_get($streamObj, 'stream'); - if (empty($stream)) { - throw new InternalServerErrorException('Failed to retrieve file properties.'); - } - $contents = fread($stream, array_get($meta, 'content_length')); - if ($content_as_base) { - $contents = base64_encode($contents); - } - $meta['content'] = $contents; + try { + $contents = $this->adapter->read($path); + } catch (FilesystemException | UnableToReadFile $exception) { + throw new InternalServerErrorException('Failed to retrieve file properties.'); } + if ($content_as_base) { + $contents = base64_encode($contents); + } + $meta['content'] = $contents; } unset($meta['type']); @@ -393,53 +458,75 @@ public function getFileProperties($container, $path, $include_content = false, $ */ public function streamFile($container, $path, $download = false) { + if (empty($path)) { + Log::warning('Failed to stream file: ' . $path); + $statusHeader = 'HTTP/1.1 404'; + header($statusHeader); + header('Content-Type: text/html'); + echo 'The specified file ' . $path . ' does not exist.'; + return; + } + try { - $result = $this->adapter->readStream($path); + $fileAttributes = $this->getFileAttributes($path); + + $fileName = basename($path); + $ext = FileUtilities::getFileExtension($fileName); + $disposition = ($download) ? 'attachment; filename="' . $fileName . '";' : 'inline'; + + header('Cache-Control: no-cache, private'); + header('Last-Modified: ' . $this->lastModified($fileAttributes)); + header('Content-Type: ' . $this->mimeType($fileAttributes)); + header('Content-Length:' . $fileAttributes->fileSize()); + if ($download || 'html' !== $ext) { + header('Content-Disposition: ' . $disposition); + } + + if ($this->notModified($fileAttributes)) { + header('HTTP/1.1 304 Not Modified'); + return; + } + $chunk = \Config::get('df.file_chunk_size'); - - if (!empty($path) && isset($result['stream'])) { - $file = basename($path); - $meta = $this->adapter->getMetadata($path); - $mimeType = array_get($this->adapter->getMimetype($path), 'mimetype'); - $timestamp = array_get($this->adapter->getTimestamp($path), 'timestamp'); - $stream = array_get($result, 'stream'); - - $ext = FileUtilities::getFileExtension($file); - $disposition = ($download) ? 'attachment; filename="' . $file . '";' : 'inline'; - header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', $timestamp)); - header('Content-Type: ' . $mimeType); - header('Content-Length:' . array_get($meta, 'size')); - if ($download || 'html' !== $ext) { - header('Content-Disposition: ' . $disposition); - } - header('Cache-Control: private'); // use this to open files directly - header('Expires: 0'); - header('Pragma: public'); - if (empty($chunk)) { - print(fread($stream, array_get($meta, 'size'))); - } else { - while (!feof($stream) and (connection_status() == 0)) { - print(fread($stream, $chunk)); - flush(); - } - } + $stream = $this->fileSystem->readStream($path); + if (empty($chunk)) { + print(fread($stream, $fileAttributes->fileSize())); } else { - Log::debug('Failed to stream file: ' . $path); - $statusHeader = 'HTTP/1.1 404'; - header($statusHeader); - header('Content-Type: text/html'); - echo 'The specified file ' . $path . ' does not exist.'; + while (!feof($stream) and (connection_status() == 0)) { + print(fread($stream, $chunk)); + flush(); + } } - } catch (\Exception $e) { - Log::debug('Failed to stream file: ' . $path); - $statusHeader = 'HTTP/1.1 404'; - header($statusHeader); - header('Content-Type: text/html'); + } catch (NotFoundException $ex) { + header('HTTP/1.1 404 Not Found'); echo 'Could not open the specified file ' . $path; - echo $e->getMessage(); + echo $ex->getMessage(); + } catch (\Exception $ex) { + header('HTTP/1.1 500 Internal Server Error'); + \Log::error('Failed to stream file: ' . $path); + \Log::error($ex->getMessage()); + \Log::error($ex->getTraceAsString()); } } + /** + * Checks if the file has not changed from the last request + * + * @param FileAttributes $file + * + * @return bool + */ + private function notModified(FileAttributes $file) : bool + { + $timestamp = $file->lastModified(); + $ifModifiedSince = \Illuminate\Support\Arr::get($_SERVER, 'HTTP_IF_MODIFIED_SINCE'); + return ( + !empty($timestamp) && + !empty($ifModifiedSince) && + strtotime($ifModifiedSince) === $timestamp + ); + } + /** * {@inheritdoc} */ @@ -469,8 +556,9 @@ public function writeFile($container, $path, $content, $content_is_base = false, $content = base64_decode($content); } - $result = $this->adapter->write($path, $content, new Config()); - if (false === $result) { + try { + $this->adapter->write($path, $content, new Config()); + } catch (FilesystemException | UnableToWriteFile $exception) { throw new InternalServerErrorException('Failed to create file.'); } } @@ -515,7 +603,9 @@ public function copyFile($container, $dest_path, $src_container, $src_path, $che throw new BadRequestException("File '$dest_path' already exists."); } } - if (!$this->adapter->copy($src_path, $dest_path)) { + try { + $this->adapter->copy($src_path, $dest_path, new Config()); + } catch (FilesystemException | UnableToCopyFile $exception) { throw new InternalServerErrorException('Failed to copy file from ' . $src_path . ' to ' . $dest_path); } } @@ -528,7 +618,9 @@ public function deleteFile($container, $path, $noCheck = false) if (!$noCheck && !$this->fileExists($container, $path)) { throw new NotFoundException("File '$path' was not found."); } - if (!$this->adapter->delete($path)) { + try { + $this->adapter->delete($path); + } catch (FilesystemException | UnableToDeleteFile $exception) { throw new InternalServerErrorException('Failed to delete file.'); } } @@ -623,30 +715,65 @@ public function addTreeToZip($zip, $path) { $path = rtrim($path, '/'); if ($this->folderExists('', $path)) { - $files = $this->adapter->listContents($path); - if (empty($files)) { + $files = $this->fileSystem->listContents($path); + $isEmpty = true; + + foreach ($files as $file) { + $isEmpty = false; + if ($file->isDir()) { + static::addTreeToZip($zip, $file->path()); + } elseif ($file->isFile()) { + $newPath = str_replace(DIRECTORY_SEPARATOR, '/', $file->path()); + $content = $this->fileSystem->read($newPath); + if (!$zip->addFromString($file->path(), $content)) { + throw new \Exception("Can not include file '$newPath' in zip file."); + } + } + } + if ($isEmpty) { $newPath = str_replace(DIRECTORY_SEPARATOR, '/', $path); if (!$zip->addEmptyDir($newPath)) { throw new \Exception("Can not include folder '$newPath' in zip file."); } - - return; - } - foreach ($files as $file) { - if ($file['type'] === 'dir') { - static::addTreeToZip($zip, $file['path']); - } elseif ($file['type'] === 'file') { - $newPath = str_replace(DIRECTORY_SEPARATOR, '/', $file['path']); - $fileObj = $this->adapter->read($newPath); - if (!empty($fileObj) && isset($fileObj['contents'])) { - if (!$zip->addFromString($file['path'], $fileObj['contents'])) { - throw new \Exception("Can not include file '$newPath' in zip file."); - } - } else { - throw new InternalServerErrorException("Could not read file '" . $file . "'"); - } - } } } } + + /** + * @param string $path + * + * @return StorageAttributes + * @throws NotFoundException + */ + protected function getCommonMetadata(string $path): StorageAttributes + { + if ($this->fileExists(null, $path)) { + return new FileAttributes($path); + } elseif ($this->folderExists(null, $path)) { + return new DirectoryAttributes($path); + } else { + throw new NotFoundException("Invalid data provided. Specified path '" . $path . "' does not exist."); + } + } + + /** + * @param string $path + * + * @return FileAttributes + * @throws NotFoundException + */ + protected function getFileAttributes(string $path): FileAttributes + { + if (!$this->fileSystem->fileExists($path)) { + throw new NotFoundException("Specified file '" . $path . "' does not exist."); + } + + return new FileAttributes( + $path, + $this->fileSystem->fileSize($path), + null, + $this->fileSystem->lastModified($path), + $this->detectMimeType($path), + ); + } } \ No newline at end of file diff --git a/src/Components/DFSftpAdapter.php b/src/Components/DFSftpAdapter.php index 05389f0..f5a14a0 100644 --- a/src/Components/DFSftpAdapter.php +++ b/src/Components/DFSftpAdapter.php @@ -2,36 +2,14 @@ namespace DreamFactory\Core\File\Components; -use League\Flysystem\Sftp\SftpAdapter; +use League\Flysystem\PhpseclibV3\SftpAdapter; +use League\Flysystem\PhpseclibV3\SftpConnectionProvider; use League\Flysystem\Util; class DFSftpAdapter extends SftpAdapter { - /** - * @inheritdoc - */ - public function getMetadata($path) - { - $connection = $this->getConnection(); - - if (empty($path)) $path = '/'; - - $info = $connection->stat($path); - - if ($info === false) { - return false; - } - - $result = Util::map($info, $this->statMap); - $result['type'] = $info['type'] === NET_SFTP_TYPE_DIRECTORY ? 'dir' : 'file'; - $result['visibility'] = $info['permissions'] & $this->permPublic ? 'public' : 'private'; - - $name = basename($path); - $base = str_replace($name, null, $path); - $base = rtrim($base, '/'); - - $result['path'] = empty($base) ? $name : $base . $this->separator . $name; - - return $result; + public function __construct(array $config) + { + parent::__construct(SftpConnectionProvider::fromArray($config), $config['root']); } } \ No newline at end of file diff --git a/src/Components/DfFtpAdapter.php b/src/Components/DfFtpAdapter.php index eeadc01..d51a9d2 100644 --- a/src/Components/DfFtpAdapter.php +++ b/src/Components/DfFtpAdapter.php @@ -2,69 +2,29 @@ namespace DreamFactory\Core\File\Components; -use League\Flysystem\Adapter\Ftp; +use League\Flysystem\Ftp\FtpAdapter; +use League\Flysystem\Ftp\FtpConnectionOptions; -class DfFtpAdapter extends Ftp +class DfFtpAdapter extends FtpAdapter { - /** - * @inheritdoc - * - * @param string $directory - */ - protected function listDirectoryContentsRecursive($directory) - { - $listing = $this->normalizeListing($this->ftpRawlist('-aln', $directory) ?: [], $directory); - $output = []; - - foreach ($listing as $directory) { - $output[] = $directory; - if ($directory['type'] !== 'dir') { - continue; - } - $output = array_merge($output, $this->listDirectoryContentsRecursive($directory['path'])); - } - - return $output; + public function __construct(array $config) + { + parent::__construct( + $this::buildFtpConnectionOptions($config) + ); } - /** - * @param string $path - * - * @return array|bool - */ - public function getMetadata($path) + private static function buildFtpConnectionOptions(array $config): FtpConnectionOptions { - $connection = $this->getConnection(); - - if ($path === '') { - return ['type' => 'dir', 'path' => '']; - } - - if (@ftp_chdir($connection, $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); - } - - $filename = basename($path); - $basePath = str_replace($filename, null, $path); - $basePath = rtrim($basePath, '/'); - - return $this->normalizeObject($listing[0], $basePath); + $options = array_merge( + [ + 'recurseManually' => true, + 'timestampsOnUnixListingsEnabled' => true + ], + $config + ); + return FtpConnectionOptions::fromArray($options); } + } \ No newline at end of file diff --git a/src/Components/DfWebDavAdapter.php b/src/Components/DfWebDavAdapter.php index a0419c6..5166321 100644 --- a/src/Components/DfWebDavAdapter.php +++ b/src/Components/DfWebDavAdapter.php @@ -7,15 +7,4 @@ class DfWebDavAdapter extends WebDAVAdapter { - /** - * Ensure a directory exists. - * - * @param string $dirname - */ - public function ensureDirectory($dirname) - { - if ( ! empty($dirname) && ! $this->has($dirname)) { - $this->createDir($dirname, new Config()); - } - } } \ No newline at end of file diff --git a/src/Components/FTPFileSystem.php b/src/Components/FTPFileSystem.php index 5d48388..145f37c 100644 --- a/src/Components/FTPFileSystem.php +++ b/src/Components/FTPFileSystem.php @@ -1,9 +1,20 @@ mimeTypeDetector = new ExtensionMimeTypeDetector(); + } + /** * {@inheritdoc} */ @@ -11,4 +22,14 @@ protected function setAdapter($config) { $this->adapter = new DfFtpAdapter($config); } + + /** + * Override default implementation in order to skip file content reading. + * Detects mime type only by $path. Don't verify file existence. + * @return string Detected mime type or 'text/plain' if type unknown + */ + protected function detectMimeType(string $path) : string + { + return $this->mimeTypeDetector->detectMimeTypeFromPath($path) ?: 'text/plain'; + } } \ No newline at end of file diff --git a/src/Components/LocalFileSystem.php b/src/Components/LocalFileSystem.php index 5c3bdff..63188f4 100644 --- a/src/Components/LocalFileSystem.php +++ b/src/Components/LocalFileSystem.php @@ -8,6 +8,7 @@ use DreamFactory\Core\Exceptions\NotFoundException; use DreamFactory\Core\Exceptions\BadRequestException; use DreamFactory\Core\Exceptions\InternalServerErrorException; +use Illuminate\Support\Arr; /** * Class LocalFileSystem @@ -71,7 +72,7 @@ public function listContainers($include_properties = false) $result = ['name' => $file, 'path' => $file]; if ($include_properties) { $temp = stat($dir); - $result['last_modified'] = gmdate(static::TIMESTAMP_FORMAT, array_get($temp, 'mtime', 0)); + $result['last_modified'] = gmdate(static::TIMESTAMP_FORMAT, Arr::get($temp, 'mtime', 0)); } $out[] = $result; @@ -134,7 +135,7 @@ public function getContainerProperties($container) */ public function createContainer($properties = [], $check_exist = false) { - $container = array_get($properties, 'name', array_get($properties, 'path')); + $container = Arr::get($properties, 'name', Arr::get($properties, 'path')); if (empty($container)) { throw new BadRequestException('No name found for container in create request.'); } @@ -239,7 +240,7 @@ public function deleteContainers($containers, $force = false) foreach ($containers as $key => $folder) { try { // path is full path, name is relative to root, take either - $name = array_get($folder, 'name', trim(array_get($folder, 'path'), '/')); + $name = Arr::get($folder, 'name', trim(Arr::get($folder, 'path'), '/')); if (!empty($name)) { $this->deleteContainer($name, $force); } else { @@ -328,7 +329,7 @@ public function getFolderProperties($container, $path) $out = ['name' => basename($path), 'path' => $path]; $dirPath = static::addContainerToName($container, $path); $temp = stat($dirPath); - $out['last_modified'] = gmdate(static::TIMESTAMP_FORMAT, array_get($temp, 'mtime', 0)); + $out['last_modified'] = gmdate(static::TIMESTAMP_FORMAT, Arr::get($temp, 'mtime', 0)); return $out; } @@ -453,12 +454,12 @@ public function deleteFolders($container, $folders, $root = '', $force = false) foreach ($folders as $key => $folder) { try { // path is full path, name is relative to root, take either - $path = array_get($folder, 'path'); + $path = Arr::get($folder, 'path'); if (!empty($path)) { $dir = static::asFullPath($path); FileUtilities::deleteTree($dir, $force); } else { - $name = array_get($folder, 'name'); + $name = Arr::get($folder, 'name'); if (!empty($name)) { $path = $root . $name; $this->deleteFolder($container, $path, $force); @@ -549,8 +550,8 @@ public function getFileProperties($container, $path, $include_content = false, $ 'path' => $path, 'name' => $shortName, 'content_type' => FileUtilities::determineContentType($ext, '', $file), - 'last_modified' => gmdate('D, d M Y H:i:s \G\M\T', array_get($temp, 'mtime', 0)), - 'content_length' => array_get($temp, 'size', 0) + 'last_modified' => gmdate('D, d M Y H:i:s \G\M\T', Arr::get($temp, 'mtime', 0)), + 'content_length' => Arr::get($temp, 'size', 0) ]; if ($include_content) { $contents = file_get_contents($file); @@ -744,7 +745,7 @@ public function deleteFiles($container, $files, $root = '') foreach ($files as $key => $fileInfo) { try { // path is full path, name is relative to root, take either - $path = array_get($fileInfo, 'path'); + $path = Arr::get($fileInfo, 'path'); if (!empty($path)) { $file = static::asFullPath($path, true); if (!is_file($file)) { @@ -754,7 +755,7 @@ public function deleteFiles($container, $files, $root = '') throw new InternalServerErrorException("Failed to delete file '$path'."); } } else { - $name = array_get($fileInfo, 'name'); + $name = Arr::get($fileInfo, 'name'); if (!empty($name)) { $path = $root . $name; $this->deleteFile($container, $path); @@ -943,7 +944,7 @@ public static function listTree($root, $prefix = '', $delimiter = '') $stat = stat($key); $out[] = [ 'path' => str_replace(DIRECTORY_SEPARATOR, '/', $local) . '/', - 'last_modified' => gmdate('D, d M Y H:i:s \G\M\T', array_get($stat, 'mtime', 0)) + 'last_modified' => gmdate('D, d M Y H:i:s \G\M\T', Arr::get($stat, 'mtime', 0)) ]; if (empty($delimiter)) { $out = array_merge($out, static::listTree($root, $local . DIRECTORY_SEPARATOR)); @@ -954,8 +955,8 @@ public static function listTree($root, $prefix = '', $delimiter = '') $out[] = [ 'path' => str_replace(DIRECTORY_SEPARATOR, '/', $local), 'content_type' => FileUtilities::determineContentType($ext, '', $key), - 'last_modified' => gmdate('D, d M Y H:i:s \G\M\T', array_get($stat, 'mtime', 0)), - 'content_length' => array_get($stat, 'size', 0) + 'last_modified' => gmdate('D, d M Y H:i:s \G\M\T', Arr::get($stat, 'mtime', 0)), + 'content_length' => Arr::get($stat, 'size', 0) ]; } else { error_log($key); diff --git a/src/Components/RemoteFileSystem.php b/src/Components/RemoteFileSystem.php index d786756..11fb5c4 100644 --- a/src/Components/RemoteFileSystem.php +++ b/src/Components/RemoteFileSystem.php @@ -8,6 +8,8 @@ use DreamFactory\Core\Exceptions\NotFoundException; use DreamFactory\Core\Exceptions\BadRequestException; use DreamFactory\Core\Exceptions\InternalServerErrorException; +use DreamFactory\Core\Exceptions\NotImplementedException; +use Illuminate\Support\Arr; /** * Class RemoteFileSystem @@ -86,7 +88,7 @@ public function deleteContainers($containers, $force = false) foreach ($containers as $key => $folder) { try { // path is full path, name is relative to root, take either - $name = array_get($folder, 'name', trim(array_get($folder, 'path'), '/')); + $name = Arr::get($folder, 'name', trim(Arr::get($folder, 'path'), '/')); if (!empty($name)) { $this->deleteContainer($name, $force); } else { @@ -144,7 +146,7 @@ public function getFolder($container, $path, $include_files = true, $include_fol } $results = $this->listBlobs($container, $path, $delimiter); foreach ($results as $data) { - $fullPathName = array_get($data, 'name'); + $fullPathName = Arr::get($data, 'name'); $data['path'] = $fullPathName; $data['name'] = rtrim(substr($fullPathName, strlen($path)), '/'); if ('/' == substr($fullPathName, -1)) { @@ -268,7 +270,7 @@ public function copyFolder($container, $dest_path, $src_container, $src_path, $c $blobs = $this->listBlobs($src_container, $src_path); if (!empty($blobs)) { foreach ($blobs as $blob) { - $srcName = array_get($blob, 'name'); + $srcName = Arr::get($blob, 'name'); if ((0 !== strcasecmp($src_path, $srcName))) { // not self properties blob $name = FileUtilities::getNameFromPath($srcName); @@ -322,7 +324,7 @@ public function deleteFolder($container, $path, $force = false, $content_only = throw new BadRequestException("Folder '$path' contains other files or folders."); } foreach ($blobs as $blob) { - $name = array_get($blob, 'name'); + $name = Arr::get($blob, 'name'); $this->deleteBlob($container, $name); } } @@ -347,8 +349,8 @@ public function deleteFolders($container, $folders, $root = '', $force = false) foreach ($folders as $key => $folder) { try { // path is full path, name is relative to root, take either - $path = array_get($folder, 'path'); - $name = array_get($folder, 'name'); + $path = Arr::get($folder, 'path'); + $name = Arr::get($folder, 'name'); if (!empty($path)) { $path = static::removeContainerFromPath($container, $path); } elseif (!empty($name)) { @@ -460,7 +462,60 @@ public function getFileProperties($container, $path, $include_content = false, $ public function streamFile($container, $path, $download = false) { $params = ($download) ? ['disposition' => 'attachment'] : []; - $this->streamBlob($container, $path, $params); + try { + $this->streamBlob($container, $path, $params); + \Log::warning('Using deprecated method - "' . __FUNCTION__ . '"'); + } catch (NotImplementedException $e) { + $this->streamBlobIfModified($container, $path, $params); + } + } + + private function streamBlobIfModified($container, $path, $params) + { + try { + $properties = $this->getBlobProperties($container, $path); + + header('Cache-Control: no-cache, private'); + header('Last-Modified: ' . $properties['last_modified']); + header('Content-Type: ' . $properties['content_type']); + + $disposition = + (isset($params['disposition']) && !empty($params['disposition'])) ? $params['disposition'] + : 'inline'; + + header('Content-Disposition: ' . $disposition . '; filename="' . $properties['name'] . '";'); + + if ($this->isNotModified($properties['last_modified'])) { + header('HTTP/1.1 304 Not Modified'); + return; + } + + header('Content-Length:' . $properties['content_length']); + $chunkSize = \Config::get('df.file_chunk_size'); + foreach ($this->getBlobInChunks($container, $path, $chunkSize) as $dataChunk) { + print($dataChunk); + flush(); + } + } catch (NotFoundException $ex) { + header('HTTP/1.1 404 Not Found'); + echo 'Could not open the specified file ' . $path; + echo $ex->getMessage(); + } catch (\Exception $ex) { + header('HTTP/1.1 500 Internal Server Error'); + \Log::error('Failed to stream file: ' . $path); + \Log::error($ex->getMessage()); + \Log::error($ex->getTraceAsString()); + } + } + + private function isNotModified(string $lastModified): bool + { + $ifModifiedSince = Arr::get($_SERVER, 'HTTP_IF_MODIFIED_SINCE'); + return ( + !empty($lastModified) && + !empty($ifModifiedSince) && + strtotime($ifModifiedSince) === strtotime($lastModified) + ); } /** @@ -616,8 +671,8 @@ public function deleteFiles($container, $files, $root = '') foreach ($files as $key => $file) { try { // path is full path, name is relative to root, take either - $path = array_get($file, 'path'); - $name = array_get($file, 'name'); + $path = Arr::get($file, 'path'); + $name = Arr::get($file, 'name'); if (!empty($path)) { $path = static::removeContainerFromPath($container, $path); } elseif (!empty($name)) { @@ -680,7 +735,7 @@ public function getFolderAsZip($container, $path, $zip = null, $zipFileName = '' $zip->addEmptyDir($path); } else { foreach ($results as $blob) { - $fullPathName = array_get($blob, 'name'); + $fullPathName = Arr::get($blob, 'name'); $shortName = substr_replace($fullPathName, '', 0, strlen($path)); if (empty($shortName)) { continue; @@ -767,8 +822,8 @@ protected function listResource($includeProperties = false) $result = $this->getFolder($this->container, '', true, true, false, $includeProperties); - $folders = array_get($result, 'folder', []); - $files = array_get($result, 'file', []); + $folders = Arr::get($result, 'folder', []); + $files = Arr::get($result, 'file', []); foreach ($folders as $folder) { $folder['path'] = trim($folder['path'], '/'); @@ -891,10 +946,28 @@ abstract public function getBlobProperties($container, $name); * @param string $container * @param string $name * @param array $params + * @deprecated streamBlob is deprecated. + * Extenders must implement `getBlobInChunks` instead. * * @throws \Exception */ - abstract public function streamBlob($container, $name, $params = []); + public function streamBlob($container, $name, $params = []) + { + throw new NotImplementedException('Use getBlobInChunks instead'); + } + + /** + * @param string $container + * @param string $name + * @param int $chunkSize + * + * @return \Generator of blob chunks of `$chunkSize` + * + */ + protected function getBlobInChunks($container, $name, $chunkSize): \Generator + { + throw new NotImplementedException('Implement this method instead of streamBlob'); + } /** * @param string $container diff --git a/src/Components/SFTPFileSystem.php b/src/Components/SFTPFileSystem.php index 4baff0b..ea50866 100644 --- a/src/Components/SFTPFileSystem.php +++ b/src/Components/SFTPFileSystem.php @@ -3,6 +3,7 @@ namespace DreamFactory\Core\File\Components; use DreamFactory\Core\Exceptions\NotFoundException; +use Illuminate\Support\Arr; class SFTPFileSystem extends FTPFileSystem { @@ -17,55 +18,4 @@ protected function setAdapter($config) unset($config['host_fingerprint']); $this->adapter = new DFSftpAdapter($config); } - - /** - * {@inheritdoc} - */ - public function getFolder($container, $path, $include_files = true, $include_folders = true, $full_tree = false) - { - if ($this->folderExists($container, $path)) { - $path = rtrim($path, '/'); - $contents = $this->adapter->listContents($path, $full_tree); - foreach ($contents as $key => $content) { - if (strtolower(array_get($content, 'type')) === 'dir') { - $this->normalizeFolderInfo($content, $path); - } else { - $this->normalizeFileInfo($content, $path); - } - $contents[$key] = $content; - } - - return $contents; - } else { - throw new NotFoundException("Folder '$path' does not exist in storage."); - } - } - - /** - * @param array $folder - * @param string $localizer - * - * @throws \DreamFactory\Core\Exceptions\InternalServerErrorException - */ - protected function normalizeFolderInfo(array & $folder, $localizer) - { - if(empty(array_get($folder, 'type')) && !empty(array_get($folder, 'path'))){ - $folder['type'] = 'dir'; - } - parent::normalizeFolderInfo($folder, $localizer); - $folder['last_modified'] = gmdate('D, d M Y H:i:s \G\M\T', array_get($folder, 'timestamp', 0)); - unset($folder['timestamp']); - } - - /** - * @param array $file - * @param string $localizer - * - * @throws \DreamFactory\Core\Exceptions\InternalServerErrorException - */ - protected function normalizeFileInfo(array &$file, $localizer) - { - parent::normalizeFileInfo($file, $localizer); - unset($file['size'], $file['timestamp']); - } } \ No newline at end of file diff --git a/src/Components/WebDAVFileSystem.php b/src/Components/WebDAVFileSystem.php index 4cae501..42c8843 100644 --- a/src/Components/WebDAVFileSystem.php +++ b/src/Components/WebDAVFileSystem.php @@ -2,8 +2,8 @@ namespace DreamFactory\Core\File\Components; -use DreamFactory\Core\Exceptions\NotFoundException; use Sabre\DAV\Client; +use Illuminate\Support\Arr; class WebDAVFileSystem extends BaseFlysystem { @@ -12,54 +12,7 @@ class WebDAVFileSystem extends BaseFlysystem */ protected function setAdapter($config) { - $this->adapter = new DfWebDavAdapter(new Client($config), array_get($config, 'path_prefix')); + $this->adapter = new DfWebDavAdapter(new Client($config), Arr::get($config, 'path_prefix')); } - /** - * {@inheritdoc} - */ - public function getFolder($container, $path, $include_files = true, $include_folders = true, $full_tree = false) - { - if ($this->folderExists($container, $path)) { - $path = rtrim($path, '/'); - $contents = $this->adapter->listContents($path, $full_tree); - foreach ($contents as $key => $content) { - if (strtolower(array_get($content, 'type')) === 'dir') { - $this->normalizeFolderInfo($content, $path); - } else { - $this->normalizeFileInfo($content, $path); - } - $contents[$key] = $content; - } - - return $contents; - } else { - throw new NotFoundException("Folder '$path' does not exist in storage."); - } - } - - /** - * @param array $folder - * @param string $localizer - * - * @throws \DreamFactory\Core\Exceptions\InternalServerErrorException - */ - protected function normalizeFolderInfo(array & $folder, $localizer) - { - parent::normalizeFolderInfo($folder, $localizer); - $folder['last_modified'] = gmdate('D, d M Y H:i:s \G\M\T', array_get($folder, 'timestamp', 0)); - unset($folder['timestamp']); - } - - /** - * @param array $file - * @param string $localizer - * - * @throws \DreamFactory\Core\Exceptions\InternalServerErrorException - */ - protected function normalizeFileInfo(array &$file, $localizer) - { - parent::normalizeFileInfo($file, $localizer); - unset($file['size'], $file['timestamp']); - } } \ No newline at end of file diff --git a/src/Services/BaseFileService.php b/src/Services/BaseFileService.php index d85f2df..21a48f5 100644 --- a/src/Services/BaseFileService.php +++ b/src/Services/BaseFileService.php @@ -59,7 +59,7 @@ public function __construct($settings = []) ]; parent::__construct($settings); - $this->publicPaths = (array)array_get($this->config, 'public_path'); + $this->publicPaths = (array)Arr::get($this->config, 'public_path'); $this->setDriver($this->config); } @@ -537,7 +537,7 @@ protected function handlePOST() if (1 < count($files)) { throw new BadRequestException("Multiple files uploaded to a single REST resource '$name'."); } - $file = array_get($files, 0); + $file = Arr::get($files, 0); if (empty($file)) { throw new BadRequestException("No file uploaded to REST resource '$name'."); } @@ -799,12 +799,12 @@ protected function handleFolderContentFromData( $out = []; if (!empty($data) && !Arr::isAssoc($data)) { foreach ($data as $key => $resource) { - switch (array_get($resource, 'type')) { + switch (Arr::get($resource, 'type')) { case 'folder': - $name = array_get($resource, 'name', ''); - $srcPath = array_get($resource, 'source_path'); + $name = Arr::get($resource, 'name', ''); + $srcPath = Arr::get($resource, 'source_path'); if (!empty($srcPath)) { - $srcContainer = array_get($resource, 'source_container', $this->container); + $srcContainer = Arr::get($resource, 'source_container', $this->container); // copy or move if (empty($name)) { $name = FileUtilities::getNameFromPath($srcPath); @@ -823,7 +823,7 @@ protected function handleFolderContentFromData( } } else { $fullPathName = $this->folderPath . $name . '/'; - $content = array_get($resource, 'content', ''); + $content = Arr::get($resource, 'content', ''); $isBase64 = array_get_bool($resource, 'is_base64'); if ($isBase64) { $content = base64_decode($content); @@ -837,11 +837,11 @@ protected function handleFolderContentFromData( } break; case 'file': - $name = array_get($resource, 'name', ''); - $srcPath = array_get($resource, 'source_path'); + $name = Arr::get($resource, 'name', ''); + $srcPath = Arr::get($resource, 'source_path'); if (!empty($srcPath)) { // copy or move - $srcContainer = array_get($resource, 'source_container', $this->container); + $srcContainer = Arr::get($resource, 'source_container', $this->container); if (empty($name)) { $name = FileUtilities::getNameFromPath($srcPath); } @@ -859,7 +859,7 @@ protected function handleFolderContentFromData( } elseif (isset($resource['content'])) { $fullPathName = $this->folderPath . $name; $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'file']; - $content = array_get($resource, 'content', ''); + $content = Arr::get($resource, 'content', ''); $isBase64 = array_get_bool($resource, 'is_base64'); if ($isBase64) { $content = base64_decode($content); @@ -892,8 +892,8 @@ protected function deleteFolderContent($data, $root = '', $force = false) $out = []; if (!empty($data)) { foreach ($data as $key => $resource) { - $path = array_get($resource, 'path'); - $name = array_get($resource, 'name'); + $path = Arr::get($resource, 'path'); + $name = Arr::get($resource, 'name'); if (!empty($path)) { $fullPath = $path; @@ -905,7 +905,7 @@ protected function deleteFolderContent($data, $root = '', $force = false) } } - switch (array_get($resource, 'type')) { + switch (Arr::get($resource, 'type')) { case 'file': $out[$key] = ['name' => $name, 'path' => $path, 'type' => 'file']; try { diff --git a/src/Services/FTPFileService.php b/src/Services/FTPFileService.php index 0976278..8d16524 100644 --- a/src/Services/FTPFileService.php +++ b/src/Services/FTPFileService.php @@ -5,12 +5,13 @@ use DreamFactory\Core\File\Components\FTPFileSystem; use DreamFactory\Core\Exceptions\InternalServerErrorException; +use Illuminate\Support\Arr; class FTPFileService extends RemoteFileService { protected function setDriver($config) { - $this->container = array_get($config, 'container'); + $this->container = Arr::get($config, 'container'); $config['root'] = $this->container; if (empty($this->container)) { diff --git a/src/Services/LocalFileService.php b/src/Services/LocalFileService.php index c2bc598..2388e8f 100644 --- a/src/Services/LocalFileService.php +++ b/src/Services/LocalFileService.php @@ -5,6 +5,7 @@ use DreamFactory\Core\File\Components\LocalFileSystem; use DreamFactory\Core\Exceptions\InternalServerErrorException; use DreamFactory\Core\Utility\Session; +use Illuminate\Support\Arr; class LocalFileService extends BaseFileService { @@ -26,7 +27,7 @@ protected static function isRelativePath($path) protected function setDriver($config) { $this->container = ''; - $root = array_get($config, 'container'); + $root = Arr::get($config, 'container'); // Replace any private lookups Session::replaceLookups($root, true); // local is the old Laravel config "disk" that may still be configured diff --git a/src/Services/SFTPFileService.php b/src/Services/SFTPFileService.php index 68b4985..b4aa0ed 100644 --- a/src/Services/SFTPFileService.php +++ b/src/Services/SFTPFileService.php @@ -4,12 +4,13 @@ use DreamFactory\Core\Exceptions\InternalServerErrorException; use DreamFactory\Core\File\Components\SFTPFileSystem; +use Illuminate\Support\Arr; class SFTPFileService extends RemoteFileService { protected function setDriver($config) { - $this->container = array_get($config, 'container'); + $this->container = Arr::get($config, 'container'); $config['root'] = $this->container; if (empty($this->container)) { diff --git a/src/Services/WebDAVFileService.php b/src/Services/WebDAVFileService.php index a6ac1b1..9e8e17b 100644 --- a/src/Services/WebDAVFileService.php +++ b/src/Services/WebDAVFileService.php @@ -4,14 +4,15 @@ use DreamFactory\Core\Exceptions\InternalServerErrorException; use DreamFactory\Core\File\Components\WebDAVFileSystem; +use Illuminate\Support\Arr; class WebDAVFileService extends RemoteFileService { protected function setDriver($config) { - $this->container = array_get($config, 'container'); + $this->container = Arr::get($config, 'container'); - if (empty(array_get($config, 'base_uri'))) { + if (empty(Arr::get($config, 'base_uri'))) { throw new InternalServerErrorException( 'WebDAV base URI not specified. Please check configuration for file service - ' . $this->name @@ -19,24 +20,24 @@ protected function setDriver($config) } $settings = [ - 'baseUri' => array_get($config, 'base_uri') + 'baseUri' => Arr::get($config, 'base_uri') ]; - if($username = array_get($config, 'username', false)){ + if($username = Arr::get($config, 'username', false)){ $settings['userName'] = $username; } - if($password = array_get($config, 'password', false)){ + if($password = Arr::get($config, 'password', false)){ $settings['password'] = $password; } - if($authType = array_get($config, 'auth_type', false)){ + if($authType = Arr::get($config, 'auth_type', false)){ $settings['authType'] = $authType; } - if($encoding = array_get($config, 'encoding', false)){ + if($encoding = Arr::get($config, 'encoding', false)){ $settings['encoding'] = $encoding; } - if($proxy = array_get($config, 'proxy', false)){ + if($proxy = Arr::get($config, 'proxy', false)){ $settings['proxy'] = $proxy; } - $settings['path_prefix'] = array_get($config, 'container'); + $settings['path_prefix'] = Arr::get($config, 'container'); $this->driver = new WebDAVFileSystem($settings); } } \ No newline at end of file