'ae', 'ö' => 'oe', 'ü' => 'ue', 'ß' => 'ss', 'Ä' => 'ae', 'Ö' => 'oe', 'Ü' => 'ue', ]; $str = str_replace(array_keys($replacements), array_values($replacements), $str); $str = mb_strtolower($str, 'UTF-8'); $str = preg_replace('/[^a-z0-9\s-]/', '', $str); $str = preg_replace('/[\s-]+/', '-', $str); return trim($str, '-'); } function redirect(string $url): never { header('Location: ' . $url); exit; } function flash(string $type, string $message): void { $_SESSION['flash'][] = ['type' => $type, 'message' => $message]; } function flash_display(): string { if (empty($_SESSION['flash'])) { return ''; } $html = ''; foreach ($_SESSION['flash'] as $msg) { $cls = e($msg['type']); $text = e($msg['message']); $html .= "
{$text}
"; } $_SESSION['flash'] = []; return $html; } function paginate(int $total, int $page, int $perPage): array { $totalPages = max(1, (int) ceil($total / $perPage)); $page = max(1, min($page, $totalPages)); $offset = ($page - 1) * $perPage; return [ 'offset' => $offset, 'limit' => $perPage, 'total_pages' => $totalPages, 'current_page' => $page, 'total' => $total, ]; } function format_date(string $datetime): string { $months = [ 1 => 'Januar', 2 => 'Februar', 3 => 'März', 4 => 'April', 5 => 'Mai', 6 => 'Juni', 7 => 'Juli', 8 => 'August', 9 => 'September', 10 => 'Oktober', 11 => 'November', 12 => 'Dezember', ]; $ts = strtotime($datetime); if ($ts === false) { return $datetime; } $day = (int) date('j', $ts); $month = $months[(int) date('n', $ts)]; $year = date('Y', $ts); return "{$day}. {$month} {$year}"; } function excerpt(string $html, int $length = 200): string { $text = strip_tags($html); if (mb_strlen($text) <= $length) { return $text; } $truncated = mb_substr($text, 0, $length); $lastSpace = mb_strrpos($truncated, ' '); if ($lastSpace !== false) { $truncated = mb_substr($truncated, 0, $lastSpace); } return $truncated . '…'; } function sanitize_html(string $html): string { $allowed = '