support des uploads
This commit is contained in:
parent
54ba47fd28
commit
ab6f9fa718
|
@ -0,0 +1,105 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\web;
|
||||||
|
|
||||||
|
use nur\sery\file\base\FileReader;
|
||||||
|
use nur\sery\php\coll\BaseArray;
|
||||||
|
use nur\sery\ValueException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Upload: un fichier téléversé
|
||||||
|
*
|
||||||
|
* @property-read string $name
|
||||||
|
* @property-read string $type
|
||||||
|
* @property-read string $tmpName
|
||||||
|
* @property-read int $error
|
||||||
|
* @property-read int $size
|
||||||
|
* @property-read string $fullPath
|
||||||
|
*/
|
||||||
|
class Upload extends BaseArray {
|
||||||
|
|
||||||
|
const MESSAGES = [
|
||||||
|
"invalid" => "Ceci n'est pas un fichier téléversé",
|
||||||
|
"nofile" => "Aucun fichier n'a été fourni",
|
||||||
|
"toobig" => "Le fichier que vous avez fourni est trop volumineux.",
|
||||||
|
"unknown" => "Une erreur s'est produite pendant le transfert du fichier. Veuillez réessayer.",
|
||||||
|
];
|
||||||
|
|
||||||
|
static function error(string $message) {
|
||||||
|
return new ValueException(static::MESSAGES[$message]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct(?array $file, bool $required=true, bool $check=true) {
|
||||||
|
parent::__construct($file);
|
||||||
|
if ($check) $this->check($required);
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(bool $required=true, bool $throw=true): bool {
|
||||||
|
$file = $this->data;
|
||||||
|
if ($file) {
|
||||||
|
$name = $file["name"] ?? null;
|
||||||
|
$type = $file["type"] ?? null;
|
||||||
|
$error = $file["error"] ?? null;
|
||||||
|
if (!is_scalar($name) || !is_scalar($type) || !is_scalar($error)) {
|
||||||
|
if ($throw) throw static::error("invalid");
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
switch ($error) {
|
||||||
|
case UPLOAD_ERR_OK:
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_NO_FILE:
|
||||||
|
if ($required) {
|
||||||
|
if ($throw) throw self::error("nofile");
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_INI_SIZE:
|
||||||
|
case UPLOAD_ERR_FORM_SIZE:
|
||||||
|
if ($throw) throw self::error("toobig");
|
||||||
|
else return false;
|
||||||
|
default:
|
||||||
|
if ($throw) self::error("unknown");
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
} elseif ($required) {
|
||||||
|
if ($throw) throw static::error("nofile");
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _AUTO_PROPERTIES = [
|
||||||
|
"tmpName" => "tmp_name",
|
||||||
|
"fullPath" => "full_path",
|
||||||
|
];
|
||||||
|
function __get($name) {
|
||||||
|
$name = static::_AUTO_PROPERTIES[$name] ?? $name;
|
||||||
|
return parent::__get($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isError(): bool {
|
||||||
|
$error = $this->error;
|
||||||
|
return $error !== UPLOAD_ERR_OK && $error !== UPLOAD_ERR_NO_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid(): bool {
|
||||||
|
return $this->error === UPLOAD_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var ?string chemin du fichier, s'il a été déplacé */
|
||||||
|
protected $file;
|
||||||
|
|
||||||
|
function moveTo(string $dest): bool {
|
||||||
|
if ($this->file === null) {
|
||||||
|
$moved = move_uploaded_file($this->tmpName, $dest);
|
||||||
|
if ($moved) $this->file = $dest;
|
||||||
|
} else {
|
||||||
|
$moved = false;
|
||||||
|
}
|
||||||
|
return $moved;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFile(): FileReader {
|
||||||
|
$file = $this->file ?? $this->tmpName;
|
||||||
|
return new FileReader($file, "r+b");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\web;
|
||||||
|
|
||||||
|
use nur\sery\cl;
|
||||||
|
|
||||||
|
class uploads {
|
||||||
|
private static function parse_files(array &$files, string $pkey, array $values, string $lastkey): void {
|
||||||
|
foreach ($values as $key => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
self::parse_files($files, "$pkey.$key", $value, $lastkey);
|
||||||
|
} else {
|
||||||
|
cl::pset($files, "$pkey.$key.$lastkey", $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private static $_files;
|
||||||
|
|
||||||
|
static function _files(?array $_files=null): ?array {
|
||||||
|
if (self::$_files === null) {
|
||||||
|
$files = [];
|
||||||
|
if ($_files === null) $_files = $_FILES;
|
||||||
|
foreach ($_files as $pkey => $values) {
|
||||||
|
$name = $values["name"] ?? null;
|
||||||
|
$type = $values["type"] ?? null;
|
||||||
|
$error = $values["error"] ?? null;
|
||||||
|
if (is_scalar($name) && is_scalar($type) && is_scalar($error)) {
|
||||||
|
$files[$pkey] = $values;
|
||||||
|
} else {
|
||||||
|
self::parse_files($files, $pkey, $values["name"], "name");
|
||||||
|
self::parse_files($files, $pkey, $values["type"], "type");
|
||||||
|
self::parse_files($files, $pkey, $values["tmp_name"], "tmp_name");
|
||||||
|
self::parse_files($files, $pkey, $values["error"], "error");
|
||||||
|
self::parse_files($files, $pkey, $values["size"], "size");
|
||||||
|
$full_path = $values["full_path"] ?? null;
|
||||||
|
if ($full_path !== null) {
|
||||||
|
self::parse_files($files, $pkey, $full_path, "full_path");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::$_files = $files;
|
||||||
|
}
|
||||||
|
return self::$_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function get(string $pkey, bool $required=true): Upload {
|
||||||
|
$_files = self::_files();
|
||||||
|
return new Upload(cl::pget($_files, $pkey));
|
||||||
|
}
|
||||||
|
|
||||||
|
static function all(string $pkey) {
|
||||||
|
$_files = self::_files();
|
||||||
|
$uploads = [];
|
||||||
|
foreach (cl::pget($_files, $pkey) as $file) {
|
||||||
|
$uploads[] = new Upload($file);
|
||||||
|
}
|
||||||
|
return $uploads;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\web;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class uploadsTest extends TestCase {
|
||||||
|
const _FILES = [
|
||||||
|
'simple' => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
'multiple' => [
|
||||||
|
'name' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
'type' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
'tmp_name' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
'error' => [
|
||||||
|
0 => 4,
|
||||||
|
1 => 4,
|
||||||
|
],
|
||||||
|
'size' => [
|
||||||
|
0 => 0,
|
||||||
|
1 => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'onelevel' => [
|
||||||
|
'name' => [
|
||||||
|
'a' => '',
|
||||||
|
'b' => '',
|
||||||
|
],
|
||||||
|
'type' => [
|
||||||
|
'a' => '',
|
||||||
|
'b' => '',
|
||||||
|
],
|
||||||
|
'tmp_name' => [
|
||||||
|
'a' => '',
|
||||||
|
'b' => '',
|
||||||
|
],
|
||||||
|
'error' => [
|
||||||
|
'a' => 4,
|
||||||
|
'b' => 4,
|
||||||
|
],
|
||||||
|
'size' => [
|
||||||
|
'a' => 0,
|
||||||
|
'b' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'multiplelevel' => [
|
||||||
|
'name' => [
|
||||||
|
'a' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'type' => [
|
||||||
|
'a' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'tmp_name' => [
|
||||||
|
'a' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'error' => [
|
||||||
|
'a' => [
|
||||||
|
0 => 4,
|
||||||
|
1 => 4,
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
0 => 4,
|
||||||
|
1 => 4,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'size' => [
|
||||||
|
'a' => [
|
||||||
|
0 => 0,
|
||||||
|
1 => 0,
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
0 => 0,
|
||||||
|
1 => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const PARSED = [
|
||||||
|
'simple' => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
'multiple' => [
|
||||||
|
0 => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
1 => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'onelevel' => [
|
||||||
|
'a' => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'multiplelevel' => [
|
||||||
|
'a' => [
|
||||||
|
0 => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
1 => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
0 => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
1 => [
|
||||||
|
'name' => '',
|
||||||
|
'type' => '',
|
||||||
|
'tmp_name' => '',
|
||||||
|
'error' => 4,
|
||||||
|
'size' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
function test_files() {
|
||||||
|
self::assertSame(self::PARSED, uploads::_files(self::_FILES));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue