diff --git a/.github/ISSUE_TEMPLATE/Bug.md b/.github/ISSUE_TEMPLATE/Bug.md
new file mode 100644
index 0000000..f563f05
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Bug.md
@@ -0,0 +1,24 @@
+---
+name: 🐛 Bug
+about: Did you encounter a bug?
+---
+
+### Bug Report
+
+
+
+| Q | A
+|------------ | ------
+| BC Break | yes/no
+| Version | x.y.z
+
+#### Summary
+
+
+
+#### How to reproduce
+
+
diff --git a/.github/ISSUE_TEMPLATE/Feature_Request.md b/.github/ISSUE_TEMPLATE/Feature_Request.md
new file mode 100644
index 0000000..de0f669
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Feature_Request.md
@@ -0,0 +1,21 @@
+---
+name: 🎉 Feature Request
+about: Do you have a new feature in mind?
+---
+
+### Feature Request
+
+
+
+| Q | A
+|------------ | ------
+| New Feature | yes/no
+| BC Break | yes/no
+
+#### Scenario / Use-case
+
+
+
+#### Summary
+
+
diff --git a/.github/ISSUE_TEMPLATE/Question.md b/.github/ISSUE_TEMPLATE/Question.md
new file mode 100644
index 0000000..fa8e763
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Question.md
@@ -0,0 +1,8 @@
+---
+name: ❓ Question
+about: Are you unsure about something?
+---
+
+### Question
+
+
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..3587312
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,50 @@
+name: CI
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ php: ['8.1', '8.2', '8.3']
+ include:
+ - php: '8.1'
+ send-to-scrutinizer: 'yes'
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup PHP with fail-fast
+ uses: shivammathur/setup-php@v2
+ with:
+ send-to-scrutinizer: 'no'
+ phpunit-flags: '--no-coverage'
+ php-version: ${{ matrix.php }}
+ coverage: xdebug
+ env:
+ fail-fast: true
+ - name: Validate composer.json and composer.lock
+ run: composer validate
+
+ - name: Cache Composer packages
+ id: composer-cache
+ uses: actions/cache@v2
+ with:
+ path: vendor
+ key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-
+
+ - name: Install dependencies
+ if: steps.composer-cache.outputs.cache-hit != 'true'
+ run: composer install --prefer-dist --no-progress --no-suggest
+
+ - name: Run test suite
+ run: |
+ composer run stan
+ sudo composer run test
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..8a8a771
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,28 @@
+name: Publish docs
+
+on:
+ push:
+ tags:
+ - '*.*.*'
+jobs:
+ docs:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Build docs
+ run: |
+ curl -OS https://couscous.io/couscous.phar
+ php couscous.phar generate --target=build/docs/ ./docs
+
+ - name: FTP Deployer
+ uses: sand4rt/ftp-deployer@v1.1
+ with:
+ host: ${{ secrets.DOCS_FTP_HOST }}
+ username: ${{ secrets.DOCS_FTP_USER }}
+ password: ${{ secrets.DOCS_FTP_PASSWORD }}
+ remote_folder: upload
+ # The local folder location
+ local_folder: build/docs/
+ # Remove existing files inside FTP remote folder
+ cleanup: false # optional
diff --git a/.gitignore b/.gitignore
index 5332ca4..1a295de 100755
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@
.buildpath
.project
build/
-tests/fixitures/
vendor/
composer.lock
atlassian-ide-plugin.xml
diff --git a/.php_cs.cache b/.php_cs.cache
new file mode 100644
index 0000000..e3481e0
--- /dev/null
+++ b/.php_cs.cache
@@ -0,0 +1 @@
+{"php":"7.2.4","version":"2.16.1","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\\Container\\ContainerInterface.php":1583588918,"src\\Container\\Local.php":71709479,"src\\Exception\\InvalidContainerException.php":1907413747,"src\\Exception\\InvalidResultException.php":466931389,"src\\Handler.php":534695839,"src\\HandlerAggregate.php":1499917949,"src\\Result\\Collection.php":2897468334,"src\\Result\\File.php":1434642790,"src\\UploadHandlerInterface.php":2299182802,"src\\Util\\Arr.php":1378696061,"src\\Result\\ResultInterface.php":844276353,"src\\Util\\Helper.php":3774982220}}
\ No newline at end of file
diff --git a/README.md b/README.md
index c709edd..d02caa6 100755
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[![Source Code](https://img.shields.io/badge/source-siriusphp/upload-blue.svg?style=flat-square)](https://github.com/siriusphp/upload)
[![Latest Version](https://img.shields.io/packagist/v/siriusphp/upload.svg?style=flat-square)](https://github.com/siriusphp/upload/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/siriusphp/upload/blob/master/LICENSE)
-[![Build Status](https://img.shields.io/travis/siriusphp/upload/master.svg?style=flat-square)](https://travis-ci.org/siriusphp/upload)
+[![Build Status](https://github.com/siriusphp/upload/actions/workflows/ci.yml/badge.svg)](https://github.com/siriusphp/upload/actions/workflows/ci.yml)
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/siriusphp/upload.svg?style=flat-square)](https://scrutinizer-ci.com/g/siriusphp/upload/code-structure)
[![Quality Score](https://img.shields.io/scrutinizer/g/siriusphp/upload.svg?style=flat-square)](https://scrutinizer-ci.com/g/siriusphp/upload)
[![Total Downloads](https://img.shields.io/packagist/dt/siriusphp/upload.svg?style=flat-square)](https://packagist.org/packages/siriusphp/upload)
diff --git a/composer.json b/composer.json
index 16db458..067920d 100755
--- a/composer.json
+++ b/composer.json
@@ -19,13 +19,16 @@
}
],
"require": {
- "php": ">=7.1",
- "siriusphp/validation": "~3.0"
+ "php": ">=8.1",
+ "siriusphp/validation": "^4.0"
},
"require-dev": {
- "phpunit/phpunit": "~8.5",
- "laminas/laminas-diactoros": "^2.2",
- "symfony/http-foundation": "^4.4"
+ "laminas/laminas-diactoros": "^3.3",
+ "symfony/http-foundation": "^6.3",
+ "pestphp/pest": "^2.24",
+ "pestphp/pest-plugin-drift": "^2.5",
+ "symfony/mime": "^6.3",
+ "phpstan/phpstan": "^1.10"
},
"suggest": {
"league/flysystem": "To upload to different destinations, not just to the local file system",
@@ -37,26 +40,25 @@
}
},
"scripts": {
- "cs": [
- "php phpcs.phar --standard=PSR2 ./src"
- ],
- "md": [
- "php phpmd.phar ./src xml phpmd.xml"
- ],
- "cbf": [
- "php phpcbf.phar ./src --standard=PSR2 -w"
+ "stan": [
+ "php vendor/bin/phpstan analyse"
],
"csfix": [
- "php php-cs-fixer.phar fix ./src --rules=@PSR2"
+ "tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --standard=PSR-2 src"
+ ],
+ "test": [
+ "php vendor/bin/pest"
],
"build-docs": [
"php couscous.phar generate --target=build/docs/ ./docs"
],
"docs": [
"cd docs && php ../couscous.phar preview"
- ],
- "test": [
- "vendor/bin/phpunit -c phpunit.xml"
]
+ },
+ "config": {
+ "allow-plugins": {
+ "pestphp/pest-plugin": true
+ }
}
}
diff --git a/docs/index.md b/docs/index.md
index e17fba4..f465dd8 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -2,7 +2,7 @@
[![Latest Version](https://img.shields.io/packagist/v/siriusphp/upload.svg?style=flat-square)](https://github.com/siriusphp/upload/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/siriusphp/upload/blob/master/LICENSE)
[![Build Status](https://img.shields.io/travis/siriusphp/upload/master.svg?style=flat-square)](https://travis-ci.org/siriusphp/upload)
-[![PHP 7 ready](http://php7ready.timesplinter.ch/siriusphp/upload/master/badge.svg)](https://travis-ci.org/siriusphp/upload)
+[![PHP 7 ready](https:////php7ready.timesplinter.ch/siriusphp/upload/master/badge.svg)](https://travis-ci.org/siriusphp/upload)
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/siriusphp/upload.svg?style=flat-square)](https://scrutinizer-ci.com/g/siriusphp/upload/code-structure)
[![Quality Score](https://img.shields.io/scrutinizer/g/siriusphp/upload.svg?style=flat-square)](https://scrutinizer-ci.com/g/siriusphp/upload)
[![Total Downloads](https://img.shields.io/packagist/dt/siriusphp/upload.svg?style=flat-square)](https://packagist.org/packages/siriusphp/upload)
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..6615f46
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,5 @@
+parameters:
+ level: 8
+ checkGenericClassInNonGenericObjectType: false
+ paths:
+ - src
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..7d0904f
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ ./tests
+
+
+
+
diff --git a/src/Container/ContainerInterface.php b/src/Container/ContainerInterface.php
index ff689b7..3712aa7 100755
--- a/src/Container/ContainerInterface.php
+++ b/src/Container/ContainerInterface.php
@@ -9,14 +9,12 @@ interface ContainerInterface
/**
* Check if the container is writable
*/
- public function isWritable();
+ public function isWritable(): bool;
/**
* This will check if a file is in the container
- *
- * @param string $file
*/
- public function has($file);
+ public function has(string $file): bool;
/**
* Saves the $content string as a file
@@ -24,20 +22,15 @@ public function has($file);
* @param string $file
* @param string $content
*/
- public function save($file, $content);
+ public function save(string $file, string $content): bool;
/**
* Delete the file from the container
- *
- * @param string $file
*/
- public function delete($file);
+ public function delete(string $file): bool;
/**
* Moves a temporary uploaded file to a destination in the container
- *
- * @param string $localFile local path
- * @param string $destination
*/
- public function moveUploadedFile($localFile, $destination);
+ public function moveUploadedFile(string $localFile, string $destination): bool;
}
diff --git a/src/Container/Local.php b/src/Container/Local.php
index b282ce5..d5b5ce8 100755
--- a/src/Container/Local.php
+++ b/src/Container/Local.php
@@ -5,24 +5,24 @@
class Local implements ContainerInterface
{
- protected $baseDirectory;
+ protected string $baseDirectory;
- public function __construct($baseDirectory)
+ public function __construct(string $baseDirectory)
{
$this->baseDirectory = $this->normalizePath($baseDirectory) . DIRECTORY_SEPARATOR;
$this->ensureDirectory($this->baseDirectory);
}
- protected function normalizePath($path)
+ protected function normalizePath(string $path): string
{
$path = dirname(rtrim($path, '\\/') . DIRECTORY_SEPARATOR . 'xxx');
return rtrim($path, DIRECTORY_SEPARATOR);
}
- protected function ensureDirectory($directory):bool
+ protected function ensureDirectory(string $directory): bool
{
- if (!file_exists($directory)) {
+ if ( ! is_dir($directory)) {
mkdir($directory, 0755, true);
}
@@ -32,7 +32,7 @@ protected function ensureDirectory($directory):bool
/**
* Check if the container is writable
*/
- public function isWritable():bool
+ public function isWritable(): bool
{
return is_writable($this->baseDirectory);
}
@@ -40,39 +40,27 @@ public function isWritable():bool
/**
* This will check if a file is in the container
*
- * @param string $file
+ * @param string $file
+ *
* @return bool
*/
- public function has($file):bool
+ public function has(string $file): bool
{
return $file && file_exists($this->baseDirectory . $file);
}
- /**
- * Saves the $content string as a file
- *
- * @param string $file
- * @param string $content
- * @return bool
- */
- public function save($file, $content):bool
+ public function save(string $file, string $content): bool
{
$file = $this->normalizePath($file);
- $dir = dirname($this->baseDirectory . $file);
+ $dir = dirname($this->baseDirectory . $file);
if ($this->ensureDirectory($dir)) {
- return (bool) file_put_contents($this->baseDirectory . $file, $content);
+ return (bool)file_put_contents($this->baseDirectory . $file, $content);
}
return false;
}
- /**
- * Delete the file from the container
- *
- * @param string $file
- * @return bool
- */
- public function delete($file):bool
+ public function delete(string $file): bool
{
$file = $this->normalizePath($file);
if (file_exists($this->baseDirectory . $file)) {
@@ -82,14 +70,7 @@ public function delete($file):bool
return true;
}
- /**
- * Moves a temporary uploaded file to a destination in the container
- *
- * @param string $localFile local path
- * @param string $destination
- * @return bool
- */
- public function moveUploadedFile($localFile, $destination):bool
+ public function moveUploadedFile(string $localFile, string $destination): bool
{
$dir = dirname($this->baseDirectory . $destination);
if (file_exists($localFile) && $this->ensureDirectory($dir)) {
@@ -97,9 +78,11 @@ public function moveUploadedFile($localFile, $destination):bool
// rename() would be good but this is better because $localFile may become 'unwritable'
$result = copy($localFile, $this->baseDirectory . $destination);
@unlink($localFile);
+
return $result;
}
}
+
return false;
}
}
diff --git a/src/Handler.php b/src/Handler.php
index dd5ef6d..a5fbc05 100755
--- a/src/Handler.php
+++ b/src/Handler.php
@@ -1,5 +1,6 @@
$options
+ *
* @throws InvalidContainerException
*/
- public function __construct($directoryOrContainer, $options = [], ValueValidator $validator = null)
+ public function __construct(mixed $directoryOrContainer, array $options = [], ValueValidator $validator = null)
{
$container = $directoryOrContainer;
if (is_string($directoryOrContainer)) {
$container = new LocalContainer($directoryOrContainer);
}
- if (!$container instanceof ContainerInterface) {
+ if ( ! $container instanceof ContainerInterface) {
throw new InvalidContainerException('Destination container for uploaded files is not valid');
}
$this->container = $container;
// create the validator
- if (!$validator) {
+ if ( ! $validator) {
$validator = new ValueValidator();
}
$this->validator = $validator;
// set options
$availableOptions = [
- static::OPTION_PREFIX => 'setPrefix',
- static::OPTION_OVERWRITE => 'setOverwrite',
+ static::OPTION_PREFIX => 'setPrefix',
+ static::OPTION_OVERWRITE => 'setOverwrite',
static::OPTION_AUTOCONFIRM => 'setAutoconfirm'
];
foreach ($availableOptions as $key => $method) {
@@ -100,16 +90,9 @@ public function __construct($directoryOrContainer, $options = [], ValueValidator
}
}
- /**
- * Enable/disable upload overwrite
- *
- * @param bool $overwrite
- *
- * @return Handler
- */
- public function setOverwrite($overwrite)
+ public function setOverwrite(bool $overwrite): self
{
- $this->overwrite = (bool) $overwrite;
+ $this->overwrite = (bool)$overwrite;
return $this;
}
@@ -120,11 +103,9 @@ public function setOverwrite($overwrite)
* - a string to be used as prefix
* - a function that returns a string
*
- * @param string|callable $prefix
- *
- * @return Handler
+ * @param string|callable $prefix
*/
- public function setPrefix($prefix)
+ public function setPrefix(mixed $prefix): self
{
$this->prefix = $prefix;
@@ -134,46 +115,34 @@ public function setPrefix($prefix)
/**
* Enable/disable upload autoconfirmation
* Autoconfirmation does not require calling `confirm()`
- *
- * @param boolean $autoconfirm
- *
- * @return Handler
*/
- public function setAutoconfirm($autoconfirm)
+ public function setAutoconfirm(bool $autoconfirm): self
{
- $this->autoconfirm = (bool) $autoconfirm;
+ $this->autoconfirm = (bool)$autoconfirm;
return $this;
}
-
+
/**
* Set the sanitizer function for cleaning up the file names
- *
- * @param callable $callback
- *
- * @return Handler
* @throws \InvalidArgumentException
*/
- public function setSanitizerCallback($callback)
+ public function setSanitizerCallback(callable|\Closure $callback): self
{
- if (!is_callable($callback)) {
+ if ( ! is_callable($callback)) {
throw new \InvalidArgumentException('The $callback parameter is not a valid callable entity');
}
$this->sanitizerCallback = $callback;
+
return $this;
}
/**
* Add validation rule (extension|size|width|height|ratio)
*
- * @param string $name
- * @param mixed $options
- * @param string $errorMessageTemplate
- * @param string $label
- *
- * @return Handler
+ * @param array $options
*/
- public function addRule($name, $options = null, $errorMessageTemplate = null, $label = null):Handler
+ public function addRule(string $name, array $options = [], string $errorMessageTemplate = null, string $label = null): self
{
$predefinedRules = [
static::RULE_EXTENSION,
@@ -187,18 +156,18 @@ public function addRule($name, $options = null, $errorMessageTemplate = null, $l
if (in_array($name, $predefinedRules)) {
$name = 'upload' . $name;
}
- $this->validator->add($name, $options, $errorMessageTemplate, $label);
+ if ($this->validator) {
+ $this->validator->add($name, $options, $errorMessageTemplate, $label);
+ }
return $this;
}
/**
* Processes a file upload and returns an upload result file/collection
- *
- * @param array $files
* @return Result\Collection|Result\File|ResultInterface
*/
- public function process($files = []):ResultInterface
+ public function process(mixed $files): ResultInterface
{
$files = Helper::normalizeFiles($files);
@@ -219,16 +188,17 @@ public function process($files = []):ResultInterface
* - validates the file
* - if valid, moves the file to the container
*
- * @param array $file
- * @return array
+ * @param array $file
+ *
+ * @return array
*/
- protected function processSingleFile(array $file):array
+ protected function processSingleFile(array $file): array
{
// store it for future reference
$file['original_name'] = $file['name'];
// sanitize the file name
- $file['name'] = $this->sanitizeFileName($file['name'], $file);
+ $file['name'] = $this->sanitizeFileName($file['name']);
$file = $this->validateFile($file);
// if there are messages the file is not valid
@@ -239,13 +209,13 @@ protected function processSingleFile(array $file):array
// add the prefix
$prefix = '';
if (is_callable($this->prefix)) {
- $prefix = (string) call_user_func($this->prefix, $file['name']);
+ $prefix = (string)call_user_func($this->prefix, $file['name']);
} elseif (is_string($this->prefix)) {
- $prefix = (string) $this->prefix;
+ $prefix = (string)$this->prefix;
}
// if overwrite is not allowed, check if the file is already in the container
- if (!$this->overwrite) {
+ if ( ! $this->overwrite) {
if ($this->container->has($prefix . $file['name'])) {
// add the timestamp to ensure the file is unique
// method is not bulletproof but it's pretty safe
@@ -254,7 +224,7 @@ protected function processSingleFile(array $file):array
}
// attempt to move the uploaded file into the container
- if (!$this->container->moveUploadedFile($file['tmp_name'], $prefix . $file['name'])) {
+ if ( ! $this->container->moveUploadedFile($file['tmp_name'], $prefix . $file['name'])) {
$file['name'] = false;
return $file;
@@ -262,8 +232,8 @@ protected function processSingleFile(array $file):array
$file['name'] = $prefix . $file['name'];
// create the lock file if autoconfirm is disabled
- if (!$this->autoconfirm) {
- $this->container->save($file['name'] . '.lock', (string) time());
+ if ( ! $this->autoconfirm) {
+ $this->container->save($file['name'] . '.lock', (string)time());
}
return $file;
@@ -272,12 +242,13 @@ protected function processSingleFile(array $file):array
/**
* Validates a file according to the rules configured on the handler
*
- * @param $file
- * @return mixed
+ * @param array $file
+ *
+ * @return array
*/
- protected function validateFile($file)
+ protected function validateFile(array $file): array
{
- if (!$this->validator->validate($file)) {
+ if ($this->validator && ! $this->validator->validate($file)) {
$file['messages'] = $this->validator->getMessages();
}
@@ -287,16 +258,13 @@ protected function validateFile($file)
/**
* Sanitize the name of the uploaded file by stripping away bad characters
* and replacing "invalid" characters with underscore _
- *
- * @param string $name
- * @param array $file
- * @return string
*/
- protected function sanitizeFileName($name, $file)
+ protected function sanitizeFileName(string $name): string
{
- if ($this->sanitizerCallback) {
- return call_user_func($this->sanitizerCallback, $name, $file);
+ if (is_callable($this->sanitizerCallback)) {
+ return call_user_func($this->sanitizerCallback, $name);
}
- return preg_replace('/[^A-Za-z0-9\.]+/', '_', $name);
+
+ return preg_replace('/[^A-Za-z0-9\.]+/', '_', $name); // @phpstan-ignore-line
}
}
diff --git a/src/HandlerAggregate.php b/src/HandlerAggregate.php
index 9f48906..edfbe7a 100755
--- a/src/HandlerAggregate.php
+++ b/src/HandlerAggregate.php
@@ -1,23 +1,22 @@
$handlers
+ */
+ protected array $handlers = [];
/**
* Adds a handler on the aggregate
- *
- * @param string $selector
- * @param Handler $handler
- * @return $this
*/
- public function addHandler($selector, Handler $handler)
+ public function addHandler(string $selector, Handler $handler): self
{
$this->handlers[$selector] = $handler;
@@ -25,18 +24,18 @@ public function addHandler($selector, Handler $handler)
}
/**
- * Processes
- * @param $files
- * @return Collection
+ * @param array $files
+ *
+ * @return Result\Collection
*/
- public function process($files)
+ public function process(mixed $files): mixed
{
$result = new Collection();
foreach ($this->handlers as $selector => $handler) {
/* @var $handler Handler */
$selectedFiles = Arr::getBySelector($files, $selector);
- if (!$selectedFiles || !is_array($selectedFiles) || empty($selectedFiles)) {
+ if (empty($selectedFiles)) {
continue;
}
@@ -50,15 +49,8 @@ public function process($files)
return $result;
}
- /**
- * Retrieve an external iterator
- *
- * @link https://php.net/manual/en/iteratoraggregate.getiterator.php
- * @return \Traversable An instance of an object implementing Iterator or
- * Traversable
- */
- public function getIterator()
+ public function getIterator(): \Traversable
{
- return $this->handlers;
+ return new \ArrayIterator($this->handlers);
}
}
diff --git a/src/Result/Collection.php b/src/Result/Collection.php
index 28424d0..7c0c20d 100755
--- a/src/Result/Collection.php
+++ b/src/Result/Collection.php
@@ -7,10 +7,13 @@
class Collection extends \ArrayIterator implements ResultInterface
{
- public function __construct($files = [], ContainerInterface $container = null)
+ /**
+ * @param array $files
+ */
+ public function __construct(array $files = [], ContainerInterface $container = null)
{
$filesArray = [];
- if (is_array($files) && !empty($files)) {
+ if ($container && ! empty($files)) {
foreach ($files as $key => $file) {
$filesArray[$key] = new File($file, $container);
}
@@ -18,7 +21,7 @@ public function __construct($files = [], ContainerInterface $container = null)
parent::__construct($filesArray);
}
- public function clear()
+ public function clear(): void
{
foreach ($this as $file) {
/* @var $file \Sirius\Upload\Result\File */
@@ -26,7 +29,7 @@ public function clear()
}
}
- public function confirm()
+ public function confirm(): void
{
foreach ($this as $file) {
/* @var $file \Sirius\Upload\Result\File */
@@ -34,7 +37,7 @@ public function confirm()
}
}
- public function isValid():bool
+ public function isValid(): bool
{
foreach ($this->getMessages() as $messages) {
if ($messages) {
@@ -45,7 +48,10 @@ public function isValid():bool
return true;
}
- public function getMessages():array
+ /**
+ * @return array>
+ */
+ public function getMessages(): array
{
$messages = [];
foreach ($this as $key => $file) {
diff --git a/src/Result/File.php b/src/Result/File.php
index 9ac4c29..1c5bbc2 100755
--- a/src/Result/File.php
+++ b/src/Result/File.php
@@ -5,6 +5,9 @@
use Sirius\Upload\Container\ContainerInterface;
+/**
+ * @property string $name
+ */
class File implements ResultInterface
{
@@ -15,23 +18,22 @@ class File implements ResultInterface
* - tmp_name
* etc
*
- * @var array
+ * @var array
*/
- protected $file;
+ protected array $file;
/**
* The container to which this file belongs to
- * @var ContainerInterface
*/
- protected $container;
+ protected ContainerInterface $container;
/**
- * @param $file
+ * @param array $file
* @param ContainerInterface $container
*/
- public function __construct($file, ContainerInterface $container)
+ public function __construct(array $file, ContainerInterface $container)
{
- $this->file = $file;
+ $this->file = $file;
$this->container = $container;
}
@@ -40,7 +42,7 @@ public function __construct($file, ContainerInterface $container)
*
* @return bool
*/
- public function isValid():bool
+ public function isValid(): bool
{
return $this->file['name'] && count($this->getMessages()) === 0;
}
@@ -48,9 +50,9 @@ public function isValid():bool
/**
* Returns the validation error messages
*
- * @return array
+ * @return array
*/
- public function getMessages():array
+ public function getMessages(): array
{
if (isset($this->file['messages'])) {
return $this->file['messages'];
@@ -63,7 +65,7 @@ public function getMessages():array
* The file that was saved during process() and has a .lock file attached
* will be cleared, in case the form processing fails
*/
- public function clear()
+ public function clear(): void
{
$this->container->delete($this->name);
$this->container->delete($this->name . '.lock');
@@ -74,18 +76,12 @@ public function clear()
* Remove the .lock file attached to the file that was saved during process()
* This should happen if the form fails validation/processing
*/
- public function confirm()
+ public function confirm(): void
{
$this->container->delete($this->name . '.lock');
}
- /**
- * File attribute getter
- *
- * @param $name
- * @return mixed
- */
- public function __get($name)
+ public function __get(string $name): mixed
{
if (isset($this->file[$name])) {
return $this->file[$name];
diff --git a/src/Result/ResultInterface.php b/src/Result/ResultInterface.php
index 97a6f05..a93fe9a 100644
--- a/src/Result/ResultInterface.php
+++ b/src/Result/ResultInterface.php
@@ -10,24 +10,24 @@ interface ResultInterface
*
* @return bool
*/
- public function isValid():bool;
+ public function isValid(): bool;
/**
* Returns the validation error messages
*
- * @return array
+ * @return array
*/
- public function getMessages():array;
+ public function getMessages(): array;
/**
* The file that was saved during process() and has a .lock file attached
* will be cleared, in case the form processing fails
*/
- public function clear();
+ public function clear(): void;
/**
* Remove the .lock file attached to the file that was saved during process()
* This should happen if the form fails validation/processing
*/
- public function confirm();
+ public function confirm(): void;
}
diff --git a/src/UploadHandlerInterface.php b/src/UploadHandlerInterface.php
index e3cd62e..0aa813f 100755
--- a/src/UploadHandlerInterface.php
+++ b/src/UploadHandlerInterface.php
@@ -3,6 +3,10 @@
namespace Sirius\Upload;
+use Psr\Http\Message\UploadedFileInterface;
+use Sirius\Upload\Result\ResultInterface;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
interface UploadHandlerInterface
{
@@ -14,7 +18,9 @@ interface UploadHandlerInterface
* be added by the container save() method so, in case the form is
* not validated, the uploaded file will be removed.
*
- * @param array $files
+ * @param array|UploadedFileInterface|UploadedFile $files
+ *
+ * @return Result\Collection|Result\File|ResultInterface
*/
- public function process($files = []);
+ public function process(mixed $files): mixed;
}
diff --git a/src/Util/Helper.php b/src/Util/Helper.php
index f014e60..6eb41fd 100644
--- a/src/Util/Helper.php
+++ b/src/Util/Helper.php
@@ -3,6 +3,9 @@
namespace Sirius\Upload\Util;
+use Psr\Http\Message\UploadedFileInterface;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
class Helper
{
const PSR7_UPLOADED_FILE_CLASS = '\Psr\Http\Message\UploadedFileInterface';
@@ -10,15 +13,14 @@ class Helper
/**
* We do not type-hint or import the class since it may not be used
- *
- * @param \Psr\Http\Message\UploadedFileInterface $file
- *
- * @return array
+ * @return array
*/
- public static function extractFromUploadedFileInterface($file)
+ public static function extractFromUploadedFileInterface(UploadedFileInterface $file): array
{
- /** @var \Psr\Http\Message\UploadedFileInterface $file */
$tempName = tempnam(sys_get_temp_dir(), 'srsupld_');
+ if (!$tempName) {
+ throw new \RuntimeException('Could not create temporary directory');
+ }
$file->moveTo($tempName);
$result = [
'name' => $file->getClientFilename(),
@@ -31,10 +33,11 @@ public static function extractFromUploadedFileInterface($file)
return $result;
}
- public static function extractFromSymfonyFile($file)
+ /**
+ * @return array
+ */
+ public static function extractFromSymfonyFile(UploadedFile $file): array
{
- /** @var \Symfony\Component\HttpFoundation\File\UploadedFile $file */
-
$result = [
'name' => $file->getClientOriginalName(),
'tmp_name' => $file->getPathname(),
@@ -46,16 +49,21 @@ public static function extractFromSymfonyFile($file)
return $result;
}
+ /**
+ * @param array $files
+ *
+ * @return array
+ */
public static function remapFilesArray(array $files): array
{
$result = [];
foreach (array_keys($files['name']) as $k) {
$result[$k] = [
'name' => $files['name'][$k],
- 'type' => @$files['type'][$k],
- 'size' => @$files['size'][$k],
- 'error' => @$files['error'][$k],
- 'tmp_name' => $files['tmp_name'][$k]
+ 'type' => $files['type'][$k] ?? null,
+ 'size' => $files['size'][$k] ?? null,
+ 'error' => $files['error'][$k] ?? null,
+ 'tmp_name' => $files['tmp_name'][$k] ?? null
];
}
@@ -69,11 +77,11 @@ public static function remapFilesArray(array $files): array
* multiple files are uploaded under the same name
* @see https://www.php.net/manual/en/features.file-upload.php
*
- * @param array|Symfony\Component\HttpFoundation\File\UploadedFile|\Psr\Http\Message\UploadedFileInterface $files
+ * @param array|UploadedFile|UploadedFileInterface $files
*
- * @return array
+ * @return array
*/
- public static function normalizeFiles($files): array
+ public static function normalizeFiles(mixed $files): array
{
if (empty($files)) {
return [];
@@ -90,7 +98,8 @@ public static function normalizeFiles($files): array
// If caller passed in an array of objects (Either PSR7 or Symfony)
if (is_array($files) && is_object(reset($files))) {
- if (is_subclass_of(reset($files), self::PSR7_UPLOADED_FILE_CLASS)) {
+ $firstFile = reset($files);
+ if ($firstFile instanceof UploadedFileInterface) {
$result = [];
foreach ($files as $file) {
$result[] = self::extractFromUploadedFileInterface($file);
@@ -99,7 +108,7 @@ public static function normalizeFiles($files): array
return $result;
}
- if (get_class(reset($files)) == self::SYMFONY_UPLOADED_FILE_CLASS) {
+ if ($firstFile instanceof UploadedFile) {
$result = [];
foreach ($files as $file) {
$result[] = self::extractFromSymfonyFile($file);
@@ -112,23 +121,23 @@ public static function normalizeFiles($files): array
// The caller passed $_FILES['some_field_name']
if (isset($files['name'])) {
// we have a single file
- if (! is_array($files['name'])) {
+ if ( ! is_array($files['name'])) {
return [$files];
} else {
// we have list of files, which PHP messes up
- return Helper::remapFilesArray($files);
+ return Helper::remapFilesArray($files); // @phpstan-ignore-line
}
} else {
// The caller passed $_FILES
- $keys = array_keys($files);
+ $keys = array_keys($files); // @phpstan-ignore-line
if (isset($keys[0]) && isset($files[$keys[0]]['name'])) {
- if (! is_array($files[$keys[0]]['name'])) {
+ if ( ! is_array($files[$keys[0]]['name'])) {
// $files is in the correct format already, even in the
// case it contains a single element.
- return $files;
+ return $files; //@phpstan-ignore-line
} else {
// we have list of files, which PHP messes up
- return Helper::remapFilesArray($files[$keys[0]]);
+ return Helper::remapFilesArray($files[$keys[0]]); // @phpstan-ignore-line
}
}
}
diff --git a/tests/.phpunit.result.cache b/tests/.phpunit.result.cache
new file mode 100644
index 0000000..2432fcc
--- /dev/null
+++ b/tests/.phpunit.result.cache
@@ -0,0 +1 @@
+C:37:"PHPUnit\Runner\DefaultTestResultCache":2714:{a:2:{s:7:"defects";a:16:{s:64:"Sirius\Upload\HandlerTest::testExceptionTrwonForInvalidContainer";i:4;s:76:"Sirius\Upload\HandlerTest::testExceptionThrownForInvalidSanitizationCallback";i:4;s:47:"Sirius\Upload\HandlerAggregateTest::testProcess";i:4;s:48:"Sirius\Upload\HandlerTest::testPsr7UploadedFiles";i:3;s:51:"Sirius\Upload\HandlerTest::testSymfonyUploadedFiles";i:4;s:52:"Sirius\Upload\HandlerTest::testBasicUploadWithPrefix";i:4;s:46:"Sirius\Upload\HandlerTest::testUploadOverwrite";i:4;s:48:"Sirius\Upload\HandlerTest::testUploadAutoconfirm";i:4;s:55:"Sirius\Upload\HandlerTest::testSingleUploadConfirmation";i:4;s:51:"Sirius\Upload\HandlerTest::testSingleUploadClearing";i:4;s:42:"Sirius\Upload\HandlerTest::testMultiUpload";i:4;s:50:"Sirius\Upload\HandlerTest::testOriginalMultiUpload";i:4;s:46:"Sirius\Upload\HandlerTest::testWrongFilesArray";i:4;s:53:"Sirius\Upload\HandlerTest::testSingleUploadValidation";i:4;s:52:"Sirius\Upload\HandlerTest::testMultiUploadValidation";i:4;s:57:"Sirius\Upload\HandlerTest::testCustomSanitizationCallback";i:4;}s:5:"times";a:24:{s:43:"Sirius\Upload\Container\LocalTest::testSave";d:0.03;s:45:"Sirius\Upload\Container\LocalTest::testDelete";d:0.011;s:59:"Sirius\Upload\Container\LocalTest::testDeleteInexistingFile";d:0.007;s:55:"Sirius\Upload\Container\LocalTest::testMoveUploadedFile";d:0.014;s:62:"Sirius\Upload\Container\LocalTest::testMoveMissingUploadedFile";d:0.007;s:47:"Sirius\Upload\HandlerAggregateTest::testProcess";d:0.1;s:48:"Sirius\Upload\HandlerAggregateTest::testIterator";d:0.024;s:52:"Sirius\Upload\HandlerTest::testBasicUploadWithPrefix";d:0.019;s:46:"Sirius\Upload\HandlerTest::testUploadOverwrite";d:0.042;s:48:"Sirius\Upload\HandlerTest::testUploadAutoconfirm";d:0.018;s:55:"Sirius\Upload\HandlerTest::testSingleUploadConfirmation";d:0.024;s:51:"Sirius\Upload\HandlerTest::testSingleUploadClearing";d:0.024;s:42:"Sirius\Upload\HandlerTest::testMultiUpload";d:0.038;s:50:"Sirius\Upload\HandlerTest::testOriginalMultiUpload";d:0.032;s:46:"Sirius\Upload\HandlerTest::testWrongFilesArray";d:0.012;s:64:"Sirius\Upload\HandlerTest::testExceptionTrwonForInvalidContainer";d:0.013;s:53:"Sirius\Upload\HandlerTest::testSingleUploadValidation";d:0.017;s:52:"Sirius\Upload\HandlerTest::testMultiUploadValidation";d:0.018;s:57:"Sirius\Upload\HandlerTest::testCustomSanitizationCallback";d:0.022;s:76:"Sirius\Upload\HandlerTest::testExceptionThrownForInvalidSanitizationCallback";d:0.012;s:48:"Sirius\Upload\HandlerTest::testPsr7UploadedFiles";d:0.045;s:51:"Sirius\Upload\HandlerTest::testSymfonyUploadedFiles";d:0.07;s:56:"Sirius\Upload\HandlerTest::testSingleSymfonyUploadedFile";d:0.03;s:53:"Sirius\Upload\HandlerTest::testSinglePsr7UploadedFile";d:0.026;}}}
\ No newline at end of file
diff --git a/tests/Pest.php b/tests/Pest.php
new file mode 100644
index 0000000..21e1252
--- /dev/null
+++ b/tests/Pest.php
@@ -0,0 +1,45 @@
+in('src');
+
+/*
+|--------------------------------------------------------------------------
+| Expectations
+|--------------------------------------------------------------------------
+|
+| When you're writing tests, you often need to check that values meet certain conditions. The
+| "expect()" function gives you access to a set of "expectations" methods that you can use
+| to assert different things. Of course, you may extend the Expectation API at any time.
+|
+*/
+
+expect()->extend('toBeOne', function () {
+ return $this->toBe(1);
+});
+
+/*
+|--------------------------------------------------------------------------
+| Functions
+|--------------------------------------------------------------------------
+|
+| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
+| project that you don't want to repeat in every file. Here you can also expose helpers as
+| global functions to help you to reduce the number of lines of code in your test files.
+|
+*/
+
+function something()
+{
+ // ..
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..d9889fe
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,13 @@
+tmpFolder . '/' . $name, $content);
+ }
+}
diff --git a/tests/fixitures/container/sample_file.jpg b/tests/fixitures/container/sample_file.jpg
new file mode 100644
index 0000000..f875e06
Binary files /dev/null and b/tests/fixitures/container/sample_file.jpg differ
diff --git a/tests/src/Container/LocalTest.php b/tests/src/Container/LocalTest.php
index 2d43dca..5ad6835 100755
--- a/tests/src/Container/LocalTest.php
+++ b/tests/src/Container/LocalTest.php
@@ -1,78 +1,65 @@
rrmdir($dir . "/" . $object);
- } else {
- unlink($dir . "/" . $object);
- }
+ if (is_dir($dir)) {
+ $objects = scandir($dir);
+ foreach ($objects as $object) {
+ if ($object != "." && $object != "..") {
+ if (filetype($dir . "/" . $object) == "dir") {
+ rrmdir($dir . "/" . $object);
+ } else {
+ unlink($dir . "/" . $object);
}
}
- reset($objects);
- rmdir($dir);
}
+ reset($objects);
+ rmdir($dir);
}
+}
- protected function setUp(): void
- {
- $this->dir = realpath(__DIR__ . '/../../') . '/fixture/';
- $this->container = new LocalContainer($this->dir);
- }
+beforeEach(function () {
+ $this->dir = realpath(__DIR__ . '/../../') . '/fixture/';
+ $this->container = new LocalContainer($this->dir);
+});
- protected function tearDown(): void
- {
- $this->rrmdir($this->dir);
- }
+afterEach(function () {
+ rrmdir($this->dir);
+});
- function testSave()
- {
- $file = 'subdir/test.txt';
- $this->assertTrue($this->container->save($file, 'cool'));
- $this->assertTrue(file_exists($this->dir . $file));
- $this->assertTrue($this->container->has($file));
- $this->assertEquals('cool', file_get_contents($this->dir . $file));
- }
+test('save', function () {
+ $file = 'subdir/test.txt';
+ expect($this->container->save($file, 'cool'))->toBeTrue();
+ expect(file_exists($this->dir . $file))->toBeTrue();
+ expect($this->container->has($file))->toBeTrue();
+ expect(file_get_contents($this->dir . $file))->toEqual('cool');
+});
- function testDelete()
- {
- $file = 'subdir/test.txt';
- $this->container->save($file, 'cool');
- $this->assertTrue(file_exists($this->dir . $file));
- $this->assertTrue($this->container->delete($file));
- $this->assertFalse(file_exists($this->dir . $file));
- }
+test('delete', function () {
+ $file = 'subdir/test.txt';
+ $this->container->save($file, 'cool');
+ expect(file_exists($this->dir . $file))->toBeTrue();
+ expect($this->container->delete($file))->toBeTrue();
+ expect(file_exists($this->dir . $file))->toBeFalse();
+});
- function testDeleteInexistingFile()
- {
- $file = 'subdir/test.txt';
- $this->assertTrue($this->container->delete($file));
- }
+test('delete inexisting file', function () {
+ $file = 'subdir/test.txt';
+ expect($this->container->delete($file))->toBeTrue();
+});
- function testMoveUploadedFile()
- {
- $file = 'test.txt';
- $file2 = 'sub/test.txt';
- $this->container->save($file, 'cool');
- $this->assertTrue($this->container->moveUploadedFile($this->dir . $file, $file2));
- $this->assertEquals('cool', file_get_contents($this->dir . $file2));
- }
+test('move uploaded file', function () {
+ $file = 'test.txt';
+ $file2 = 'sub/test.txt';
+ $this->container->save($file, 'cool');
+ expect($this->container->moveUploadedFile($this->dir . $file, $file2))->toBeTrue();
+ expect(file_get_contents($this->dir . $file2))->toEqual('cool');
+});
- function testMoveMissingUploadedFile()
- {
- $file = 'subdir/test.txt';
- $this->assertFalse($this->container->moveUploadedFile($this->dir . $file, $file));
- }
-
-}
+test('move missing uploaded file', function () {
+ $file = 'subdir/test.txt';
+ expect($this->container->moveUploadedFile($this->dir . $file, $file))->toBeFalse();
+});
diff --git a/tests/src/HandlerAggregateTest.php b/tests/src/HandlerAggregateTest.php
index cc59b4a..da761b5 100755
--- a/tests/src/HandlerAggregateTest.php
+++ b/tests/src/HandlerAggregateTest.php
@@ -1,99 +1,81 @@
tmpFolder = realpath(__DIR__ . '/../fixitures/');
+beforeEach(function () {
+ $this->tmpFolder = realpath(__DIR__ . '/../fixitures/');
+ if (!is_dir($this->tmpFolder)) {
@mkdir($this->tmpFolder . '/container');
- $this->uploadFolder = realpath(__DIR__ . '/../fixitures/container/');
+ }
+ $this->uploadFolder = realpath(__DIR__ . '/../fixitures/container/');
- $this->agg = new HandlerAggregate();
- $this->agg->addHandler(
- 'user_picture',
- new Handler(
- $this->uploadFolder . '/user_picture', array(
- Handler::OPTION_PREFIX => '',
- Handler::OPTION_OVERWRITE => false,
- Handler::OPTION_AUTOCONFIRM => false
- )
+ $this->agg = new HandlerAggregate();
+ $this->agg->addHandler(
+ 'user_picture',
+ new Handler(
+ $this->uploadFolder . '/user_picture', array(
+ Handler::OPTION_PREFIX => '',
+ Handler::OPTION_OVERWRITE => false,
+ Handler::OPTION_AUTOCONFIRM => false
)
- );
- $this->agg->addHandler(
- 'resume',
- new Handler(
- $this->uploadFolder . '/resume', array(
- Handler::OPTION_PREFIX => '',
- Handler::OPTION_OVERWRITE => false,
- Handler::OPTION_AUTOCONFIRM => false
- )
+ )
+ );
+ $this->agg->addHandler(
+ 'resume',
+ new Handler(
+ $this->uploadFolder . '/resume', array(
+ Handler::OPTION_PREFIX => '',
+ Handler::OPTION_OVERWRITE => false,
+ Handler::OPTION_AUTOCONFIRM => false
)
- );
- $this->agg->addHandler(
- 'portfolio[photos]',
- new Handler(
- $this->uploadFolder . '/photo', array(
- Handler::OPTION_PREFIX => '',
- Handler::OPTION_OVERWRITE => false,
- Handler::OPTION_AUTOCONFIRM => false
- )
+ )
+ );
+ $this->agg->addHandler(
+ 'portfolio[photos]',
+ new Handler(
+ $this->uploadFolder . '/photo', array(
+ Handler::OPTION_PREFIX => '',
+ Handler::OPTION_OVERWRITE => false,
+ Handler::OPTION_AUTOCONFIRM => false
)
- );
- }
+ )
+ );
+});
- protected function tearDown(): void
- {
- $files = glob($this->uploadFolder . '/*'); // get all file names
- foreach ($files as $file) { // iterate files
- if (is_file($file)) {
- unlink($file);
- } // delete file
- }
+afterEach(function () {
+ $files = glob($this->uploadFolder . '/*');
+ // get all file names
+ foreach ($files as $file) { // iterate files
+ if (is_file($file)) {
+ unlink($file);
+ } // delete file
}
+});
- function createTemporaryFile($name, $content = "")
- {
- file_put_contents($this->tmpFolder . '/' . $name, $content);
- }
+test('process', function () {
+ $this->createTemporaryFile('abc.tmp');
+ $this->createTemporaryFile('def.tmp');
+ $files = array(
+ 'user_picture' => array(
+ 'name' => 'pic.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ ),
+ 'resume' => array(
+ 'name' => 'resume.doc',
+ 'tmp_name' => $this->tmpFolder . '/def.tmp'
+ )
+ );
+ $result = $this->agg->process($files);
- function testProcess() {
- $this->createTemporaryFile('abc.tmp');
- $this->createTemporaryFile('def.tmp');
- $files = array(
- 'user_picture' => array(
- 'name' => 'pic.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
- ),
- 'resume' => array(
- 'name' => 'resume.doc',
- 'tmp_name' => $this->tmpFolder . '/def.tmp'
- )
- );
- $result = $this->agg->process($files);
-
- $this->assertTrue(file_exists($this->uploadFolder . '/user_picture/' . $result['user_picture']->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/user_picture/' . $result['user_picture']->name . '.lock'));
+ expect(file_exists($this->uploadFolder . '/user_picture/' . $result['user_picture']->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/user_picture/' . $result['user_picture']->name . '.lock'))->toBeTrue();
- $result->confirm();
- $this->assertFalse(file_exists($this->uploadFolder . '/user_picture/' . $result['user_picture']->name . '.lock'));
- }
+ $result->confirm();
+ expect(file_exists($this->uploadFolder . '/user_picture/' . $result['user_picture']->name . '.lock'))->toBeFalse();
+});
- function testIterator() {
- $handlers = $this->agg->getIterator();
- $this->assertTrue($handlers['user_picture'] instanceof Handler);
- }
-}
+test('iterator', function () {
+ $handlers = $this->agg->getIterator();
+ expect($handlers['user_picture'] instanceof Handler)->toBeTrue();
+});
diff --git a/tests/src/HandlerTest.php b/tests/src/HandlerTest.php
index 5329bdd..afcee52 100755
--- a/tests/src/HandlerTest.php
+++ b/tests/src/HandlerTest.php
@@ -1,409 +1,371 @@
tmpFolder = realpath(__DIR__ . '/../fixitures/');
+beforeEach(function () {
+ $this->tmpFolder = realpath(__DIR__ . '/../fixitures/');
+ if (!is_dir($this->tmpFolder)) {
@mkdir($this->tmpFolder . '/container');
- $this->uploadFolder = realpath(__DIR__ . '/../fixitures/container/');
- $this->handler = new Handler(
- $this->uploadFolder, array(
- Handler::OPTION_PREFIX => '',
- Handler::OPTION_OVERWRITE => false,
- Handler::OPTION_AUTOCONFIRM => false
- )
- );
- }
-
- protected function tearDown(): void
- {
- $files = glob($this->uploadFolder . '/*'); // get all file names
- foreach ($files as $file) { // iterate files
- if (is_file($file)) {
- unlink($file);
- } // delete file
- }
- }
-
- function createTemporaryFile($name, $content = "")
- {
- file_put_contents($this->tmpFolder . '/' . $name, $content);
}
-
- function testBasicUploadWithPrefix()
- {
- $this->handler->setPrefix('subfolder/');
- $this->createTemporaryFile('abc.tmp');
-
- $result = $this->handler->process(
- array(
- 'name' => 'abc.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
- )
- );
-
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
- // tearDown does not clean the subfolders
- unlink($this->uploadFolder . '/' . $result->name);
- unlink($this->uploadFolder . '/' . $result->name . '.lock');
+ $this->uploadFolder = realpath(__DIR__ . '/../fixitures/container/');
+ $this->handler = new Handler(
+ $this->uploadFolder, array(
+ Handler::OPTION_PREFIX => '',
+ Handler::OPTION_OVERWRITE => false,
+ Handler::OPTION_AUTOCONFIRM => false
+ )
+ );
+});
+
+afterEach(function () {
+ $files = glob($this->uploadFolder . '/*');
+ // get all file names
+ foreach ($files as $file) { // iterate files
+ if (is_file($file)) {
+ unlink($file);
+ } // delete file
}
-
- function testUploadOverwrite()
- {
- $this->createTemporaryFile('abc.tmp', 'first_file');
-
- $result = $this->handler->process(
+});
+
+test('basic upload with prefix', function () {
+ $this->handler->setPrefix('subfolder/');
+ $this->createTemporaryFile('abc.tmp');
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'abc.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ )
+ );
+
+ expect(file_exists($this->uploadFolder . '/' . $result->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeTrue();
+
+ // tearDown does not clean the subfolders
+ unlink($this->uploadFolder . '/' . $result->name);
+ unlink($this->uploadFolder . '/' . $result->name . '.lock');
+});
+
+test('upload overwrite', function () {
+ $this->createTemporaryFile('abc.tmp', 'first_file');
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'abc.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ )
+ );
+
+ expect('first_file')->toEqual(file_get_contents($this->uploadFolder . '/abc.jpg'));
+
+ // no overwrite, the first upload should be preserved
+ $this->handler->setOverwrite(false);
+ $this->createTemporaryFile('abc.tmp', 'second_file');
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'abc.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ )
+ );
+
+ expect('first_file')->toEqual(file_get_contents($this->uploadFolder . '/abc.jpg'));
+
+ // overwrite, the first uploaded file should be changed
+ $this->handler->setOverwrite(true);
+ $this->createTemporaryFile('abc.tmp', 'second_file');
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'abc.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ )
+ );
+
+ expect('second_file')->toEqual(file_get_contents($this->uploadFolder . '/abc.jpg'));
+});
+
+test('upload autoconfirm', function () {
+ $this->handler->setAutoconfirm(true);
+ $this->createTemporaryFile('abc.tmp', 'first_file');
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'abc.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ )
+ );
+
+ expect(file_exists($this->uploadFolder . '/' . $result->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeFalse();
+});
+
+test('single upload confirmation', function () {
+ $this->createTemporaryFile('abc.tmp', 'first_file');
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'abc.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ )
+ );
+
+ expect(file_exists($this->uploadFolder . '/' . $result->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeTrue();
+
+ $result->confirm();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeFalse();
+});
+
+test('single upload clearing', function () {
+ $this->createTemporaryFile('abc.tmp', 'first_file');
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'abc.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ )
+ );
+
+ expect(file_exists($this->uploadFolder . '/' . $result->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeTrue();
+
+ $fileName = $result->name;
+ $result->clear();
+
+ expect(file_exists($this->uploadFolder . '/' . $fileName))->toBeFalse();
+ expect(file_exists($this->uploadFolder . '/' . $fileName . '.lock'))->toBeFalse();
+});
+
+test('multi upload', function () {
+ $this->createTemporaryFile('abc.tmp', 'first_file');
+ $this->createTemporaryFile('def.tmp', 'first_file');
+
+ // array is already properly formatted
+ $result = $this->handler->process(
+ array(
array(
'name' => 'abc.jpg',
'tmp_name' => $this->tmpFolder . '/abc.tmp'
- )
- );
-
- $this->assertEquals(file_get_contents($this->uploadFolder . '/abc.jpg'), 'first_file');
-
- // no overwrite, the first upload should be preserved
- $this->handler->setOverwrite(false);
- $this->createTemporaryFile('abc.tmp', 'second_file');
-
- $result = $this->handler->process(
- array(
- 'name' => 'abc.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
- )
- );
-
- $this->assertEquals(file_get_contents($this->uploadFolder . '/abc.jpg'), 'first_file');
-
- // overwrite, the first uploaded file should be changed
- $this->handler->setOverwrite(true);
- $this->createTemporaryFile('abc.tmp', 'second_file');
-
- $result = $this->handler->process(
- array(
- 'name' => 'abc.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
- )
- );
-
- $this->assertEquals(file_get_contents($this->uploadFolder . '/abc.jpg'), 'second_file');
- }
-
- function testUploadAutoconfirm()
- {
- $this->handler->setAutoconfirm(true);
- $this->createTemporaryFile('abc.tmp', 'first_file');
-
- $result = $this->handler->process(
- array(
- 'name' => 'abc.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
- )
- );
-
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name));
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
- }
-
- function testSingleUploadConfirmation()
- {
- $this->createTemporaryFile('abc.tmp', 'first_file');
-
- $result = $this->handler->process(
- array(
- 'name' => 'abc.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
- )
- );
-
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
-
- $result->confirm();
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
- }
-
-
- function testSingleUploadClearing()
- {
- $this->createTemporaryFile('abc.tmp', 'first_file');
-
- $result = $this->handler->process(
+ ),
array(
- 'name' => 'abc.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ 'name' => 'def.jpg',
+ 'tmp_name' => $this->tmpFolder . '/def.tmp'
)
- );
+ )
+ );
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
+ expect($result->isValid())->toBeTrue();
- $fileName = $result->name;
- $result->clear();
-
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $fileName));
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $fileName . '.lock'));
+ # var_dump(glob($this->uploadFolder . '/*'));
+ foreach ($result as $file) {
+ expect(file_exists($this->uploadFolder . '/' . $file->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $file->name . '.lock'))->toBeTrue();
}
- function testMultiUpload()
- {
- $this->createTemporaryFile('abc.tmp', 'first_file');
- $this->createTemporaryFile('def.tmp', 'first_file');
-
- // array is already properly formatted
- $result = $this->handler->process(
- array(
- array(
- 'name' => 'abc.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
- ),
- array(
- 'name' => 'def.jpg',
- 'tmp_name' => $this->tmpFolder . '/def.tmp'
- )
- )
- );
-
- $this->assertTrue($result->isValid());
-
-# var_dump(glob($this->uploadFolder . '/*'));
- foreach ($result as $file) {
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $file->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $file->name . '.lock'));
- }
-
- // confirmation removes the .lock files
- $result->confirm();
- foreach ($result as $file) {
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $file->name));
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $file->name . '.lock'));
- }
-
- // clearing removes the uploaded files and their locks (which are already removed)
- $result->clear();
- foreach ($result as $file) {
- $this->assertNull($file->name);
- }
- }
-
- function testOriginalMultiUpload()
- {
- $this->createTemporaryFile('abc.tmp', 'first_file');
- $this->createTemporaryFile('def.tmp', 'first_file');
-
- // array is as provided by PHP
- $result = $this->handler->process(
- array(
- 'name' => array(
- 'abc.jpg',
- 'def.jpg',
- ),
- 'tmp_name' => array(
- $this->tmpFolder . '/abc.tmp',
- $this->tmpFolder . '/def.tmp'
- ),
- )
- );
-
- $this->assertEquals(count($result), 2);
- foreach ($result as $file) {
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $file->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $file->name . '.lock'));
- }
+ // confirmation removes the .lock files
+ $result->confirm();
+ foreach ($result as $file) {
+ expect(file_exists($this->uploadFolder . '/' . $file->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $file->name . '.lock'))->toBeFalse();
}
- function testWrongFilesArray()
- {
- $result = $this->handler->process(array('names' => 'abc.jpg'));
- $this->assertEquals(count($result), 0);
+ // clearing removes the uploaded files and their locks (which are already removed)
+ $result->clear();
+ foreach ($result as $file) {
+ expect($file->name)->toBeNull();
}
-
- function testExceptionTrwonForInvalidContainer()
- {
- $this->expectException('Sirius\Upload\Exception\InvalidContainerException');
-
- $handler = new Handler(new \stdClass());
+});
+
+test('original multi upload', function () {
+ $this->createTemporaryFile('abc.tmp', 'first_file');
+ $this->createTemporaryFile('def.tmp', 'first_file');
+
+ // array is as provided by PHP
+ $result = $this->handler->process(
+ array(
+ 'name' => array(
+ 'abc.jpg',
+ 'def.jpg',
+ ),
+ 'tmp_name' => array(
+ $this->tmpFolder . '/abc.tmp',
+ $this->tmpFolder . '/def.tmp'
+ ),
+ )
+ );
+
+ expect(2)->toEqual(count($result));
+ foreach ($result as $file) {
+ expect(file_exists($this->uploadFolder . '/' . $file->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $file->name . '.lock'))->toBeTrue();
}
+});
+
+test('wrong files array', function () {
+ $result = $this->handler->process(array('names' => 'abc.jpg'));
+ expect(0)->toEqual(count($result));
+});
+
+test('exception trwon for invalid container', function () {
+ $this->expectException('Sirius\Upload\Exception\InvalidContainerException');
+
+ $handler = new Handler(new \stdClass());
+});
+
+test('single upload validation', function () {
+ $this->createTemporaryFile('abc.tmp', 'non image file');
+
+ // uploaded files must be an image
+ $this->handler->addRule(Handler::RULE_IMAGE);
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'abc.jpg',
+ 'tmp_name' => $this->tmpFolder . '/abc.tmp'
+ )
+ );
+
+ expect($result->isValid())->toBeFalse();
+ expect(1)->toEqual(count($result->getMessages()));
+ expect($result->nonAttribute)->toBeNull();
+});
+
+test('multi upload validation', function () {
+
+ $this->createTemporaryFile('abc.tmp', 'first_file');
+ $this->createTemporaryFile('def.tmp', 'second_file');
+
+ // uploaded file must be an image
+ $this->handler->addRule(Handler::RULE_IMAGE);
+
+ // array is as provided by PHP
+ $result = $this->handler->process(
+ array(
+ 'name' => array(
+ 'abc.jpg',
+ 'def.jpg',
+ ),
+ 'tmp_name' => array(
+ $this->tmpFolder . '/abc.tmp',
+ $this->tmpFolder . '/def.tmp'
+ ),
+ )
+ );
+ $messages = $result->getMessages();
+
+ expect($result->isValid())->toBeFalse();
+ expect(2)->toEqual(count($messages));
+ expect(1)->toEqual(count($messages[0]));
+});
+
+test('custom sanitization callback', function () {
+ $this->handler->setSanitizerCallback(function ($name) {
+ return preg_replace('/[^A-Za-z0-9\.]+/', '-', strtolower($name));
+ });
+ $this->createTemporaryFile('ABC 123.tmp', 'non image file');
+
+ $result = $this->handler->process(
+ array(
+ 'name' => 'ABC 123.tmp',
+ 'tmp_name' => $this->tmpFolder . '/ABC 123.tmp'
+ )
+ );
+
+ expect(file_exists($this->uploadFolder . '/abc-123.tmp'))->toBeTrue();
+});
+
+test('psr7 uploaded files', function () {
+ $files = ['abc.tmp', 'def.tmp'];
+
+ $psr7Files = [];
+
+ foreach ($files as $file) {
+ $this->createTemporaryFile($file, 'first_file');
- function testSingleUploadValidation()
- {
- $this->createTemporaryFile('abc.tmp', 'non image file');
-
- // uploaded files must be an image
- $this->handler->addRule(Handler::RULE_IMAGE);
-
- $result = $this->handler->process(
- array(
- 'name' => 'abc.jpg',
- 'tmp_name' => $this->tmpFolder . '/abc.tmp'
- )
+ $factory = new StreamFactory();
+ $stream = $factory->createStreamFromFile($this->tmpFolder . '/' . $file);
+ $psr7Files[] = new UploadedFile(
+ $stream,
+ $stream->getSize(),
+ UPLOAD_ERR_OK,
+ $file
);
-
- $this->assertFalse($result->isValid());
- $this->assertEquals(count($result->getMessages()), 1);
- $this->assertNull($result->nonAttribute);
}
- function testMultiUploadValidation()
- {
- $this->createTemporaryFile('abc.tmp', 'first_file');
- $this->createTemporaryFile('def.tmp', 'second_file');
-
- // uploaded file must be an image
- $this->handler->addRule(Handler::RULE_IMAGE);
+ $result = $this->handler->process($psr7Files);
- // array is as provided by PHP
- $result = $this->handler->process(
- array(
- 'name' => array(
- 'abc.jpg',
- 'def.jpg',
- ),
- 'tmp_name' => array(
- $this->tmpFolder . '/abc.tmp',
- $this->tmpFolder . '/def.tmp'
- ),
- )
- );
- $messages = $result->getMessages();
+ foreach ($result as $item) {
+ expect(file_exists($this->uploadFolder . '/' . $item->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $item->name . '.lock'))->toBeTrue();
- $this->assertFalse($result->isValid());
- $this->assertEquals(count($messages), 2);
- $this->assertEquals(count($messages[0]), 1);
+ $item->confirm();
+ expect(file_exists($this->uploadFolder . '/' . $item->name . '.lock'))->toBeFalse();
}
+});
- function testCustomSanitizationCallback()
- {
- $this->handler->setSanitizerCallback(function ($name, $file) {
- return preg_replace(
- '/[^A-Za-z0-9\.]+/', '-',
- substr(md5_file($file['tmp_name']), 0, 8) . '-' . strtolower($name)
- );
- });
- $this->createTemporaryFile('ABC 123.tmp', 'non image file');
-
- $result = $this->handler->process(
- array(
- 'name' => 'ABC 123.tmp',
- 'tmp_name' => $this->tmpFolder . '/ABC 123.tmp'
- )
- );
+test('single psr7 uploaded file', function () {
+ $file = 'abc.tmp';
- $this->assertTrue(file_exists($this->uploadFolder . '/35d41ded-abc-123.tmp'));
- }
-
- function testExceptionThrownForInvalidSanitizationCallback()
- {
- $this->expectException('InvalidArgumentException');
- $this->handler->setSanitizerCallback('not a callable');
- }
+ $this->createTemporaryFile($file, 'first_file');
- function testPsr7UploadedFiles()
- {
- $files = ['abc.tmp', 'def.tmp'];
+ $factory = new StreamFactory();
+ $stream = $factory->createStreamFromFile($this->tmpFolder . '/' . $file);
+ $psr7File = new UploadedFile(
+ $stream,
+ $stream->getSize(),
+ UPLOAD_ERR_OK,
+ $file
+ );
- $psr7Files = [];
+ $result = $this->handler->process($psr7File);
- foreach ($files as $file) {
- $this->createTemporaryFile($file, 'first_file');
+ expect(file_exists($this->uploadFolder . '/' . $result->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeTrue();
- $factory = new StreamFactory();
- $stream = $factory->createStreamFromFile($this->tmpFolder . '/' . $file);
- $psr7Files[] = new UploadedFile(
- $stream,
- $stream->getSize(),
- UPLOAD_ERR_OK,
- $file
- );
- }
+ $result->confirm();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeFalse();
+});
- $result = $this->handler->process($psr7Files);
+test('symfony uploaded files', function () {
+ $files = ['abc.tmp', 'def.tmp'];
- foreach ($result as $item) {
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $item->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $item->name . '.lock'));
-
- $item->confirm();
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $item->name . '.lock'));
- }
- }
-
- function testSinglePsr7UploadedFile()
- {
- $file = 'abc.tmp';
+ $symfonyFiles = [];
+ foreach ($files as $file) {
$this->createTemporaryFile($file, 'first_file');
- $factory = new StreamFactory();
- $stream = $factory->createStreamFromFile($this->tmpFolder . '/' . $file);
- $psr7File = new UploadedFile(
- $stream,
- $stream->getSize(),
- UPLOAD_ERR_OK,
- $file
- );
-
- $result = $this->handler->process($psr7File);
-
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
-
- $result->confirm();
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
+ $symfonyFiles[] = new SymfonyUploadedFile($this->tmpFolder . '/' . $file, $file);
}
- function testSymfonyUploadedFiles()
- {
- $files = ['abc.tmp', 'def.tmp'];
+ $result = $this->handler->process($symfonyFiles);
- $symfonyFiles = [];
+ foreach ($result as $item) {
+ expect(file_exists($this->uploadFolder . '/' . $item->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $item->name . '.lock'))->toBeTrue();
- foreach ($files as $file) {
- $this->createTemporaryFile($file, 'first_file');
-
- $symfonyFiles[] = new SymfonyUploadedFile($this->tmpFolder . '/' . $file, $file);
- }
-
- $result = $this->handler->process($symfonyFiles);
-
- foreach ($result as $item) {
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $item->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $item->name . '.lock'));
-
- $item->confirm();
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $item->name . '.lock'));
- }
+ $item->confirm();
+ expect(file_exists($this->uploadFolder . '/' . $item->name . '.lock'))->toBeFalse();
}
+});
- function testSingleSymfonyUploadedFile()
- {
- $file = 'abc.tmp';
+test('single symfony uploaded file', function () {
+ $file = 'abc.tmp';
- $this->createTemporaryFile($file, 'first_file');
-
- $symfonyFile = new SymfonyUploadedFile($this->tmpFolder . '/' . $file, $file);
+ $this->createTemporaryFile($file, 'first_file');
- $result = $this->handler->process($symfonyFile);
+ $symfonyFile = new SymfonyUploadedFile($this->tmpFolder . '/' . $file, $file);
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name));
- $this->assertTrue(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
+ $result = $this->handler->process($symfonyFile);
- $result->confirm();
- $this->assertFalse(file_exists($this->uploadFolder . '/' . $result->name . '.lock'));
- }
+ expect(file_exists($this->uploadFolder . '/' . $result->name))->toBeTrue();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeTrue();
-}
+ $result->confirm();
+ expect(file_exists($this->uploadFolder . '/' . $result->name . '.lock'))->toBeFalse();
+});