Initial commit IQParts\Content
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
composer.lock
|
||||||
|
vendor/
|
||||||
|
tmp/
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
coverage/
|
||||||
10
.scrutinizer.yml
Normal file
10
.scrutinizer.yml
Normal file
@@ -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
|
||||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -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.
|
||||||
42
Readme.md
Normal file
42
Readme.md
Normal file
@@ -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'
|
||||||
|
|
||||||
|
```
|
||||||
30
composer.json
Normal file
30
composer.json
Normal file
@@ -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"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
phpunit.xml
Normal file
18
phpunit.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
colors="true"
|
||||||
|
verbose="true"
|
||||||
|
backupGlobals="false"
|
||||||
|
beStrictAboutOutputDuringTests="true"
|
||||||
|
beStrictAboutTestsThatDoNotTestAnything="true"
|
||||||
|
>
|
||||||
|
<testsuite name="IQParts Content tests">
|
||||||
|
<directory>./test/Unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
<filter>
|
||||||
|
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||||
|
<directory suffix=".php">./src</directory>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
</phpunit>
|
||||||
86
src/AssetResolver.php
Normal file
86
src/AssetResolver.php
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\Content;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Stream;
|
||||||
|
use IQParts\Content\Object\CacheControl;
|
||||||
|
use IQParts\Content\Object\File;
|
||||||
|
use League\Flysystem\FileNotFoundException;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AssetResolver
|
||||||
|
* @package IQParts\Framework\Content
|
||||||
|
*/
|
||||||
|
final class AssetResolver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var MimeDetector
|
||||||
|
*/
|
||||||
|
private $detector;
|
||||||
|
/**
|
||||||
|
* @var Filesystem
|
||||||
|
*/
|
||||||
|
private $filesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AssetResolver constructor.
|
||||||
|
* @param Filesystem $filesystem
|
||||||
|
* @internal param string $assetDir
|
||||||
|
*/
|
||||||
|
public function __construct(Filesystem $filesystem )
|
||||||
|
{
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/ContentProviderInterface.php
Normal file
56
src/ContentProviderInterface.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\Framework\Content;
|
||||||
|
|
||||||
|
|
||||||
|
interface ContentProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $location
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function exists(string $location): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $location
|
||||||
|
* @param $data
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function put(string $location, $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $location
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
public function get(string $location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $location
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCollection(string $location): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $location
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getSize(string $location): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $location
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function remove(string $location): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $location
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPermissions(string $location): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $location
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function createCollection(string $location): bool;
|
||||||
|
}
|
||||||
197
src/Filesystem.php
Normal file
197
src/Filesystem.php
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\Content;
|
||||||
|
|
||||||
|
use IQParts\Content\Object\File;
|
||||||
|
use IQParts\Content\Object\Folder;
|
||||||
|
use League\Flysystem\AdapterInterface;
|
||||||
|
use League\Flysystem\Config;
|
||||||
|
use League\Flysystem\Directory;
|
||||||
|
use League\Flysystem\FileNotFoundException;
|
||||||
|
use League\Flysystem\Filesystem as FlySystem;
|
||||||
|
use League\Flysystem\Plugin\ListPaths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Filesystem
|
||||||
|
* @package IQParts\Content
|
||||||
|
*/
|
||||||
|
final class Filesystem
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var FlySystem
|
||||||
|
*/
|
||||||
|
private $flySystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filesystem constructor.
|
||||||
|
* @param AdapterInterface $adapter
|
||||||
|
* @param Config $config
|
||||||
|
*/
|
||||||
|
public function __construct(AdapterInterface $adapter, Config $config = null)
|
||||||
|
{
|
||||||
|
if (!$config) $config = [];
|
||||||
|
$flySystem = new FlySystem($adapter, $config);
|
||||||
|
$flySystem->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;
|
||||||
|
}
|
||||||
|
}
|
||||||
162
src/MimeDetector.php
Normal file
162
src/MimeDetector.php
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\Content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MimeDetector
|
||||||
|
* @package IQParts\Framework\Content
|
||||||
|
*/
|
||||||
|
final class MimeDetector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var
|
||||||
|
*/
|
||||||
|
private $mimeTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory constructor.
|
||||||
|
* @param array $extraMimes
|
||||||
|
*/
|
||||||
|
public function __construct(array $extraMimes = [])
|
||||||
|
{
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
74
src/Object/CacheControl.php
Normal file
74
src/Object/CacheControl.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\Content\Object;
|
||||||
|
|
||||||
|
final class CacheControl
|
||||||
|
{
|
||||||
|
public const MUST_REVALIDATE = 0;
|
||||||
|
public const NO_TRANSFORM = 1;
|
||||||
|
public const NO_CACHE = 2;
|
||||||
|
public const NO_STORE = 3;
|
||||||
|
public const PUBLIC = 4;
|
||||||
|
public const PRIVATE = 5;
|
||||||
|
public const PROXY = 6;
|
||||||
|
public const MAX_AGE = 7;
|
||||||
|
public const S_MAX_AGE = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $modes = [
|
||||||
|
'must-revalidate',
|
||||||
|
'no-transform',
|
||||||
|
'no-cache',
|
||||||
|
'no-store',
|
||||||
|
'public',
|
||||||
|
'private',
|
||||||
|
'proxy-revalidate',
|
||||||
|
'max-age',
|
||||||
|
's-max-age'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $seconds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CacheControl constructor.
|
||||||
|
* @param int $mode
|
||||||
|
* @param int $seconds
|
||||||
|
*/
|
||||||
|
public function __construct(int $mode, int $seconds = -1)
|
||||||
|
{
|
||||||
|
if ($mode < 0 || $mode > 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
252
src/Object/File.php
Normal file
252
src/Object/File.php
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace IQParts\Content\Object;
|
||||||
|
|
||||||
|
use IQParts\Content\MimeDetector;
|
||||||
|
use League\Flysystem\File as FlyFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class File
|
||||||
|
* @package IQParts\Content\Object
|
||||||
|
*/
|
||||||
|
final class File extends Item
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var FlyFile
|
||||||
|
*/
|
||||||
|
private $file;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $path;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $basename;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $size;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $extension;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $parent;
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
private $date;
|
||||||
|
/**
|
||||||
|
* @var false|int
|
||||||
|
*/
|
||||||
|
private $timestamp;
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $isEditable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PublicFile constructor.
|
||||||
|
* @param FlyFile $file
|
||||||
|
*/
|
||||||
|
public function __construct(FlyFile $file = null)
|
||||||
|
{
|
||||||
|
if ($file !== null) {
|
||||||
|
$editable = [
|
||||||
|
"js" => 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
187
src/Object/Folder.php
Normal file
187
src/Object/Folder.php
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\Content\Object;
|
||||||
|
|
||||||
|
use League\Flysystem\Directory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Folder
|
||||||
|
* @package IQParts\Content\Object
|
||||||
|
*/
|
||||||
|
final class Folder extends Item
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Directory
|
||||||
|
*/
|
||||||
|
private $directory;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $path;
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $parent;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $basename;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $dirs;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $files;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Folder constructor.
|
||||||
|
* @param Directory $directory
|
||||||
|
*/
|
||||||
|
public function __construct(Directory $directory = null)
|
||||||
|
{
|
||||||
|
if ($directory) {
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/Object/Item.php
Normal file
20
src/Object/Item.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\Content\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Item
|
||||||
|
* @package IQParts\Content\Object
|
||||||
|
*/
|
||||||
|
abstract class Item
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
abstract function isDir(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
abstract function isFile(): bool;
|
||||||
|
}
|
||||||
24
test/AbstractTestCase.php
Normal file
24
test/AbstractTestCase.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\ContentTest;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AbstractTestCase
|
||||||
|
* @package IQParts\ContentTest
|
||||||
|
*/
|
||||||
|
abstract class AbstractTestCase extends TestCase
|
||||||
|
{
|
||||||
|
public function getTmpDirectory()
|
||||||
|
{
|
||||||
|
$location = __DIR__ . '/../tmp';
|
||||||
|
if (!file_exists($location)) {
|
||||||
|
if (!mkdir($location)) {
|
||||||
|
throw new \RuntimeException('Could not create directory: ' . $location);
|
||||||
|
}
|
||||||
|
chmod($location, 0777);
|
||||||
|
}
|
||||||
|
return $location;
|
||||||
|
}
|
||||||
|
}
|
||||||
86
test/Unit/AssetResolverTest.php
Normal file
86
test/Unit/AssetResolverTest.php
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\ContentTest\Unit;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use GuzzleHttp\Psr7\Stream;
|
||||||
|
use IQParts\Content\AssetResolver;
|
||||||
|
use IQParts\Content\Filesystem;
|
||||||
|
use IQParts\Content\Object\CacheControl;
|
||||||
|
use IQParts\ContentTest\AbstractTestCase;
|
||||||
|
use League\Flysystem\Adapter\Local;
|
||||||
|
use League\Flysystem\FileNotFoundException;
|
||||||
|
use League\Flysystem\Filesystem as FlySystem;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
final class AssetResolverTest extends AbstractTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public function testResolver()
|
||||||
|
{
|
||||||
|
$fs = new Filesystem(new Local(__DIR__ . '/../files'));
|
||||||
|
|
||||||
|
$resolver = new AssetResolver($fs);
|
||||||
|
|
||||||
|
$this->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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
51
test/Unit/CacheControlTest.php
Normal file
51
test/Unit/CacheControlTest.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\ContentTest\Unit;
|
||||||
|
|
||||||
|
use IQParts\Content\Object\CacheControl;
|
||||||
|
use IQParts\ContentTest\AbstractTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CacheControlTest
|
||||||
|
* @package IQParts\ContentTest\Unit
|
||||||
|
*/
|
||||||
|
final class CacheControlTest extends AbstractTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function testCacheControlWithSeconds()
|
||||||
|
{
|
||||||
|
$cacheControl = new CacheControl(CacheControl::MAX_AGE, 10);
|
||||||
|
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
64
test/Unit/FileTest.php
Normal file
64
test/Unit/FileTest.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\ContentTest\Unit;
|
||||||
|
|
||||||
|
use IQParts\Content\Object\File;
|
||||||
|
use IQParts\ContentTest\AbstractTestCase;
|
||||||
|
use League\Flysystem\Adapter\Local;
|
||||||
|
use League\Flysystem\File as FlyFile;
|
||||||
|
use League\Flysystem\Filesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FileTest
|
||||||
|
* @package IQParts\ContentTest\Unit
|
||||||
|
*/
|
||||||
|
final class FileTest extends AbstractTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return File
|
||||||
|
*/
|
||||||
|
private function createFile(bool $extension = true): File
|
||||||
|
{
|
||||||
|
$fs = new Filesystem(new Local(__DIR__ . '/../files'));
|
||||||
|
|
||||||
|
if ($extension) {
|
||||||
|
return new File($fs->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()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
84
test/Unit/FilesystemTest.php
Normal file
84
test/Unit/FilesystemTest.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\ContentTest\Unit;
|
||||||
|
|
||||||
|
use IQParts\Content\Filesystem;
|
||||||
|
use IQParts\Content\Object\File;
|
||||||
|
use IQParts\Content\Object\Folder;
|
||||||
|
use IQParts\Content\Object\Item;
|
||||||
|
use League\Flysystem\Adapter\Local;
|
||||||
|
use League\Flysystem\FileNotFoundException;
|
||||||
|
use League\Flysystem\Filesystem as FlySystem;
|
||||||
|
|
||||||
|
use IQParts\ContentTest\AbstractTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FilesystemTest
|
||||||
|
* @package IQParts\ContentTest\Unit
|
||||||
|
*/
|
||||||
|
final class FilesystemTest extends AbstractTestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function testFilesystem()
|
||||||
|
{
|
||||||
|
$fs = new Filesystem(new Local(__DIR__ . '/../files'));
|
||||||
|
|
||||||
|
$this->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');
|
||||||
|
}
|
||||||
|
}
|
||||||
43
test/Unit/FolderTest.php
Normal file
43
test/Unit/FolderTest.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\ContentTest\Unit;
|
||||||
|
|
||||||
|
use IQParts\Content\Object\Folder;
|
||||||
|
use IQParts\ContentTest\AbstractTestCase;
|
||||||
|
use League\Flysystem\Adapter\Local;
|
||||||
|
use League\Flysystem\Directory;
|
||||||
|
use League\Flysystem\Filesystem;
|
||||||
|
|
||||||
|
final class FolderTest extends AbstractTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function testFolder()
|
||||||
|
{
|
||||||
|
$folder = Folder::fromVariables(
|
||||||
|
base64_encode('folder'),
|
||||||
|
'folder',
|
||||||
|
'folder',
|
||||||
|
base64_encode('/'),
|
||||||
|
[],
|
||||||
|
[base64_encode('folder/test.txt')]
|
||||||
|
);
|
||||||
|
$this->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());
|
||||||
|
}
|
||||||
|
}
|
||||||
22
test/Unit/MimeDetectorTest.php
Normal file
22
test/Unit/MimeDetectorTest.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQParts\ContentTest\Unit;
|
||||||
|
|
||||||
|
use IQParts\Content\MimeDetector;
|
||||||
|
use IQParts\ContentTest\AbstractTestCase;
|
||||||
|
|
||||||
|
final class MimeDetectorTest extends AbstractTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function testMimeDetector()
|
||||||
|
{
|
||||||
|
$detector = new MimeDetector();
|
||||||
|
|
||||||
|
$this->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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
0
test/files/folder/folder/no-extension
Normal file
0
test/files/folder/folder/no-extension
Normal file
0
test/files/test.txt
Normal file
0
test/files/test.txt
Normal file
Reference in New Issue
Block a user