commit 349fa9c003473a7c101c3a06926e9cf782f2880f Author: Mèir Noordermeer Date: Thu Mar 1 18:41:55 2018 +0100 Initial commit IQParts\Content diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7247644 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.idea/ +composer.lock +vendor/ +tmp/ +.vs/ +.vscode/ +coverage/ \ No newline at end of file diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..7c7c845 --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,10 @@ +filter: + paths: ["src/*"] +tools: + external_code_coverage: true + php_code_coverage: true + php_sim: true + php_mess_detector: true + php_pdepend: true + php_analyzer: true + php_cpd: true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dd43ee9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 Industrial Quality Parts + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..b25f202 --- /dev/null +++ b/Readme.md @@ -0,0 +1,42 @@ +# IQParts/Content + +### Installation +`composer require iqparts/content` + +### Content +This library is a small wrapper around [League\Flysystem](https://github.com/thephpleague/flysystem). +This library lets you do anything that Flysystem already can, but uses its own objects with some extra +functions. + +### Resolver +This library also includes an AssetResolver which returns a PSR-7 response if the file exists +on the filesystem. + +### Usage +``` php +// Filesystem +$fs = new IQParts\Content\Filesystem( + new League\Flysystem\AdapterInterface(), + new League\Flysystem\Config() +); + +// AssetResolver +$resolver = new AssetResolver($fs); + +if ($resolver->exists('my-file.txt') { + // PSR-7 $response + $response = $resolver->resolve( + $request, // PSR-7 request + $response, // PSR-7 response + $cacheControl // CacheControl object, + $inline // Inline or as attachment + ); +} + +// MimeDetector +$mimetype = (new MimeDetector([ + 'my-mime' => 'my-mimetype' +]))->detectFromExtension('document.pdf'); +$mimetype = 'application/pdf' + +``` \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8d15f4d --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "name": "iqparts/content", + "description": "Small wrapper around League/Flysystem with an added AssetResolver for resolving files as responses.", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Mèir Noordermeer", + "email": "meirnoordermeer@me.com" + } + ], + "require": { + "league/flysystem": "^1.0", + "psr/http-message": "^1.0", + "guzzlehttp/psr7": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "autoload": { + "psr-4": { + "IQParts\\Content\\": ["src"] + } + }, + "autoload-dev": { + "psr-4": { + "IQParts\\ContentTest\\": ["test"] + } + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..4652666 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,18 @@ + + + + ./test/Unit + + + + ./src + + + diff --git a/src/AssetResolver.php b/src/AssetResolver.php new file mode 100644 index 0000000..a4a3b45 --- /dev/null +++ b/src/AssetResolver.php @@ -0,0 +1,86 @@ +detector = new MimeDetector(); + $this->filesystem = $filesystem ; + } + + /** + * @param string $file + * @return bool + */ + public function exists(string $file): bool + { + // Will never throw as we check if the file exists. + return $this->filesystem->exists($file) && $this->filesystem->get($file) instanceof File; + } + + /** + * @param RequestInterface $request + * @param ResponseInterface $response + * @param CacheControl $cacheControl + * @param bool $inline + * @return ResponseInterface|bool + * @throws FileNotFoundException + */ + public function resolve( + RequestInterface $request, + ResponseInterface $response, + CacheControl $cacheControl, + bool $inline = true + ) + { + $target = $request->getRequestTarget(); + + $file = $this->filesystem->get($target); + + /** @var ResponseInterface $response */ + $response = $response->withHeader('Cache-Control', (string) $cacheControl); + $response = $response->withHeader('Content-Type', $this->detector->detectFromExtension( + $file->getBasename()) + ); + + if ($inline) { + $response = $response->withHeader('Content-Disposition', 'inline; filename="' . $file->getBasename() . '";'); + } else { + $response = $response->withHeader('Content-Disposition', 'attachment; filename="' . $file->getBasename() . '";'); + } + $response = $response->withHeader('Content-Length', (string)$file->getSize()); + + /** + * Will always return a stream + */ + $stream = $file->getStream(); + return $response->withBody(new Stream($stream))->withStatus(200); + } +} \ No newline at end of file diff --git a/src/ContentProviderInterface.php b/src/ContentProviderInterface.php new file mode 100644 index 0000000..15407c2 --- /dev/null +++ b/src/ContentProviderInterface.php @@ -0,0 +1,56 @@ +addPlugin(new ListPaths()); + $this->flySystem = $flySystem; + } + + /** + * @param string $path + * @return bool + */ + public function exists(string $path): bool + { + if ($path === '/') $path = ''; + return $this->flySystem->has($path); + } + + /** + * @param string $path + * @return File|Folder|false + * @throws FileNotFoundException + */ + public function get(string $path) + { + /** + * If path = '/' -> create a folder object with all dirs and folders as children + */ + if ($path === '/' || $path === '') { + $dirs = []; + $files = []; + $paths = $this->flySystem->listPaths('', false); + foreach ($paths as $item) { + /** @var File|Directory $i */ + $i = $this->flySystem->get($item); + if ($i->getType() === 'dir') { + $dirs[] = base64_encode($i->getPath()); + } else { + $files[] = base64_encode($i->getPath()); + } + } + + return Folder::fromVariables(base64_encode('/'), '/', '', null, $dirs, $files); + } else { + $item = $this->flySystem->get($path); + if ($item instanceof Directory) { + return new Folder($item); + } else { + return new File($item); + } + } + } + + /** + * @param string $path + * @return bool|false|resource + */ + public function getStream(string $path) + { + return $this->flySystem->readStream($path); + } + + /** + * @param string $path + * @param bool $recursive + * @return \Iterator|Folder[]|File[] + */ + public function listDirContents(string $path, bool $recursive = false): \Iterator + { + if ($path === '/') $path = ''; + foreach ($this->flySystem->listPaths($path, $recursive) as $path) { + $item = $this->flySystem->get($path); + if ($item instanceof Directory) { + yield new Folder($item); + } else { + yield new File($item); + } + } + } + + /** + * @param string $path + * @param bool $recursive + * @return \Iterator|Folder[] + */ + public function listDirs(string $path, bool $recursive = false): \Iterator + { + if ($path === '/') $path = ''; + foreach ($this->flySystem->listPaths($path, $recursive) as $path) { + $item = $this->flySystem->get($path); + if ($item instanceof Directory) { + yield new Folder($item); + } + } + } + + /** + * @param string $path + * @param bool $recursive + * @return \Iterator|File[] + */ + public function listFiles(string $path, bool $recursive = false): \Iterator + { + if ($path === '/') $path = ''; + foreach ($this->flySystem->listContents($path, $recursive) as $i) { + $item = $this->flySystem->get($i['path']); + if ($item instanceof \League\Flysystem\File) yield new File($item); + } + } + + /** + * @param string $path + * @return bool + */ + public function delete(string $path) + { + return $this->flySystem->delete($path); + } + + /** + * @param string $path + * @return bool + */ + public function deleteDir(string $path) + { + return $this->flySystem->deleteDir($path); + } + + /** + * @param string $path + * @param array $config + * @return bool + */ + public function createDir(string $path, array $config = []) + { + return $this->flySystem->createDir($path, $config); + } + + /** + * @param string $path + * @param $contents + * @param array $config + * @return bool + */ + public function put(string $path, $contents, array $config = []): bool + { + return $this->flySystem->put($path, $contents, $config); + } + + /** + * @param string $path + * @param resource $contents + * @param array $config + * @return bool + */ + public function putStream(string $path, $contents, array $config = []): bool + { + return $this->flySystem->putStream($path, $contents, $config); + } + + /** + * @return FlySystem + */ + public function getFlySystem(): FlySystem + { + return $this->flySystem; + } +} \ No newline at end of file diff --git a/src/MimeDetector.php b/src/MimeDetector.php new file mode 100644 index 0000000..074ce28 --- /dev/null +++ b/src/MimeDetector.php @@ -0,0 +1,162 @@ +mimeTypes = array_merge( + [ + 'doc' => 'application/msword', + 'dot' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', + 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', + 'xls' => 'application/vnd.ms-excel', + 'xlt' => 'application/vnd.ms-excel', + 'xla' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', + 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pot' => 'application/vnd.ms-powerpoint', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppa' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', + 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', + 'mdb' => 'application/vnd.ms-access', + 'aac' => 'audio/aac', + 'abw' => 'application/x-abiword', + 'arc' => 'application/octet-stream', + 'avi' => 'video/x-msvideo', + 'azw' => 'application/vnd.amazon.ebook', + 'bin' => 'application/octet-stream', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'csh' => 'application/x-csh', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'eot' => 'application/vnd.ms-fontobject', + 'epub' => 'application/epub+zip', + 'gif' => 'image/gif', + 'htm' => 'text/html', + 'html' => 'text/html', + 'twig' => 'text/html', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'jar' => 'application/java-archive', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpg', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpeg' => 'video/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'otf' => 'font/otf', + 'png' => 'image/png', + 'pdf' => 'application/pdf', + 'rar' => 'application/x-rar-compressed', + 'rtf' => 'application/rtf', + 'sh' => 'application/x-sh', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'tar' => 'application/x-tar', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'ts' => 'application/typescript', + 'txt' => 'text/plain', + 'ttf' => 'font/ttf', + 'vsd' => 'application/vnd.visio', + 'wav' => 'audio/x-wav', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'woff' => 'font/woff', + 'woff2' => 'font/woff2', + 'xhtml' => 'application/xhtml+xml', + 'xml' => 'application/xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'zip' => 'application/zip', + '3gp' => 'video/3gpp', + '3g2' => 'video/3gpp2', + '7z' => 'application/x-7z-compressed' + ], + $extraMimes + ); + } + + /** + * @param string $filename + * @return string + */ + public function detectFromExtension(string $filename): string + { + return $this->mimeTypes[$this->getExtension($filename)] ?? 'text/plain'; + } + + /** + * @param string $filename + * @return bool + */ + public function isText(string $filename): bool + { + $mime = $this->detectFromExtension($filename); + if (fnmatch('text/*', $mime)) { + return true; + } + + $text = [ + "application/javascript" => true, + "application/rtf" => true, + "application/json" => true, + "application/x-sh" => true, + "application/xml" => true + ]; + return isset($text[$mime]); + } + + /** + * @param $filename + * @return string + */ + private function getExtension($filename): string + { + $file = basename($filename); + $dotPlace = strrpos($file, '.'); + if ($dotPlace === false) { + return $file; + } + return substr($file, $dotPlace + 1); + } + +} \ No newline at end of file diff --git a/src/Object/CacheControl.php b/src/Object/CacheControl.php new file mode 100644 index 0000000..065f5fe --- /dev/null +++ b/src/Object/CacheControl.php @@ -0,0 +1,74 @@ + 7) { + throw new \InvalidArgumentException('Invalid mode.'); + } + + if ($mode >= 7 && $seconds < 0) { + throw new \InvalidArgumentException('Invalid number of seconds.'); + } + + $this->mode = $mode; + $this->seconds = $seconds; + } + + /** + * @return int|string + */ + public function __toString() + { + $mode = $this->modes[$this->mode]; + + if ($this->mode >= 7) { + return $mode . '=' . (string)$this->seconds; + } + + return $mode; + } +} \ No newline at end of file diff --git a/src/Object/File.php b/src/Object/File.php new file mode 100644 index 0000000..a6bfe7e --- /dev/null +++ b/src/Object/File.php @@ -0,0 +1,252 @@ + true, + "css" => true, + "html" => true, + "svg" => true + ]; + + $this->file = $file; + $this->path = $file->getPath(); + $this->size = $file->getSize(); + $this->basename = basename($file->getPath()); + $this->date = new \DateTime(); + $timestamp = $file->getTimestamp(); + $this->date->setTimestamp($timestamp); + $this->timestamp = $timestamp; + + $dotPlace = strrpos($this->basename, '.'); + if ($dotPlace !== false) $extension = substr($this->basename, $dotPlace + 1); + else $extension = ''; + $this->extension = $extension; + + $parentSep = strrpos($this->path, '/'); + if ($parentSep !== false) { + $parent = substr($this->path, 0, $parentSep); + } else $parent = '/'; + $this->parent = base64_encode($parent); + $this->isEditable = $editable[$extension] ?? false; + } + } + + /** + * @return string + */ + public function getId(): string + { + return base64_encode($this->path); + } + + /** + * @return FlyFile + */ + public function getFile(): FlyFile + { + return $this->file; + } + + /** + * @return string + */ + public function getPath(): string + { + return $this->path; + } + + /** + * @return int + */ + public function getSize(): int + { + return $this->size; + } + + /** + * @return string + */ + public function getBasename(): string + { + return $this->basename; + } + + /** + * @return string + */ + public function getExtension() + { + return $this->extension; + } + + /** + * @return string + */ + public function getParent() + { + return $this->parent; + } + + /** + * @return \DateTime + */ + public function getDate(): \DateTime + { + return $this->date; + } + + /** + * @return int + */ + public function getTimestamp() + { + return $this->timestamp; + } + + /** + * @return string + */ + public function getMD5(): string + { + return md5($this->file->read()); + } + + /** + * @return false|string + */ + public function getContents() + { + return $this->file->read(); + } + + /** + * @return false|resource + */ + public function getStream() + { + return $this->file->readStream(); + } + + /** + * @return false|string + */ + public function getMimetype() + { + return $this->file->getMimetype(); + } + + /** + * @return bool + */ + public function isEditable(): bool + { + return (new MimeDetector())->isText($this->basename); + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + 'basename' => $this->getBasename(), + 'contents' => $this->isEditable() ? $this->getContents() : null, + 'date' => $this->getDate()->format(DATE_ISO8601), + 'extension' => $this->getExtension(), + 'id' => $this->getId(), + 'isEditable' => $this->isEditable(), + 'mimetype' => $this->getMimetype(), + 'md5' => $this->getMD5(), + 'parent' => $this->getParent(), + 'path' => $this->getPath(), + 'size' => $this->getSize(), + 'timestamp' => $this->getTimestamp() + ]; + } + + /** + * @return bool + */ + function isDir(): bool + { + return false; + } + + /** + * @return bool + */ + function isFile(): bool + { + return true; + } + + /** + * @param string $path + * @param string $parent + * @param int $timestamp + * @return File + */ + public static function fromVariables(string $path, string $parent, int $timestamp): self + { + $file = new self; + $file->path = $path; + $file->parent = $parent; + $file->timestamp = $timestamp; + return $file; + } +} \ No newline at end of file diff --git a/src/Object/Folder.php b/src/Object/Folder.php new file mode 100644 index 0000000..9da3b4d --- /dev/null +++ b/src/Object/Folder.php @@ -0,0 +1,187 @@ +directory = $directory; + $this->path = $directory->getPath(); + $filesystem = $directory->getFilesystem(); + + $parentSep = strrpos($this->path, '/'); + if ($parentSep !== false) { + $parent = substr($this->path, 0, $parentSep); + } else $parent = '/'; + $this->parent = base64_encode($parent); + $this->basename = basename($this->path); + + $paths = $filesystem->listContents($this->path); + $dirs = []; + $files = []; + + foreach ($paths as $path) { + $item = $filesystem->get($path['path']); + if ($item instanceof Directory) { + $dirs[] = base64_encode($item->getPath()); + } else { + $files[] = base64_encode($item->getPath()); + } + } + $this->dirs = $dirs; + $this->files = $files; + } + } + + /** + * @return Directory + */ + public function getDirectory(): Directory + { + return $this->directory; + } + + /** + * @return string + */ + public function getId(): string + { + return base64_encode($this->getPath()); + } + + /** + * @return string + */ + public function getPath(): string + { + return $this->path; + } + + /** + * @return string|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * @return string + */ + public function getBasename(): string + { + return $this->basename; + } + + /** + * @return array + */ + public function getDirs(): array + { + return $this->dirs; + } + + /** + * @return array + */ + public function getFiles(): array + { + return $this->files; + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + 'id' => $this->getId(), + 'path' => $this->getPath(), + 'basename' => $this->getBasename(), + 'parent' => $this->getParent(), + 'dirs' => $this->getDirs(), + 'files' => $this->getFiles() + ]; + } + + /** + * @return bool + */ + function isDir(): bool + { + return true; + } + + /** + * @return bool + */ + function isFile(): bool + { + return false; + } + + /** + * @param string $id + * @param string $path + * @param string $basename + * @param string|null $parent + * @param array $dirs + * @param array $files + * @return Folder + */ + public static function fromVariables( + string $id, + string $path, + string $basename, + $parent = null, + array $dirs = [], + array $files = [] + ): Folder + { + $dir = new self(); + $dir->id = $id; + $dir->path = $path; + $dir->basename = $basename; + $dir->parent = $parent; + $dir->dirs = $dirs; + $dir->files = $files; + return $dir; + } +} \ No newline at end of file diff --git a/src/Object/Item.php b/src/Object/Item.php new file mode 100644 index 0000000..48ad25d --- /dev/null +++ b/src/Object/Item.php @@ -0,0 +1,20 @@ +assertTrue($resolver->exists('test.txt')); + $this->assertFalse($resolver->exists('folder')); + + + $request = $this->createMock(RequestInterface::class); + $request + ->expects($this->at(0)) + ->method('getRequestTarget') + ->willReturn('/test.txt'); + + $response = new Response(); + /** @var RequestInterface $request */ + $response = $resolver->resolve($request, $response, new CacheControl(CacheControl::PRIVATE)); + + $this->assertEquals('private', $response->getHeader('Cache-Control')[0]); + $this->assertEquals('text/plain', $response->getHeader('Content-Type')[0]); + $this->assertEquals('inline; filename="test.txt";', $response->getHeader('Content-Disposition')[0]); + $this->assertEquals('0', $response->getHeader('Content-Length')[0]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertInstanceOf(Stream::class, $response->getBody()); + + $request = $this->createMock(RequestInterface::class); + $request + ->expects($this->at(0)) + ->method('getRequestTarget') + ->willReturn('/does-not-exist.txt'); + + $this->expectException(FileNotFoundException::class); + $resolver->resolve($request, $response, new CacheControl(CacheControl::PRIVATE)); + } + + /** + * @test + */ + public function testAttachment() + { + $fs = new Filesystem(new Local(__DIR__ . '/../files')); + + $resolver = new AssetResolver($fs); + + $request = $this->createMock(RequestInterface::class); + $request + ->expects($this->at(0)) + ->method('getRequestTarget') + ->willReturn('/test.txt'); + + $response = new Response(); + /** @var RequestInterface $request */ + $response = $resolver->resolve($request, $response, new CacheControl(CacheControl::PRIVATE), false); + + $this->assertEquals('private', $response->getHeader('Cache-Control')[0]); + $this->assertEquals('text/plain', $response->getHeader('Content-Type')[0]); + $this->assertEquals('attachment; filename="test.txt";', $response->getHeader('Content-Disposition')[0]); + $this->assertEquals('0', $response->getHeader('Content-Length')[0]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertInstanceOf(Stream::class, $response->getBody()); + } + +} \ No newline at end of file diff --git a/test/Unit/CacheControlTest.php b/test/Unit/CacheControlTest.php new file mode 100644 index 0000000..bcd4e6f --- /dev/null +++ b/test/Unit/CacheControlTest.php @@ -0,0 +1,51 @@ +assertEquals('max-age=10', (string)$cacheControl); + } + + /** + * @test + */ + public function testCacheControlWithoutSeconds() + { + $cacheControl = new CacheControl(CacheControl::PRIVATE); + + $this->assertEquals('private', (string)$cacheControl); + } + + /** + * @test + */ + public function testCacheControlWithInvalidSeconds() + { + $this->expectException(\InvalidArgumentException::class); + new CacheControl(CacheControl::MAX_AGE, -1); + } + + /** + * @test + */ + public function testCacheControlWithInvalidMode() + { + $this->expectException(\InvalidArgumentException::class); + new CacheControl(-1); + } +} \ No newline at end of file diff --git a/test/Unit/FileTest.php b/test/Unit/FileTest.php new file mode 100644 index 0000000..fdf61a3 --- /dev/null +++ b/test/Unit/FileTest.php @@ -0,0 +1,64 @@ +get('test.txt')); + } else { + return new File($fs->get('folder/folder/no-extension')); + } + } + + /** + * @test + */ + public function testFile() + { + $file = $this->createFile(true); + + $this->assertEquals('txt', $file->getExtension()); + $this->assertEquals('test.txt', $file->getBasename()); + $this->assertEquals(base64_encode('/'), $file->getParent()); + $this->assertTrue(is_int($file->getSize())); + $this->assertEquals(md5(file_get_contents(__DIR__ . '/../files/test.txt')), $file->getMD5()); + $this->assertTrue($file->isEditable()); + $this->assertEquals('', $file->getContents());; + $this->assertTrue(is_integer($file->getTimestamp())); + $this->assertInstanceOf(\DateTime::class, $file->getDate()); + $this->assertEquals('text/plain', $file->getMimetype()); + $this->assertInstanceOf(FlyFile::class, $file->getFile()); + $this->assertEquals(base64_encode('test.txt'), $file->getId()); + $this->assertTrue($file->isFile()); + $this->assertEquals('test.txt', $file->getPath()); + $this->assertTrue(is_resource($file->getStream())); + $this->assertTrue(is_array($file->toArray())); + $this->assertFalse($file->isDir()); + + $file = $this->createFile(false); + $this->assertEquals('', $file->getExtension()); + $this->assertInstanceOf(File::class, File::fromVariables( + 'folder/folder/no-extension', + 'folder/folder', + time() + )); + } +} \ No newline at end of file diff --git a/test/Unit/FilesystemTest.php b/test/Unit/FilesystemTest.php new file mode 100644 index 0000000..c34dd1f --- /dev/null +++ b/test/Unit/FilesystemTest.php @@ -0,0 +1,84 @@ +assertTrue($fs->exists('test.txt')); + $this->assertInstanceOf(File::class, $fs->get('test.txt')); + $this->assertInstanceOf(Folder::class, $fs->get('folder')); + $fs->put('test.txt', 'test'); + $this->assertEquals('test', $fs->get('test.txt')->getContents()); + $fs->put('test.txt', ''); + + foreach ($fs->listDirs('folder') as $dir) { + $this->assertInstanceOf(Folder::class, $dir); + } + foreach ($fs->listFiles('') as $file) { + $this->assertInstanceOf(File::class, $file); + } + + $resource = $fs->getStream('test.txt'); + $this->assertTrue(is_resource($resource)); + $this->assertTrue($fs->putStream('test.txt', $resource)); + fclose($resource); + + $this->assertTrue($fs->createDir('testing')); + $this->assertTrue($fs->exists('testing')); + $fs->deleteDir('testing'); + $this->assertFalse($fs->exists('testing')); + + $fs->put('test2.txt', ''); + $this->assertTrue($fs->exists('test2.txt')); + $fs->delete('test2.txt'); + $this->assertFalse($fs->exists('test2.txt')); + + $this->assertInstanceOf(FlySystem::class, $fs->getFlySystem()); + + $folder = $fs->get('/'); + $this->assertInstanceOf(Folder::class, $folder); + $this->assertEquals([ + base64_encode('test.txt') + ], + $folder->getFiles() + ); + $this->assertEquals(base64_encode('folder'), $folder->getDirs()[0]); + + foreach ($fs->listDirs('/') as $dir) { + $this->assertInstanceOf(Folder::class, $dir); + } + + foreach ($fs->listFiles('/') as $file) { + $this->assertInstanceOf(File::class, $file); + } + + foreach ($fs->listDirContents('folder', false) as $item) { + $this->assertInstanceOf(Item::class, $item); + } + + $this->expectException(FileNotFoundException::class); + $fs->get('does-not-exist'); + } +} \ No newline at end of file diff --git a/test/Unit/FolderTest.php b/test/Unit/FolderTest.php new file mode 100644 index 0000000..fd3650b --- /dev/null +++ b/test/Unit/FolderTest.php @@ -0,0 +1,43 @@ +assertInstanceOf(Folder::class, $folder); + $fs = new Filesystem(new Local(__DIR__ . '/../files')); + $folder = new Folder($fs->get('folder')); + $this->assertInstanceOf(Folder::class, $folder); + + $this->assertEquals('folder', $folder->getBasename()); + $this->assertEquals(base64_encode('/'), $folder->getParent()); + $this->assertEquals([base64_encode('folder/folder')], $folder->getDirs()); + $this->assertEquals([], $folder->getFiles()); + $this->assertEquals('folder', $folder->getPath()); + $this->assertEquals(base64_encode('folder'), $folder->getId()); + $this->assertTrue($folder->isDir()); + $this->assertEquals('folder', $folder->getPath()); + $this->assertTrue(is_array($folder->toArray())); + $this->assertFalse($folder->isFile()); + $this->assertInstanceOf(Directory::class, $folder->getDirectory()); + } +} \ No newline at end of file diff --git a/test/Unit/MimeDetectorTest.php b/test/Unit/MimeDetectorTest.php new file mode 100644 index 0000000..af39086 --- /dev/null +++ b/test/Unit/MimeDetectorTest.php @@ -0,0 +1,22 @@ +assertTrue($detector->isText('test.txt')); + $this->assertFalse($detector->isText('test.doc')); + $this->assertEquals('application/javascript', $detector->detectFromExtension('test.js')); + $this->assertEquals('text/plain', $detector->detectFromExtension('test')); + } +} \ No newline at end of file diff --git a/test/files/folder/folder/no-extension b/test/files/folder/folder/no-extension new file mode 100644 index 0000000..e69de29 diff --git a/test/files/test.txt b/test/files/test.txt new file mode 100644 index 0000000..e69de29