['application/pdf'], 'doc' => ['application/msword'], 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 'jpg' => ['image/jpeg'], 'jpeg' => ['image/jpeg'], 'png' => ['image/png'], ]; const UPLOAD_TYPES_PDF_IMG = [ 'pdf' => ['application/pdf'], 'jpg' => ['image/jpeg'], 'jpeg' => ['image/jpeg'], 'png' => ['image/png'], ]; const UPLOAD_TYPES_IMAGES = [ 'jpg' => ['image/jpeg'], 'jpeg' => ['image/jpeg'], 'png' => ['image/png'], 'gif' => ['image/gif'], 'webp' => ['image/webp'], ]; const UPLOAD_TYPES_PDF = [ 'pdf' => ['application/pdf'], ]; const UPLOAD_TYPES_OFFICE = [ 'pdf' => ['application/pdf'], 'doc' => ['application/msword'], 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 'xls' => ['application/vnd.ms-excel'], 'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'], 'ppt' => ['application/vnd.ms-powerpoint'], 'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'], 'txt' => ['text/plain'], 'zip' => ['application/zip', 'application/x-zip-compressed'], ]; /** * Validiert und speichert eine hochgeladene Datei. * * @param array $file Eintrag aus $_FILES, z.B. $_FILES['datei'] * @param array $opts allowed (ext=>mimes, Pflicht), dir (Pflicht), prefix, * max_bytes (Default 12 MB), required (Default true), * naming ('dated' [Default] = Datum_Username_Prefix_Rand, * 'plain' = Username_Rand, 'hash' = nur Rand), * username (Default current_username(); für 'dated'/'plain'), * messages (Überschreibt Standardtexte: missing/upload/ext/mime/size/move) * @return array ['ok'=>bool, 'error'=>?string, 'filename'=>?string, * 'original_name'=>?string, 'mime'=>?string] */ function handle_upload(array $file, array $opts): array { $allowed = $opts['allowed']; $dir = rtrim($opts['dir'], '/') . '/'; $maxBytes = $opts['max_bytes'] ?? 12 * 1024 * 1024; $required = $opts['required'] ?? true; $naming = $opts['naming'] ?? 'dated'; $prefix = $opts['prefix'] ?? 'datei'; $username = $opts['username'] ?? current_username(); $msg = array_merge([ 'missing' => 'Bitte eine Datei auswählen.', 'upload' => 'Fehler beim Hochladen der Datei.', 'ext' => 'Dateityp nicht erlaubt.', 'mime' => 'Dateityp nicht erlaubt.', 'size' => 'Datei ist zu groß (max. 12 MB).', 'move' => 'Fehler beim Hochladen der Datei.', ], $opts['messages'] ?? []); $err = $file['error'] ?? UPLOAD_ERR_NO_FILE; if ($err === UPLOAD_ERR_NO_FILE || !isset($file['tmp_name'])) { if ($required) return ['ok' => false, 'error' => $msg['missing']]; return ['ok' => true, 'error' => null, 'filename' => null, 'original_name' => null, 'mime' => null]; } if ($err !== UPLOAD_ERR_OK) { return ['ok' => false, 'error' => $msg['upload']]; } $originalName = $file['name']; $ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); if (!isset($allowed[$ext])) { return ['ok' => false, 'error' => $msg['ext']]; } $finfo = new finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($file['tmp_name']); if (!in_array($mime, $allowed[$ext], true)) { return ['ok' => false, 'error' => $msg['mime']]; } if ($file['size'] > $maxBytes) { return ['ok' => false, 'error' => $msg['size']]; } $safeName = match ($naming) { 'hash' => bin2hex(random_bytes(16)) . '.' . $ext, 'plain' => $username . '_' . bin2hex(random_bytes(8)) . '.' . $ext, default => date('Y-m-d') . '_' . $username . '_' . $prefix . '_' . bin2hex(random_bytes(8)) . '.' . $ext, }; if (!is_dir($dir)) { mkdir($dir, 0755, true); } if (!move_uploaded_file($file['tmp_name'], $dir . $safeName)) { return ['ok' => false, 'error' => $msg['move']]; } return [ 'ok' => true, 'error' => null, 'filename' => $safeName, 'original_name' => $originalName, 'mime' => $mime, ]; } function redirect_error(string $back, string $msg): never { header('Location: ' . $back . '?error=' . urlencode($msg)); exit; } function redirect_ok(string $back, string $param = 'success', string $val = '1'): never { header('Location: ' . $back . '?' . $param . '=' . $val); exit; }