202 lines
No EOL
5.7 KiB
PHP
202 lines
No EOL
5.7 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Upload
|
|
*
|
|
* File Upload class
|
|
*
|
|
* @package Kirby Toolkit
|
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
|
* @link http://getkirby.com
|
|
* @copyright Bastian Allgeier
|
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
|
*/
|
|
class Upload {
|
|
|
|
const ERROR_FAILED_UPLOAD = 0;
|
|
const ERROR_MISSING_TMP_DIR = 1;
|
|
const ERROR_MISSING_FILE = 2;
|
|
const ERROR_UNALLOWED_OVERWRITE = 3;
|
|
const ERROR_PARTIAL_UPLOAD = 4;
|
|
const ERROR_MAX_SIZE = 5;
|
|
const ERROR_MOVE_FAILED = 6;
|
|
const ERROR_UNACCEPTED = 7;
|
|
|
|
public $options = array();
|
|
public $error = null;
|
|
public $file = null;
|
|
public $to = null;
|
|
|
|
public function __construct($to, $params = array()) {
|
|
|
|
$defaults = array(
|
|
'input' => 'file',
|
|
'index' => 0,
|
|
'to' => $to,
|
|
'overwrite' => true,
|
|
'maxSize' => false,
|
|
'accept' => null,
|
|
);
|
|
|
|
$this->options = array_merge($defaults, $params);
|
|
|
|
try {
|
|
$this->move();
|
|
$this->file = new Media($this->to());
|
|
} catch(Exception $e) {
|
|
$this->error = $e;
|
|
}
|
|
|
|
}
|
|
|
|
public function error() {
|
|
return $this->error;
|
|
}
|
|
|
|
public function source() {
|
|
|
|
$source = isset($_FILES[$this->options['input']]) ? $_FILES[$this->options['input']] : null;
|
|
|
|
// get the correct file out of multiple based on the "index" option
|
|
if($source && is_int($this->options['index']) && is_array($source['name'])) {
|
|
$allSources = $source;
|
|
$source = array();
|
|
|
|
// get the correct value out of the $values array with all files
|
|
foreach($allSources as $key => $values) {
|
|
$source[$key] = isset($values[$this->options['index']]) ? $values[$this->options['index']] : null;
|
|
}
|
|
}
|
|
|
|
// prevent duplicate ios uploads
|
|
// ios automatically uploads all images as image.jpg,
|
|
// which will lead to overwritten duplicates.
|
|
// this dirty hack will simply add a uniqid between the
|
|
// name and the extension to avoid duplicates
|
|
if($source && f::name($source['name']) == 'image' && detect::ios()) {
|
|
$source['name'] = 'image-' . uniqid() . '.' . ltrim(f::extension($source['name']), '.');
|
|
}
|
|
|
|
return $source;
|
|
|
|
}
|
|
|
|
public function to() {
|
|
|
|
if(!is_null($this->to)) return $this->to;
|
|
|
|
$source = $this->source();
|
|
$name = f::name($source['name']);
|
|
$extension = f::extension($source['name']);
|
|
$safeName = f::safeName($name);
|
|
$safeExtension = str_replace('jpeg', 'jpg', str::lower($extension));
|
|
|
|
if(empty($safeExtension)) {
|
|
$safeExtension = f::mimeToExtension(f::mime($source['tmp_name']));
|
|
}
|
|
|
|
return $this->to = str::template($this->options['to'], array(
|
|
'name' => $name,
|
|
'filename' => $source['name'],
|
|
'safeName' => $safeName,
|
|
'safeFilename' => $safeName . r(!empty($safeExtension), '.' . $safeExtension),
|
|
'extension' => $extension,
|
|
'safeExtension' => $safeExtension
|
|
));
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum accepted file size
|
|
*
|
|
* @return int
|
|
*/
|
|
public function maxSize() {
|
|
$sizes = array(detect::maxPostSize(), detect::maxUploadSize());
|
|
if($this->options['maxSize']) {
|
|
$sizes[] = $this->options['maxSize'];
|
|
}
|
|
return min($sizes);
|
|
}
|
|
|
|
public function file() {
|
|
return $this->file;
|
|
}
|
|
|
|
protected function move() {
|
|
|
|
$source = $this->source();
|
|
|
|
if(is_null($source['name']) || is_null($source['tmp_name'])) {
|
|
$this->fail(static::ERROR_MISSING_FILE);
|
|
}
|
|
|
|
if($source['error'] !== 0) {
|
|
|
|
switch($source['error']) {
|
|
case UPLOAD_ERR_INI_SIZE:
|
|
case UPLOAD_ERR_FORM_SIZE:
|
|
$this->fail(static::ERROR_MAX_SIZE);
|
|
case UPLOAD_ERR_PARTIAL:
|
|
$this->fail(static::ERROR_PARTIAL_UPLOAD);
|
|
case UPLOAD_ERR_NO_FILE:
|
|
$this->fail(static::ERROR_MISSING_FILE);
|
|
case UPLOAD_ERR_NO_TMP_DIR:
|
|
$this->fail(static::ERROR_MISSING_TMP_DIR);
|
|
case UPLOAD_ERR_CANT_WRITE:
|
|
$this->fail(static::ERROR_MOVE_FAILED);
|
|
case UPLOAD_ERR_EXTENSION:
|
|
$this->fail(static::ERROR_UNACCEPTED);
|
|
default:
|
|
$this->fail(static::ERROR_FAILED_UPLOAD);
|
|
}
|
|
|
|
}
|
|
|
|
if(file_exists($this->to()) && $this->options['overwrite'] === false) {
|
|
$this->fail(static::ERROR_UNALLOWED_OVERWRITE);
|
|
}
|
|
|
|
if($this->options['maxSize'] && $source['size'] > $this->options['maxSize']) {
|
|
$this->fail(static::ERROR_MAX_SIZE);
|
|
}
|
|
|
|
if(is_callable($this->options['accept'])) {
|
|
$accepted = call($this->options['accept'], new Media($source['tmp_name']));
|
|
if($accepted === false) {
|
|
$this->fail(static::ERROR_UNACCEPTED);
|
|
}
|
|
}
|
|
|
|
if(!@move_uploaded_file($source['tmp_name'], $this->to())) {
|
|
$this->fail(static::ERROR_MOVE_FAILED);
|
|
}
|
|
|
|
}
|
|
|
|
protected function messages() {
|
|
return array(
|
|
static::ERROR_MISSING_FILE => 'The file is missing',
|
|
static::ERROR_MISSING_TMP_DIR => 'The /tmp directory is missing on your server',
|
|
static::ERROR_FAILED_UPLOAD => 'The upload failed',
|
|
static::ERROR_PARTIAL_UPLOAD => 'The file has been only been partially uploaded',
|
|
static::ERROR_UNALLOWED_OVERWRITE => 'The file exists and cannot be overwritten',
|
|
static::ERROR_MAX_SIZE => 'The file is too big. The maximum size is ' . f::niceSize($this->maxSize()),
|
|
static::ERROR_MOVE_FAILED => 'The file could not be moved',
|
|
static::ERROR_UNACCEPTED => 'The file is not accepted by the server'
|
|
);
|
|
}
|
|
|
|
protected function fail($code) {
|
|
|
|
$messages = $this->messages();
|
|
|
|
if(!isset($messages[$code])) {
|
|
$code = static::ERROR_FAILED_UPLOAD;
|
|
}
|
|
|
|
throw new Error($messages[$code], $code);
|
|
|
|
}
|
|
|
|
} |