Laravel version update

Laravel version update
This commit is contained in:
Manish Verma
2018-08-06 18:48:58 +05:30
parent d143048413
commit 126fbb0255
13678 changed files with 1031482 additions and 778530 deletions

View File

@@ -0,0 +1,8 @@
# Ide
.idea
# Composer
composer.lock
/vendor
/build

View File

@@ -0,0 +1,13 @@
language: php
php:
- 5.3
- 5.4
- 5.5
before_script:
- composer install --dev --no-interaction --prefer-source
script:
- mkdir -p build
- phpunit --configuration travis.phpunit.xml

View File

@@ -0,0 +1,5 @@
# 1.0.0
## Breaking Changes
* Flow\Exception was replaced by Flow\FileOpenException and Flow\FileLockException
* php requirement was changed to >=5.4
* if chunk was not found, 204 status is returned instead of 404

20
vendor/flowjs/flow-php-server/LICENSE vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Aidas Klimas
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.

98
vendor/flowjs/flow-php-server/README.md vendored Normal file
View File

@@ -0,0 +1,98 @@
flow.js php server [![Build Status](https://travis-ci.org/flowjs/flow-php-server.png?branch=master)](https://travis-ci.org/flowjs/flow-php-server) [![Coverage Status](https://coveralls.io/repos/flowjs/flow-php-server/badge.png?branch=master)](https://coveralls.io/r/flowjs/flow-php-server?branch=master)
=======================
PHP library for handling chunk uploads. Library contains helper methods for:
* Testing if uploaded file chunk exists.
* Validating file chunk
* Creating separate chunks folder
* Validating uploaded chunks
* Merging all chunks to a single file
This library is compatible with HTML5 file upload library: https://github.com/flowjs/flow.js
How to get started?
--------------
Setup Composer: https://getcomposer.org/doc/00-intro.md
Run this command in your project:
```
composer require flowjs/flow-php-server
```
This will create a vendor directory for you, which contains an autoload.php file.
Create a new php file named `upload.php`:
```php
//Path to autoload.php from current location
require_once './vendor/autoload.php';
$config = new \Flow\Config();
$config->setTempDir('./chunks_temp_folder');
$request = new \Flow\Request();
if (\Flow\Basic::save('./' . $request->getIdentifier(), $config, $request)) {
// file saved successfully and can be accessed at './final_file_destination'
} else {
// This is not a final chunk or request is invalid, continue to upload.
}
```
Make sure that `./chunks_temp_folder` path exists and is writable. All chunks will be saved in this folder.
If you are stuck with this example, please read this issue: [How to use the flow-php-server](https://github.com/flowjs/flow-php-server/issues/3#issuecomment-46979467)
Advanced Usage
--------------
```php
$config = new \Flow\Config();
$config->setTempDir('./chunks_temp_folder');
$file = new \Flow\File($config);
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if ($file->checkChunk()) {
header("HTTP/1.1 200 Ok");
} else {
header("HTTP/1.1 204 No Content");
return ;
}
} else {
if ($file->validateChunk()) {
$file->saveChunk();
} else {
// error, invalid chunk upload request, retry
header("HTTP/1.1 400 Bad Request");
return ;
}
}
if ($file->validateFile() && $file->save('./final_file_name')) {
// File upload was completed
} else {
// This is not a final chunk, continue to upload
}
```
Delete unfinished files
-----------------------
For this you should setup cron, which would check each chunk upload time.
If chunk is uploaded long time ago, then chunk should be deleted.
Helper method for checking this:
```php
\Flow\Uploader::pruneChunks('./chunks_folder');
```
Cron task can be avoided by using random function execution.
```php
if (1 == mt_rand(1, 100)) {
\Flow\Uploader::pruneChunks('./chunks_folder');
}
```
Contribution
------------
Your participation in development is very welcome!
To ensure consistency throughout the source code, keep these rules in mind as you are working:
* All features or bug fixes must be tested by one or more specs.
* Your code should follow [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding style guide

View File

@@ -0,0 +1,35 @@
{
"name": "flowjs/flow-php-server",
"description": "PHP library for handling chunk uploads. Works with flow.js html5 file uploads.",
"license": "MIT",
"authors": [
{
"name": "Aidas Klimas",
"email": "aidaskk@gmail.com"
}
],
"keywords": [
"flow.js",
"flow",
"resumable.js",
"resumable",
"upload",
"file upload",
"html5 file upload",
"chunks"
],
"require": {
"php": ">=5.3"
},
"require-dev": {
"mikey179/vfsStream": "v1.2.0",
"league/phpunit-coverage-listener": "~1.1",
"fabpot/php-cs-fixer": "dev-master",
"phpunit/phpunit": "4.*"
},
"autoload": {
"psr-0": {
"Flow": "src"
}
}
}

4
vendor/flowjs/flow-php-server/phpunit vendored Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
php ./vendor/phpunit/phpunit/phpunit "$@"

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="test/bootstrap.php"
>
<testsuites>
<testsuite name="Flow Test Suite">
<directory>./test/Unit/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/Flow/</directory>
<exclude>
<file>./src/Flow/Basic.php</file>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,67 @@
<?php
namespace Flow;
class Autoloader
{
/**
* Directory path
*
* @var string
*/
private $dir;
/**
* Constructor
*
* @param string|null $dir
*/
public function __construct($dir = null)
{
if (is_null($dir)) {
$dir = __DIR__.'/..';
}
$this->dir = $dir;
}
/**
* Return directory path
*
* @return string
*/
public function getDir()
{
return $this->dir;
}
/**
* Register
*
* @codeCoverageIgnore
* @param string|null $dir
*/
public static function register($dir = null)
{
ini_set('unserialize_callback_func', 'spl_autoload_call');
spl_autoload_register(array(new self($dir), 'autoload'));
}
/**
* Handles autoloading of classes
*
* @param string $class A class name
*
* @return boolean Returns true if the class has been loaded
*/
public function autoload($class)
{
if (0 !== strpos($class, 'Flow')) {
return;
}
if (file_exists($file = $this->dir.'/'.str_replace('\\', '/', $class).'.php')) {
require $file;
}
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Flow;
/**
* Class Basic
*
* Example for handling basic uploads
*
* @package Flow
*/
class Basic
{
/**
* @param string $destination where to save file
* @param string|ConfigInterface $config
* @param RequestInterface $request optional
* @return bool
*/
public static function save($destination, $config, RequestInterface $request = null)
{
if (!$config instanceof ConfigInterface) {
$config = new Config(array(
'tempDir' => $config,
));
}
$file = new File($config, $request);
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if ($file->checkChunk()) {
header("HTTP/1.1 200 Ok");
} else {
// The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.
header("HTTP/1.1 204 No Content");
return false;
}
} else {
if ($file->validateChunk()) {
$file->saveChunk();
} else {
// error, invalid chunk upload request, retry
header("HTTP/1.1 400 Bad Request");
return false;
}
}
if ($file->validateFile() && $file->save($destination)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace Flow;
class Config implements ConfigInterface
{
/**
* Config
*
* @var array
*/
private $config;
/**
* Controller
*
* @param array $config
*/
public function __construct($config = array())
{
$this->config = $config;
}
/**
* Set path to temporary directory for chunks storage
*
* @param $path
*/
public function setTempDir($path)
{
$this->config['tempDir'] = $path;
}
/**
* Get path to temporary directory for chunks storage
*
* @return string
*/
public function getTempDir()
{
return isset($this->config['tempDir']) ? $this->config['tempDir'] : '';
}
/**
* Set chunk identifier
*
* @param callable $callback
*/
public function setHashNameCallback($callback)
{
$this->config['hashNameCallback'] = $callback;
}
/**
* Generate chunk identifier
*
* @return callable
*/
public function getHashNameCallback()
{
return isset($this->config['hashNameCallback']) ? $this->config['hashNameCallback'] : '\Flow\Config::hashNameCallback';
}
/**
* Callback to pre-process chunk
*
* @param callable $callback
*/
public function setPreprocessCallback($callback)
{
$this->config['preprocessCallback'] = $callback;
}
/**
* Callback to pre-process chunk
*
* @return callable|null
*/
public function getPreprocessCallback()
{
return isset($this->config['preprocessCallback']) ? $this->config['preprocessCallback'] : null;
}
/**
* Delete chunks on save
*
* @param bool $delete
*/
public function setDeleteChunksOnSave($delete)
{
$this->config['deleteChunksOnSave'] = $delete;
}
/**
* Delete chunks on save
*
* @return bool
*/
public function getDeleteChunksOnSave()
{
return isset($this->config['deleteChunksOnSave']) ? $this->config['deleteChunksOnSave'] : true;
}
/**
* Generate chunk identifier
*
* @param RequestInterface $request
*
* @return string
*/
public static function hashNameCallback(RequestInterface $request)
{
return sha1($request->getIdentifier());
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Flow;
interface ConfigInterface
{
/**
* Get path to temporary directory for chunks storage
*
* @return string
*/
public function getTempDir();
/**
* Generate chunk identifier
*
* @return callable
*/
public function getHashNameCallback();
/**
* Callback to pre-process chunk
*
* @param callable $callback
*/
public function setPreprocessCallback($callback);
/**
* Callback to preprocess chunk
*
* @return callable|null
*/
public function getPreprocessCallback();
/**
* Delete chunks on save
*
* @param bool $delete
*/
public function setDeleteChunksOnSave($delete);
/**
* Delete chunks on save
*
* @return bool
*/
public function getDeleteChunksOnSave();
}

View File

@@ -0,0 +1,233 @@
<?php
namespace Flow;
class File
{
/**
* @var RequestInterface
*/
protected $request;
/**
* @var ConfigInterface
*/
private $config;
/**
* File hashed unique identifier
*
* @var string
*/
private $identifier;
/**
* Constructor
*
* @param ConfigInterface $config
* @param RequestInterface $request
*/
public function __construct(ConfigInterface $config, RequestInterface $request = null)
{
$this->config = $config;
if ($request === null) {
$request = new Request();
}
$this->request = $request;
$this->identifier = call_user_func($this->config->getHashNameCallback(), $request);
}
/**
* Get file identifier
*
* @return string
*/
public function getIdentifier()
{
return $this->identifier;
}
/**
* Return chunk path
*
* @param int $index
*
* @return string
*/
public function getChunkPath($index)
{
return $this->config->getTempDir().DIRECTORY_SEPARATOR.basename($this->identifier).'_'. (int) $index;
}
/**
* Check if chunk exist
*
* @return bool
*/
public function checkChunk()
{
return file_exists($this->getChunkPath($this->request->getCurrentChunkNumber()));
}
/**
* Validate file request
*
* @return bool
*/
public function validateChunk()
{
$file = $this->request->getFile();
if (!$file) {
return false;
}
if (!isset($file['tmp_name']) || !isset($file['size']) || !isset($file['error'])) {
return false;
}
if ($this->request->getCurrentChunkSize() != $file['size']) {
return false;
}
if ($file['error'] !== UPLOAD_ERR_OK) {
return false;
}
return true;
}
/**
* Save chunk
*
* @return bool
*/
public function saveChunk()
{
$file = $this->request->getFile();
return $this->_move_uploaded_file($file['tmp_name'], $this->getChunkPath($this->request->getCurrentChunkNumber()));
}
/**
* Check if file upload is complete
*
* @return bool
*/
public function validateFile()
{
$totalChunks = $this->request->getTotalChunks();
$totalChunksSize = 0;
for ($i = $totalChunks; $i >= 1; $i--) {
$file = $this->getChunkPath($i);
if (!file_exists($file)) {
return false;
}
$totalChunksSize += filesize($file);
}
return $this->request->getTotalSize() == $totalChunksSize;
}
/**
* Merge all chunks to single file
*
* @param string $destination final file location
*
*
* @throws FileLockException
* @throws FileOpenException
* @throws \Exception
*
* @return bool indicates if file was saved
*/
public function save($destination)
{
$fh = fopen($destination, 'wb');
if (!$fh) {
throw new FileOpenException('failed to open destination file: '.$destination);
}
if (!flock($fh, LOCK_EX | LOCK_NB, $blocked)) {
// @codeCoverageIgnoreStart
if ($blocked) {
// Concurrent request has requested a lock.
// File is being processed at the moment.
// Warning: lock is not checked in windows.
return false;
}
// @codeCoverageIgnoreEnd
throw new FileLockException('failed to lock file: '.$destination);
}
$totalChunks = $this->request->getTotalChunks();
try {
$preProcessChunk = $this->config->getPreprocessCallback();
for ($i = 1; $i <= $totalChunks; $i++) {
$file = $this->getChunkPath($i);
$chunk = fopen($file, "rb");
if (!$chunk) {
throw new FileOpenException('failed to open chunk: '.$file);
}
if ($preProcessChunk !== null) {
call_user_func($preProcessChunk, $chunk);
}
stream_copy_to_stream($chunk, $fh);
fclose($chunk);
}
} catch (\Exception $e) {
flock($fh, LOCK_UN);
fclose($fh);
throw $e;
}
if ($this->config->getDeleteChunksOnSave()) {
$this->deleteChunks();
}
flock($fh, LOCK_UN);
fclose($fh);
return true;
}
/**
* Delete chunks dir
*/
public function deleteChunks()
{
$totalChunks = $this->request->getTotalChunks();
for ($i = 1; $i <= $totalChunks; $i++) {
$path = $this->getChunkPath($i);
if (file_exists($path)) {
unlink($path);
}
}
}
/**
* This method is used only for testing
*
* @private
* @codeCoverageIgnore
*
* @param string $filePath
* @param string $destinationPath
*
* @return bool
*/
public function _move_uploaded_file($filePath, $destinationPath)
{
return move_uploaded_file($filePath, $destinationPath);
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Flow;
class FileLockException extends \Exception
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Flow;
class FileOpenException extends \Exception
{
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Flow;
/**
* Class FustyRequest
*
* Imitates single file request as a single chunk file upload
*
* @package Flow
*/
class FustyRequest extends Request
{
private $isFusty = false;
public function __construct($params = null, $file = null)
{
parent::__construct($params, $file);
$this->isFusty = $this->getTotalSize() === null && $this->getFileName() && $this->getFile();
if ($this->isFusty) {
$this->params['flowTotalSize'] = isset($this->file['size']) ? $this->file['size'] : 0;
$this->params['flowTotalChunks'] = 1;
$this->params['flowChunkNumber'] = 1;
$this->params['flowChunkSize'] = $this->params['flowTotalSize'];
$this->params['flowCurrentChunkSize'] = $this->params['flowTotalSize'];
}
}
/**
* Checks if request is formed by fusty flow
* @return bool
*/
public function isFustyFlowRequest()
{
return $this->isFusty;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Flow\Mongo;
use Flow\Config;
/**
* @codeCoverageIgnore
*/
class MongoConfig extends Config implements MongoConfigInterface
{
private $gridFs;
/**
* @param \MongoGridFS $gridFS storage of the upload (and chunks)
*/
function __construct(\MongoGridFS $gridFS)
{
parent::__construct();
$this->gridFs = $gridFS;
}
/**
* @return \MongoGridFS
*/
public function getGridFs()
{
return $this->gridFs;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Flow\Mongo;
use Flow\ConfigInterface;
/**
* @codeCoverageIgnore
*/
interface MongoConfigInterface extends ConfigInterface
{
/**
* @return \MongoGridFS
*/
public function getGridFs();
}

View File

@@ -0,0 +1,181 @@
<?php
namespace Flow\Mongo;
use Flow\File;
use Flow\Request;
use Flow\RequestInterface;
/**
* Notes:
*
* - One should ensure indices on the gridfs collection on the property 'flowIdentifier'.
* - Chunk preprocessor not supported (must not modify chunks size)!
* - Must use 'forceChunkSize=true' on client side.
*
* @codeCoverageIgnore
*/
class MongoFile extends File
{
private $uploadGridFsFile;
/**
* @var MongoConfigInterface
*/
private $config;
function __construct(MongoConfigInterface $config, RequestInterface $request = null)
{
if ($request === null) {
$request = new Request();
}
parent::__construct($config, $request);
$this->config = $config;
}
/**
* return array
*/
protected function getGridFsFile()
{
if (!$this->uploadGridFsFile) {
$gridFsFileQuery = $this->getGridFsFileQuery();
$changed = $gridFsFileQuery;
$changed['flowUpdated'] = new \MongoDate();
$this->uploadGridFsFile = $this->config->getGridFs()->findAndModify($gridFsFileQuery, $changed, null,
['upsert' => true, 'new' => true]);
}
return $this->uploadGridFsFile;
}
/**
* @param $index int|string 1-based
* @return bool
*/
public function chunkExists($index)
{
return $this->config->getGridFs()->chunks->find([
'files_id' => $this->getGridFsFile()['_id'],
'n' => (intval($index) - 1)
])->limit(1)->hasNext();
}
public function checkChunk()
{
return $this->chunkExists($this->request->getCurrentChunkNumber());
}
/**
* Save chunk
* @return bool
* @throws \Exception if upload size is invalid or some other unexpected error occurred.
*/
public function saveChunk()
{
try {
$file = $this->request->getFile();
$chunkQuery = [
'files_id' => $this->getGridFsFile()['_id'],
'n' => intval($this->request->getCurrentChunkNumber()) - 1,
];
$chunk = $chunkQuery;
$data = file_get_contents($file['tmp_name']);
$actualChunkSize = strlen($data);
if ($actualChunkSize > $this->request->getDefaultChunkSize() ||
($actualChunkSize < $this->request->getDefaultChunkSize() &&
$this->request->getCurrentChunkNumber() != $this->request->getTotalChunks())
) {
throw new \Exception("Invalid upload! (size: {$actualChunkSize})");
}
$chunk['data'] = new \MongoBinData($data, 0); // \MongoBinData::GENERIC is not defined for older mongo drivers
$this->config->getGridFs()->chunks->findAndModify($chunkQuery, $chunk, [], ['upsert' => true]);
unlink($file['tmp_name']);
$this->ensureIndices();
return true;
} catch (\Exception $e) {
// try to remove a possibly (partly) stored chunk:
if (isset($chunkQuery)) {
$this->config->getGridFs()->chunks->remove($chunkQuery);
}
throw $e;
}
}
/**
* @return bool
*/
public function validateFile()
{
$totalChunks = $this->request->getTotalChunks();
for ($i = 1; $i <= $totalChunks; $i++) {
if (!$this->chunkExists($i)) {
return false;
}
}
return true;
}
/**
* Merge all chunks to single file
* @param $metadata array additional metadata for final file
* @return \MongoId|bool of saved file or false if file was already saved
* @throws \Exception
*/
public function saveToGridFs($metadata = null)
{
$file = $this->getGridFsFile();
$file['flowStatus'] = 'finished';
$file['metadata'] = $metadata;
$result = $this->config->getGridFs()->findAndModify($this->getGridFsFileQuery(), $file);
// on second invocation no more file can be found, as the flowStatus changed:
if (is_null($result)) {
return false;
} else {
return $file['_id'];
}
}
public function save($destination)
{
throw new \Exception("Must not use 'save' on MongoFile - use 'saveToGridFs'!");
}
public function deleteChunks()
{
// nothing to do, as chunks are directly part of the final file
}
public function ensureIndices()
{
$chunksCollection = $this->config->getGridFs()->chunks;
$indexKeys = ['files_id' => 1, 'n' => 1];
$indexOptions = ['unique' => true, 'background' => true];
if(method_exists($chunksCollection, 'createIndex')) { // only available for PECL mongo >= 1.5.0
$chunksCollection->createIndex($indexKeys, $indexOptions);
} else {
$chunksCollection->ensureIndex($indexKeys, $indexOptions);
}
}
/**
* @return array
*/
protected function getGridFsFileQuery()
{
return [
'flowIdentifier' => $this->request->getIdentifier(),
'flowStatus' => 'uploading',
'filename' => $this->request->getFileName(),
'chunkSize' => intval($this->request->getDefaultChunkSize()),
'length' => intval($this->request->getTotalSize())
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Flow\Mongo;
use Flow\FileOpenException;
/**
* @codeCoverageIgnore
*/
class MongoUploader
{
/**
* Delete chunks older than expiration time.
*
* @param \MongoGridFS $gridFs
* @param int $expirationTime seconds
*
* @throws FileOpenException
*/
public static function pruneChunks($gridFs, $expirationTime = 172800)
{
$result = $gridFs->remove([
'flowUpdated' => ['$lt' => new \MongoDate(time() - $expirationTime)],
'flowStatus' => 'uploading'
]);
if (!$result) {
throw new FileOpenException("Could not remove chunks!");
}
}
}

View File

@@ -0,0 +1,50 @@
Usage
--------------
* Must use 'forceChunkSize=true' on client side.
* Chunk preprocessor not supported.
* One should ensure indices on the gridfs collection on the property 'flowIdentifier'.
Besides the points above, the usage is analogous to the 'normal' flow-php:
```php
$config = new \Flow\Mongo\MongoConfig($yourGridFs);
$file = new \Flow\Mongo\MongoFile($config);
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if ($file->checkChunk()) {
header("HTTP/1.1 200 Ok");
} else {
header("HTTP/1.1 204 No Content");
return ;
}
} else {
if ($file->validateChunk()) {
$file->saveChunk();
} else {
// error, invalid chunk upload request, retry
header("HTTP/1.1 400 Bad Request");
return ;
}
}
if ($file->validateFile()) {
// File upload was completed
$id = $file->saveToGridFs(['your metadata'=>'value']);
if($id) {
//do custom post processing here, $id is the MongoId of the gridfs file
}
} else {
// This is not a final chunk, continue to upload
}
```
Delete unfinished files
-----------------------
For this you should setup cron, which would check each chunk upload time.
If chunk is uploaded long time ago, then chunk should be deleted.
Helper method for checking this:
```php
\Flow\Mongo\MongoUploader::pruneChunks($yourGridFs);
```

View File

@@ -0,0 +1,152 @@
<?php
namespace Flow;
class Request implements RequestInterface
{
/**
* Request parameters
*
* @var array
*/
protected $params;
/**
* File
*
* @var array
*/
protected $file;
/**
* Constructor
*
* @param array|null $params
* @param array|null $file
*/
public function __construct($params = null, $file = null)
{
if ($params === null) {
$params = $_REQUEST;
}
if ($file === null && isset($_FILES['file'])) {
$file = $_FILES['file'];
}
$this->params = $params;
$this->file = $file;
}
/**
* Get parameter value
*
* @param string $name
*
* @return string|int|null
*/
public function getParam($name)
{
return isset($this->params[$name]) ? $this->params[$name] : null;
}
/**
* Get uploaded file name
*
* @return string|null
*/
public function getFileName()
{
return $this->getParam('flowFilename');
}
/**
* Get total file size in bytes
*
* @return int|null
*/
public function getTotalSize()
{
return $this->getParam('flowTotalSize');
}
/**
* Get file unique identifier
*
* @return string|null
*/
public function getIdentifier()
{
return $this->getParam('flowIdentifier');
}
/**
* Get file relative path
*
* @return string|null
*/
public function getRelativePath()
{
return $this->getParam('flowRelativePath');
}
/**
* Get total chunks number
*
* @return int|null
*/
public function getTotalChunks()
{
return $this->getParam('flowTotalChunks');
}
/**
* Get default chunk size
*
* @return int|null
*/
public function getDefaultChunkSize()
{
return $this->getParam('flowChunkSize');
}
/**
* Get current uploaded chunk number, starts with 1
*
* @return int|null
*/
public function getCurrentChunkNumber()
{
return $this->getParam('flowChunkNumber');
}
/**
* Get current uploaded chunk size
*
* @return int|null
*/
public function getCurrentChunkSize()
{
return $this->getParam('flowCurrentChunkSize');
}
/**
* Return $_FILES request
*
* @return array|null
*/
public function getFile()
{
return $this->file;
}
/**
* Checks if request is formed by fusty flow
*
* @return bool
*/
public function isFustyFlowRequest()
{
return false;
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Flow;
interface RequestInterface
{
/**
* Get uploaded file name
*
* @return string
*/
public function getFileName();
/**
* Get total file size in bytes
*
* @return int
*/
public function getTotalSize();
/**
* Get file unique identifier
*
* @return string
*/
public function getIdentifier();
/**
* Get file relative path
*
* @return string
*/
public function getRelativePath();
/**
* Get total chunks number
*
* @return int
*/
public function getTotalChunks();
/**
* Get default chunk size
*
* @return int
*/
public function getDefaultChunkSize();
/**
* Get current uploaded chunk number, starts with 1
*
* @return int
*/
public function getCurrentChunkNumber();
/**
* Get current uploaded chunk size
*
* @return int
*/
public function getCurrentChunkSize();
/**
* Return $_FILES request
*
* @return array|null
*/
public function getFile();
/**
* Checks if request is formed by fusty flow
*
* @return bool
*/
public function isFustyFlowRequest();
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Flow;
class Uploader
{
/**
* Delete chunks older than expiration time.
*
* @param string $chunksFolder
* @param int $expirationTime seconds
*
* @throws FileOpenException
*/
public static function pruneChunks($chunksFolder, $expirationTime = 172800)
{
$handle = opendir($chunksFolder);
if (!$handle) {
throw new FileOpenException('failed to open folder: '.$chunksFolder);
}
while (false !== ($entry = readdir($handle))) {
if ($entry == "." || $entry == ".." || $entry == ".gitignore") {
continue;
}
$path = $chunksFolder.DIRECTORY_SEPARATOR.$entry;
if (is_dir($path)) {
continue;
}
if (time() - filemtime($path) > $expirationTime) {
unlink($path);
}
}
closedir($handle);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Unit;
/**
* Autoload unit tests
*
* @coversDefaultClass \Flow\Autoloader
*
* @package Unit
*/
class AutoloadTest extends FlowUnitCase
{
/**
* @covers ::__construct
* @covers ::getDir
*/
public function testAutoloader_construct_default()
{
$expDir = realpath(__DIR__ . '/../../src/Flow') . '/..';
$autoloader = new \Flow\Autoloader();
$this->assertSame($expDir, $autoloader->getDir());
}
/**
* @covers ::__construct
* @covers ::getDir
*/
public function testAutoloader_construct_custom()
{
$expDir = __DIR__;
$autoloader = new \Flow\Autoloader($expDir);
$this->assertSame($expDir, $autoloader->getDir());
}
/**
* @covers ::autoload
*/
public function testClassesExist()
{
$autoloader = new \Flow\Autoloader();
$autoloader->autoload('noclass');
$this->assertFalse(class_exists('noclass', false));
$autoloader->autoload('Flow\NoClass');
$this->assertFalse(class_exists('Flow\NoClass', false));
$autoloader->autoload('Flow\File');
$this->assertTrue(class_exists('Flow\File'));
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace Unit;
use Flow\Config;
use Flow\Request;
/**
* Config unit tests
*
* @coversDefaultClass \Flow\Config
*
* @package Unit
*/
class ConfigTest extends FlowUnitCase
{
/**
* @covers ::getTempDir
* @covers ::getDeleteChunksOnSave
* @covers ::getHashNameCallback
* @covers ::getPreprocessCallback
* @covers ::__construct
*/
public function testConfig_construct_config()
{
$exampleConfig = array(
'tempDir' => '/some/dir',
'deleteChunksOnSave' => TRUE,
'hashNameCallback' => '\SomeNs\SomeClass::someMethod',
'preprocessCallback' => '\SomeNs\SomeClass::preProcess'
);
$config = new Config($exampleConfig);
$this->assertSame($exampleConfig['tempDir'], $config->getTempDir());
$this->assertSame($exampleConfig['deleteChunksOnSave'], $config->getDeleteChunksOnSave());
$this->assertSame($exampleConfig['hashNameCallback'], $config->getHashNameCallback());
$this->assertSame($exampleConfig['preprocessCallback'], $config->getPreprocessCallback());
}
/**
* @covers ::getTempDir
* @covers ::getDeleteChunksOnSave
* @covers ::getHashNameCallback
* @covers ::getPreprocessCallback
* @covers ::__construct
*/
public function testConfig_construct_default()
{
$config = new Config();
$this->assertSame('', $config->getTempDir());
$this->assertSame(true, $config->getDeleteChunksOnSave());
$this->assertSame('\Flow\Config::hashNameCallback', $config->getHashNameCallback());
$this->assertSame(null, $config->getPreprocessCallback());
}
/**
* @covers ::setTempDir
* @covers ::getTempDir
*/
public function testConfig_setTempDir()
{
$dir = '/some/dir';
$config = new Config();
$config->setTempDir($dir);
$this->assertSame($dir, $config->getTempDir());
}
/**
* @covers ::setHashNameCallback
* @covers ::getHashNameCallback
*/
public function testConfig_setHashNameCallback()
{
$callback = '\SomeNs\SomeClass::someMethod';
$config = new Config();
$config->setHashNameCallback($callback);
$this->assertSame($callback, $config->getHashNameCallback());
}
/**
* @covers ::setPreprocessCallback
* @covers ::getPreprocessCallback
*/
public function testConfig_setPreprocessCallback()
{
$callback = '\SomeNs\SomeClass::someOtherMethod';
$config = new Config();
$config->setPreprocessCallback($callback);
$this->assertSame($callback, $config->getPreprocessCallback());
}
/**
* @covers ::setDeleteChunksOnSave
* @covers ::getDeleteChunksOnSave
*/
public function testConfig_setDeleteChunksOnSave()
{
$config = new Config();
$config->setDeleteChunksOnSave(false);
$this->assertFalse($config->getDeleteChunksOnSave());
}
public function testConfig_hashNameCallback()
{
$request = new Request($this->requestArr);
$expHash = sha1($request->getIdentifier());
$this->assertSame($expHash, Config::hashNameCallback($request));
}
}

View File

@@ -0,0 +1,428 @@
<?php
namespace Unit;
use Flow\File;
use Flow\Config;
use Flow\FileLockException;
use Flow\FileOpenException;
use Flow\Request;
use \org\bovigo\vfs\vfsStreamWrapper;
use \org\bovigo\vfs\vfsStreamDirectory;
use \org\bovigo\vfs\vfsStream;
/**
* File unit tests
*
* @coversDefaultClass \Flow\File
*
* @package Unit
*/
class FileTest extends FlowUnitCase
{
/**
* Config
*
* @var Config
*/
protected $config;
/**
* Virtual file system
*
* @var vfsStreamDirectory
*/
protected $vfs;
protected function setUp()
{
parent::setUp();
// Setup virtual file system
vfsStreamWrapper::register();
$this->vfs = new vfsStreamDirectory('chunks');
vfsStreamWrapper::setRoot($this->vfs);
// Setup Config
$this->config = new Config();
$this->config->setTempDir($this->vfs->url());
}
/**
* @covers ::__construct
* @covers ::getIdentifier
*/
public function testFile_construct_withRequest()
{
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$expIdentifier = sha1($this->requestArr['flowIdentifier']);
$this->assertSame($expIdentifier, $file->getIdentifier());
}
/**
* @covers ::__construct
* @covers ::getIdentifier
*/
public function testFile_construct_noRequest()
{
$_REQUEST = $this->requestArr;
$file = new File($this->config);
$expIdentifier = sha1($this->requestArr['flowIdentifier']);
$this->assertSame($expIdentifier, $file->getIdentifier());
}
/**
* @covers ::getChunkPath
*/
public function testFile_construct_getChunkPath()
{
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$expPath = $this->vfs->url() . DIRECTORY_SEPARATOR . sha1($this->requestArr['flowIdentifier']) . '_1';
$this->assertSame($expPath, $file->getChunkPath(1));
}
/**
* @covers ::checkChunk
*/
public function testFile_construct_checkChunk()
{
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$this->assertFalse($file->checkChunk());
$chunkName = sha1($request->getIdentifier()) . '_' . $request->getCurrentChunkNumber();
$firstChunk = vfsStream::newFile($chunkName);
$this->vfs->addChild($firstChunk);
$this->assertTrue($file->checkChunk());
}
/**
* @covers ::validateChunk
*/
public function testFile_validateChunk()
{
// No $_FILES
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$this->assertFalse($file->validateChunk());
// No 'file' key $_FILES
$fileInfo = new \ArrayObject();
$request = new Request($this->requestArr, $fileInfo);
$file = new File($this->config, $request);
$this->assertFalse($file->validateChunk());
// Upload OK
$fileInfo->exchangeArray(array(
'size' => 10,
'error' => UPLOAD_ERR_OK,
'tmp_name' => ''
));
$this->assertTrue($file->validateChunk());
// Chunk size doesn't match
$fileInfo->exchangeArray(array(
'size' => 9,
'error' => UPLOAD_ERR_OK,
'tmp_name' => ''
));
$this->assertFalse($file->validateChunk());
// Upload error
$fileInfo->exchangeArray(array(
'size' => 10,
'error' => UPLOAD_ERR_EXTENSION,
'tmp_name' => ''
));
$this->assertFalse($file->validateChunk());
}
/**
* @covers ::validateFile
*/
public function testFile_validateFile()
{
$this->requestArr['flowTotalSize'] = 10;
$this->requestArr['flowTotalChunks'] = 3;
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$chunkPrefix = sha1($request->getIdentifier()) . '_';
// No chunks uploaded yet
$this->assertFalse($file->validateFile());
// First chunk
$firstChunk = vfsStream::newFile($chunkPrefix . '1');
$firstChunk->setContent('123');
$this->vfs->addChild($firstChunk);
// Uploaded not yet complete
$this->assertFalse($file->validateFile());
// Second chunk
$secondChunk = vfsStream::newFile($chunkPrefix . '2');
$secondChunk->setContent('456');
$this->vfs->addChild($secondChunk);
// Uploaded not yet complete
$this->assertFalse($file->validateFile());
// Third chunk
$lastChunk = vfsStream::newFile($chunkPrefix . '3');
$lastChunk->setContent('7890');
$this->vfs->addChild($lastChunk);
// All chunks uploaded
$this->assertTrue($file->validateFile());
//// Test false values
// File size doesn't match
$lastChunk->setContent('789');
$this->assertFalse($file->validateFile());
// Correct file size and expect true
$this->requestArr['flowTotalSize'] = 9;
$this->assertTrue($file->validateFile());
}
/**
* @covers ::deleteChunks
*/
public function testFile_deleteChunks()
{
//// Setup test
$this->requestArr['flowTotalChunks'] = 4;
$fileInfo = new \ArrayObject();
$request = new Request($this->requestArr, $fileInfo);
$file = new File($this->config, $request);
$chunkPrefix = sha1($request->getIdentifier()) . '_';
$firstChunk = vfsStream::newFile($chunkPrefix . 1);
$this->vfs->addChild($firstChunk);
$secondChunk = vfsStream::newFile($chunkPrefix . 3);
$this->vfs->addChild($secondChunk);
$thirdChunk = vfsStream::newFile('other');
$this->vfs->addChild($thirdChunk);
//// Actual test
$this->assertTrue(file_exists($firstChunk->url()));
$this->assertTrue(file_exists($secondChunk->url()));
$this->assertTrue(file_exists($thirdChunk->url()));
$file->deleteChunks();
$this->assertFalse(file_exists($firstChunk->url()));
$this->assertFalse(file_exists($secondChunk->url()));
$this->assertTrue(file_exists($thirdChunk->url()));
}
/**
* @covers ::saveChunk
*/
public function testFile_saveChunk()
{
//// Setup test
// Setup temporary file
$tmpDir = new vfsStreamDirectory('tmp');
$tmpFile = vfsStream::newFile('tmpFile');
$tmpFile->setContent('1234567890');
$tmpDir->addChild($tmpFile);
$this->vfs->addChild($tmpDir);
$this->filesArr['file']['tmp_name'] = $tmpFile->url();
// Mock File to use rename instead of move_uploaded_file
$request = new Request($this->requestArr, $this->filesArr['file']);
$file = $this->getMock('Flow\File', array('_move_uploaded_file'), array($this->config, $request));
$file->expects($this->once())
->method('_move_uploaded_file')
->will($this->returnCallback(function ($filename, $destination) {
return rename($filename, $destination);
}));
// Expected destination file
$expDstFile = $this->vfs->url() . DIRECTORY_SEPARATOR . sha1($request->getIdentifier()) . '_1';
//// Accrual test
$this->assertFalse(file_exists($expDstFile));
$this->assertTrue(file_exists($tmpFile->url()));
/** @noinspection PhpUndefinedMethodInspection */
$this->assertTrue($file->saveChunk());
$this->assertTrue(file_exists($expDstFile));
//$this->assertFalse(file_exists($tmpFile->url()));
$this->assertSame('1234567890', file_get_contents($expDstFile));
}
/**
* @covers ::save
*/
public function testFile_save()
{
//// Setup test
$this->requestArr['flowTotalChunks'] = 3;
$this->requestArr['flowTotalSize'] = 10;
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$chunkPrefix = sha1($request->getIdentifier()) . '_';
$chunk = vfsStream::newFile($chunkPrefix . '1', 0777);
$chunk->setContent('0123');
$this->vfs->addChild($chunk);
$chunk = vfsStream::newFile($chunkPrefix . '2', 0777);
$chunk->setContent('456');
$this->vfs->addChild($chunk);
$chunk = vfsStream::newFile($chunkPrefix . '3', 0777);
$chunk->setContent('789');
$this->vfs->addChild($chunk);
$filePath = $this->vfs->url() . DIRECTORY_SEPARATOR . 'file';
//// Actual test
$this->assertTrue($file->save($filePath));
$this->assertTrue(file_exists($filePath));
$this->assertEquals($request->getTotalSize(), filesize($filePath));
}
/**
* @covers ::save
*/
public function testFile_save_lock()
{
//// Setup test
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$dstFile = $this->vfs->url() . DIRECTORY_SEPARATOR . 'file';
// Lock file
$fh = fopen($dstFile, 'wb');
$this->assertTrue(flock($fh, LOCK_EX));
//// Actual test
try {
// practically on a normal file system exception would not be thrown, this happens
// because vfsStreamWrapper does not support locking with block
$file->save($dstFile);
$this->fail();
} catch (FileLockException $e) {
$this->assertEquals('failed to lock file: ' . $dstFile, $e->getMessage());
}
}
/**
* @covers ::save
*/
public function testFile_save_FileOpenException()
{
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
try {
@$file->save('not/existing/path');
$this->fail();
} catch (FileOpenException $e) {
$this->assertEquals('failed to open destination file: not/existing/path', $e->getMessage());
}
}
/**
* @covers ::save
*/
public function testFile_save_chunk_FileOpenException()
{
//// Setup test
$this->requestArr['flowTotalChunks'] = 3;
$this->requestArr['flowTotalSize'] = 10;
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$chunkPrefix = sha1($request->getIdentifier()) . '_';
$chunk = vfsStream::newFile($chunkPrefix . '1', 0777);
$chunk->setContent('0123');
$this->vfs->addChild($chunk);
$chunk = vfsStream::newFile($chunkPrefix . '2', 0777);
$chunk->setContent('456');
$this->vfs->addChild($chunk);
$missingChunk = $this->vfs->url() . DIRECTORY_SEPARATOR . $chunkPrefix . '3';
$filePath = $this->vfs->url() . DIRECTORY_SEPARATOR . 'file';
//// Actual test
try {
@$file->save($filePath);
} catch (FileOpenException $e) {
$this->assertEquals('failed to open chunk: ' . $missingChunk, $e->getMessage());
}
}
/**
* @covers ::save
*/
public function testFile_save_preProcess()
{
//// Setup test
$this->requestArr['flowTotalChunks'] = 1;
$this->requestArr['flowTotalSize'] = 10;
$processCalled = false;
$process = function($chunk) use (&$processCalled)
{
$processCalled = true;
};
$this->config->setPreprocessCallback($process);
$request = new Request($this->requestArr);
$file = new File($this->config, $request);
$chunkPrefix = sha1($request->getIdentifier()) . '_';
$chunk = vfsStream::newFile($chunkPrefix . '1', 0777);
$chunk->setContent('1234567890');
$this->vfs->addChild($chunk);
$filePath = $this->vfs->url() . DIRECTORY_SEPARATOR . 'file';
//// Actual test
$this->assertTrue($file->save($filePath));
$this->assertTrue(file_exists($filePath));
$this->assertEquals($request->getTotalSize(), filesize($filePath));
$this->assertTrue($processCalled);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Unit;
use ArrayObject;
class FlowUnitCase extends \PHPUnit_Framework_TestCase
{
/**
* Test request
*
* @var array
*/
protected $requestArr;
/**
* $_FILES
*
* @var array
*/
protected $filesArr;
protected function setUp()
{
$this->requestArr = new ArrayObject(array(
'flowChunkNumber' => 1,
'flowChunkSize' => 1048576,
'flowCurrentChunkSize' => 10,
'flowTotalSize' => 100,
'flowIdentifier' => '13632-prettifyjs',
'flowFilename' => 'prettify.js',
'flowRelativePath' => 'home/prettify.js',
'flowTotalChunks' => 42
));
$this->filesArr = array(
'file' => array(
'name' => 'someFile.gif',
'type' => 'image/gif',
'size' => '10',
'tmp_name' => '/tmp/abc1234',
'error' => UPLOAD_ERR_OK
)
);
}
protected function tearDown()
{
$_REQUEST = array();
$_FILES = array();
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace Unit;
use Flow\File;
use Flow\FustyRequest;
use Flow\Config;
use org\bovigo\vfs\vfsStreamWrapper;
use org\bovigo\vfs\vfsStreamDirectory;
use org\bovigo\vfs\vfsStream;
/**
* FustyRequest unit tests
*
* @coversDefaultClass \Flow\FustyRequest
*
* @package Unit
*/
class FustyRequestTest extends FlowUnitCase
{
/**
* Virtual file system
*
* @var vfsStreamDirectory
*/
protected $vfs;
protected function setUp()
{
parent::setUp();
vfsStreamWrapper::register();
$this->vfs = new vfsStreamDirectory('chunks');
vfsStreamWrapper::setRoot($this->vfs);
}
/**
* @covers ::__construct
* @covers ::isFustyFlowRequest
*/
public function testFustyRequest_construct()
{
$firstChunk = vfsStream::newFile('temp_file');
$firstChunk->setContent('1234567890');
$this->vfs->addChild($firstChunk);
$fileInfo = new \ArrayObject(array(
'size' => 10,
'error' => UPLOAD_ERR_OK,
'tmp_name' => $firstChunk->url()
));
$request = new \ArrayObject(array(
'flowIdentifier' => '13632-prettifyjs',
'flowFilename' => 'prettify.js',
'flowRelativePath' => 'home/prettify.js'
));
$fustyRequest = new FustyRequest($request, $fileInfo);
$this->assertSame('prettify.js', $fustyRequest->getFileName());
$this->assertSame('13632-prettifyjs', $fustyRequest->getIdentifier());
$this->assertSame('home/prettify.js', $fustyRequest->getRelativePath());
$this->assertSame(1, $fustyRequest->getCurrentChunkNumber());
$this->assertTrue($fustyRequest->isFustyFlowRequest());
$this->assertSame(10, $fustyRequest->getTotalSize());
$this->assertSame(10, $fustyRequest->getDefaultChunkSize());
$this->assertSame(10, $fustyRequest->getCurrentChunkSize());
$this->assertSame(1, $fustyRequest->getTotalChunks());
}
/**
*/
public function testFustyRequest_ValidateUpload()
{
//// Setup test
$firstChunk = vfsStream::newFile('temp_file');
$firstChunk->setContent('1234567890');
$this->vfs->addChild($firstChunk);
$fileInfo = new \ArrayObject(array(
'size' => 10,
'error' => UPLOAD_ERR_OK,
'tmp_name' => $firstChunk->url()
));
$request = new \ArrayObject(array(
'flowIdentifier' => '13632-prettifyjs',
'flowFilename' => 'prettify.js',
'flowRelativePath' => 'home/prettify.js'
));
$fustyRequest = new FustyRequest($request, $fileInfo);
$config = new Config();
$config->setTempDir($this->vfs->url());
/** @var File $file */
$file = $this->getMock('Flow\File', array('_move_uploaded_file'), array($config, $fustyRequest));
/** @noinspection PhpUndefinedMethodInspection */
$file->expects($this->once())
->method('_move_uploaded_file')
->will($this->returnCallback(function ($filename, $destination) {
return rename($filename, $destination);
}));
//// Actual test
$this->assertTrue($file->validateChunk());
$this->assertFalse($file->validateFile());
$this->assertTrue($file->saveChunk());
$this->assertTrue($file->validateFile());
$path = $this->vfs->url() . DIRECTORY_SEPARATOR . 'new';
$this->assertTrue($file->save($path));
$this->assertEquals(10, filesize($path));
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Unit;
use Flow\Request;
/**
* Request unit tests
*
* @coversDefaultClass \Flow\Request
*
* @package Unit
*/
class RequestTest extends FlowUnitCase
{
/**
* @covers ::__construct
*/
public function testRequest_construct_withREQUEST()
{
$_REQUEST = $this->requestArr;
$request = new Request();
$this->assertSame('prettify.js', $request->getFileName());
$this->assertSame(100, $request->getTotalSize());
$this->assertSame('13632-prettifyjs', $request->getIdentifier());
$this->assertSame('home/prettify.js', $request->getRelativePath());
$this->assertSame(42, $request->getTotalChunks());
$this->assertSame(1048576, $request->getDefaultChunkSize());
$this->assertSame(1, $request->getCurrentChunkNumber());
$this->assertSame(10, $request->getCurrentChunkSize());
$this->assertSame(null, $request->getFile());
$this->assertFalse($request->isFustyFlowRequest());
}
/**
* @covers ::__construct
* @covers ::getParam
* @covers ::getFileName
* @covers ::getTotalSize
* @covers ::getIdentifier
* @covers ::getRelativePath
* @covers ::getTotalChunks
* @covers ::getDefaultChunkSize
* @covers ::getCurrentChunkNumber
* @covers ::getCurrentChunkSize
* @covers ::getFile
* @covers ::isFustyFlowRequest
*/
public function testRequest_construct_withCustomRequest()
{
$request = new Request($this->requestArr);
$this->assertSame('prettify.js', $request->getFileName());
$this->assertSame(100, $request->getTotalSize());
$this->assertSame('13632-prettifyjs', $request->getIdentifier());
$this->assertSame('home/prettify.js', $request->getRelativePath());
$this->assertSame(42, $request->getTotalChunks());
$this->assertSame(1048576, $request->getDefaultChunkSize());
$this->assertSame(1, $request->getCurrentChunkNumber());
$this->assertSame(10, $request->getCurrentChunkSize());
$this->assertSame(null, $request->getFile());
$this->assertFalse($request->isFustyFlowRequest());
}
/**
* @covers ::__construct
*/
public function testRequest_construct_withFILES()
{
$_FILES = $this->filesArr;
$request = new Request();
$this->assertSame($this->filesArr['file'], $request->getFile());
}
/**
* @covers ::__construct
*/
public function testRequest_construct_withCustFiles()
{
$request = new Request(null, $this->filesArr['file']);
$this->assertSame($this->filesArr['file'], $request->getFile());
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Unit;
use Flow\FileOpenException;
use org\bovigo\vfs\vfsStreamWrapper;
use org\bovigo\vfs\vfsStreamDirectory;
use org\bovigo\vfs\vfsStream;
use Flow\Uploader;
/**
* Uploader unit tests
*
* @coversDefaultClass \Flow\Uploader
*
* @package Unit
*/
class UploaderTest extends FlowUnitCase
{
/**
* Virtual file system
*
* @var vfsStreamDirectory
*/
protected $vfs;
protected function setUp()
{
vfsStreamWrapper::register();
$this->vfs = new vfsStreamDirectory('chunks');
vfsStreamWrapper::setRoot($this->vfs);
}
/**
* @covers ::pruneChunks
*/
public function testUploader_pruneChunks()
{
//// Setup test
$newDir = vfsStream::newDirectory('1');
$newDir->lastModified(time()-31);
$newDir->lastModified(time());
$fileFirst = vfsStream::newFile('file31');
$fileFirst->lastModified(time()-31);
$fileSecond = vfsStream::newFile('random_file');
$fileSecond->lastModified(time()-30);
$upDir = vfsStream::newFile('..');
$this->vfs->addChild($newDir);
$this->vfs->addChild($fileFirst);
$this->vfs->addChild($fileSecond);
$this->vfs->addChild($upDir);
//// Actual test
Uploader::pruneChunks($this->vfs->url(), 30);
$this->assertTrue(file_exists($newDir->url()));
$this->assertFalse(file_exists($fileFirst->url()));
$this->assertTrue(file_exists($fileSecond->url()));
}
/**
* @covers ::pruneChunks
*/
public function testUploader_exception()
{
try {
@Uploader::pruneChunks('not/existing/dir', 30);
$this->fail();
} catch (FileOpenException $e) {
$this->assertSame('failed to open folder: not/existing/dir', $e->getMessage());
}
}
}

View File

@@ -0,0 +1,7 @@
<?php
require_once(__DIR__ . '/../vendor/autoload.php');
require_once(__DIR__ . '/../src/Flow/Autoloader.php');
require_once(__DIR__ . '/Unit/FlowUnitCase.php');
Flow\Autoloader::register();

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="test/bootstrap.php"
>
<testsuites>
<testsuite name="Flow Test Suite">
<directory>./test/Unit/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/Flow/</directory>
<exclude>
<file>./src/Flow/Basic.php</file>
</exclude>
</whitelist>
</filter>
<logging>
<log type="coverage-clover" target="build/coverage.xml"/>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
</logging>
<listeners>
<listener class="League\PHPUnitCoverageListener\Listener">
<arguments>
<array>
<element key="printer">
<object class="League\PHPUnitCoverageListener\Printer\StdOut"/>
</element>
<element key="hook">
<object class="League\PHPUnitCoverageListener\Hook\Travis"/>
</element>
<element key="namespace">
<string>Flow</string>
</element>
<element key="repo_token">
<string>TtscpSyNYUnuG2LkxtWCQmAtBk8vWAMsI</string>
</element>
<element key="target_url">
<string>https://coveralls.io/api/v1/jobs</string>
</element>
<element key="coverage_dir">
<string>build</string>
</element>
</array>
</arguments>
</listener>
</listeners>
</phpunit>