support des uploads

This commit is contained in:
Jephté Clain 2024-05-20 20:15:38 +04:00
parent 54ba47fd28
commit ab6f9fa718
3 changed files with 357 additions and 0 deletions

105
src/web/Upload.php Normal file
View File

@ -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");
}
}

60
src/web/uploads.php Normal file
View File

@ -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;
}
}

192
tests/web/uploadsTest.php Normal file
View File

@ -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));
}
}