Bereite sauberen Installationsstand vor
Runtime-Daten aus Git entfernen, WebP-Konvertierung ergänzen, Installation und Updates dokumentieren sowie den Repository-Link im Footer hinzufügen.
This commit is contained in:
+183
-7
@@ -86,22 +86,198 @@ function handle_image_upload(?array $file): ?string {
|
||||
if ($file['error'] !== UPLOAD_ERR_OK) {
|
||||
throw new RuntimeException('Upload-Fehler (Code '.$file['error'].')');
|
||||
}
|
||||
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];
|
||||
|
||||
$sourcePath = (string)($file['tmp_name'] ?? '');
|
||||
if ($sourcePath === '' || !is_file($sourcePath)) {
|
||||
throw new RuntimeException('Die hochgeladene Bilddatei fehlt.');
|
||||
}
|
||||
|
||||
$allowed = ['image/jpeg', 'image/png', 'image/webp'];
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = finfo_file($finfo, $file['tmp_name']);
|
||||
if ($finfo === false) {
|
||||
throw new RuntimeException('Der Dateityp konnte nicht geprüft werden.');
|
||||
}
|
||||
$mime = finfo_file($finfo, $sourcePath);
|
||||
finfo_close($finfo);
|
||||
if (!isset($allowed[$mime])) {
|
||||
if (!is_string($mime) || !in_array($mime, $allowed, true)) {
|
||||
throw new RuntimeException('Nur JPG/PNG/WEBP erlaubt.');
|
||||
}
|
||||
$ext = $allowed[$mime];
|
||||
$basename = 'event_'.date('Ymd_His').'_' . bin2hex(random_bytes(4)) . '.' . $ext;
|
||||
$dest = rtrim(UPLOAD_DIR, '/').'/'.$basename;
|
||||
if (!move_uploaded_file($file['tmp_name'], $dest)) {
|
||||
|
||||
if (@getimagesize($sourcePath) === false) {
|
||||
throw new RuntimeException('Die hochgeladene Datei ist kein gültiges Bild.');
|
||||
}
|
||||
|
||||
ensure_storage();
|
||||
$uploadToken = 'event_'.date('Ymd_His').'_' . bin2hex(random_bytes(4));
|
||||
$basename = $uploadToken . '.webp';
|
||||
$uploadDir = rtrim(UPLOAD_DIR, '/\\');
|
||||
$stagedSource = $uploadDir . '/.' . $uploadToken . '.upload';
|
||||
$stagedWebp = $uploadDir . '/.' . $uploadToken . '.webp.tmp';
|
||||
$destination = $uploadDir . '/' . $basename;
|
||||
|
||||
if (!move_uploaded_file($sourcePath, $stagedSource)) {
|
||||
throw new RuntimeException('Konnte Datei nicht speichern.');
|
||||
}
|
||||
|
||||
try {
|
||||
convert_image_to_webp($stagedSource, $mime, $stagedWebp, WEBP_QUALITY);
|
||||
$webpSize = is_file($stagedWebp) ? filesize($stagedWebp) : false;
|
||||
if ($webpSize === false || $webpSize === 0) {
|
||||
throw new RuntimeException('Die WebP-Datei konnte nicht erstellt werden.');
|
||||
}
|
||||
if (!@rename($stagedWebp, $destination)) {
|
||||
throw new RuntimeException('Die WebP-Datei konnte nicht gespeichert werden.');
|
||||
}
|
||||
} finally {
|
||||
if (is_file($stagedSource)) {
|
||||
@unlink($stagedSource);
|
||||
}
|
||||
if (is_file($stagedWebp)) {
|
||||
@unlink($stagedWebp);
|
||||
}
|
||||
}
|
||||
|
||||
return '/uploads/'.$basename; // Pfad relativ zur Webroot
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert ein geprüftes JPG-, PNG- oder WebP-Bild in eine WebP-Datei.
|
||||
* Unterstützt GD mit WebP-Support sowie Imagick als Alternative.
|
||||
*/
|
||||
function convert_image_to_webp(string $source, string $mime, string $destination, int $quality = 85): void {
|
||||
$quality = max(0, min(100, $quality));
|
||||
$gdLoaders = [
|
||||
'image/jpeg' => 'imagecreatefromjpeg',
|
||||
'image/png' => 'imagecreatefrompng',
|
||||
'image/webp' => 'imagecreatefromwebp',
|
||||
];
|
||||
$gdLoader = $gdLoaders[$mime] ?? null;
|
||||
|
||||
if ($gdLoader !== null && function_exists($gdLoader) && function_exists('imagewebp')) {
|
||||
convert_image_to_webp_with_gd($source, $mime, $destination, $quality, $gdLoader);
|
||||
return;
|
||||
}
|
||||
|
||||
if (class_exists('Imagick')) {
|
||||
convert_image_to_webp_with_imagick($source, $destination, $quality);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
'WebP-Konvertierung ist auf dem Server nicht verfügbar. Bitte PHP-GD mit WebP-Support oder Imagick aktivieren.'
|
||||
);
|
||||
}
|
||||
|
||||
function convert_image_to_webp_with_gd(
|
||||
string $source,
|
||||
string $mime,
|
||||
string $destination,
|
||||
int $quality,
|
||||
string $loader
|
||||
): void {
|
||||
$image = @$loader($source);
|
||||
if ($image === false) {
|
||||
throw new RuntimeException('Das Bild konnte nicht gelesen werden.');
|
||||
}
|
||||
|
||||
try {
|
||||
if ($mime === 'image/jpeg') {
|
||||
$image = apply_jpeg_exif_orientation($image, $source);
|
||||
}
|
||||
|
||||
if (function_exists('imagepalettetotruecolor')
|
||||
&& function_exists('imageistruecolor')
|
||||
&& !imageistruecolor($image)
|
||||
) {
|
||||
imagepalettetotruecolor($image);
|
||||
}
|
||||
imagealphablending($image, true);
|
||||
imagesavealpha($image, true);
|
||||
|
||||
if (!@imagewebp($image, $destination, $quality)) {
|
||||
throw new RuntimeException('Das Bild konnte nicht als WebP gespeichert werden.');
|
||||
}
|
||||
} finally {
|
||||
imagedestroy($image);
|
||||
}
|
||||
}
|
||||
|
||||
function apply_jpeg_exif_orientation($image, string $source) {
|
||||
if (!function_exists('exif_read_data')) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
$exif = @exif_read_data($source, 'IFD0', true);
|
||||
$orientation = (int)($exif['IFD0']['Orientation'] ?? $exif['Orientation'] ?? 1);
|
||||
|
||||
if (in_array($orientation, [2, 4, 5, 7], true)) {
|
||||
$flipMode = in_array($orientation, [2, 5, 7], true) ? IMG_FLIP_HORIZONTAL : IMG_FLIP_VERTICAL;
|
||||
if (!imageflip($image, $flipMode)) {
|
||||
throw new RuntimeException('Die Bildausrichtung konnte nicht korrigiert werden.');
|
||||
}
|
||||
}
|
||||
|
||||
$angle = 0;
|
||||
if ($orientation === 3) {
|
||||
$angle = 180;
|
||||
} elseif (in_array($orientation, [5, 6], true)) {
|
||||
$angle = -90;
|
||||
} elseif (in_array($orientation, [7, 8], true)) {
|
||||
$angle = 90;
|
||||
}
|
||||
if ($angle === 0) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
$transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
|
||||
$rotated = imagerotate($image, $angle, $transparent);
|
||||
if ($rotated === false) {
|
||||
throw new RuntimeException('Die Bildausrichtung konnte nicht korrigiert werden.');
|
||||
}
|
||||
imagesavealpha($rotated, true);
|
||||
imagedestroy($image);
|
||||
|
||||
return $rotated;
|
||||
}
|
||||
|
||||
function convert_image_to_webp_with_imagick(
|
||||
string $source,
|
||||
string $destination,
|
||||
int $quality
|
||||
): void {
|
||||
$sourceImage = new Imagick();
|
||||
$image = null;
|
||||
|
||||
try {
|
||||
$sourceImage->readImage($source);
|
||||
$sourceImage->setIteratorIndex(0);
|
||||
$image = $sourceImage->getImage();
|
||||
|
||||
if (method_exists($image, 'autoOrientImage')) {
|
||||
$image->autoOrientImage();
|
||||
}
|
||||
$image->setImageFormat('webp');
|
||||
$image->setImageCompressionQuality($quality);
|
||||
$image->stripImage();
|
||||
|
||||
if (!$image->writeImage($destination)) {
|
||||
throw new RuntimeException('Das Bild konnte nicht als WebP gespeichert werden.');
|
||||
}
|
||||
} catch (Throwable $exception) {
|
||||
if ($exception instanceof RuntimeException) {
|
||||
throw $exception;
|
||||
}
|
||||
throw new RuntimeException('Das Bild konnte nicht in WebP konvertiert werden.', 0, $exception);
|
||||
} finally {
|
||||
if ($image instanceof Imagick) {
|
||||
$image->clear();
|
||||
$image->destroy();
|
||||
}
|
||||
$sourceImage->clear();
|
||||
$sourceImage->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
function render_event_jsonld_graph(array $events): string {
|
||||
$graph = [];
|
||||
foreach ($events as $e) {
|
||||
|
||||
Reference in New Issue
Block a user