diff --git a/src/web/Upload.php b/src/web/Upload.php new file mode 100644 index 0000000..def3a95 --- /dev/null +++ b/src/web/Upload.php @@ -0,0 +1,105 @@ + "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"); + } +} diff --git a/src/web/uploads.php b/src/web/uploads.php new file mode 100644 index 0000000..b3ef4c7 --- /dev/null +++ b/src/web/uploads.php @@ -0,0 +1,60 @@ + $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; + } +} diff --git a/tests/web/uploadsTest.php b/tests/web/uploadsTest.php new file mode 100644 index 0000000..52d56f0 --- /dev/null +++ b/tests/web/uploadsTest.php @@ -0,0 +1,192 @@ + [ + '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)); + } +}