Files
tsv08kulmbach-website/app/helpers.php
fs 5170dcb606 Icon-System: icon()-Helper, lokale Bootstrap-Icons, icons-add.php
Inline-SVG-Ausgabe via icon('name', 'klasse', 'label?') aus
public/assets/icons/. Dekorativ (aria-hidden) oder semantisch (role=img).
.icon-Basisklasse in utilities.css (1em, currentColor). bin/icons-add.php
holt neue Icons einzeln von icons.getbootstrap.com.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
2026-06-19 08:11:58 +02:00

146 lines
4.0 KiB
PHP

<?php
declare(strict_types=1);
/**
* Config-Wert holen: config('smtp.host') oder config() für das ganze Array.
*/
function config(?string $key = null, mixed $default = null): mixed
{
$value = $GLOBALS['__config'];
if ($key === null) {
return $value;
}
foreach (explode('.', $key) as $part) {
if (!is_array($value) || !array_key_exists($part, $value)) {
return $default;
}
$value = $value[$part];
}
return $value;
}
/**
* HTML-Escaping — für JEDE dynamische Ausgabe verwenden.
*/
function e(string|int|float|null $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
/**
* Interner Link aus Slug: url('fussball') → '/fussball', url('') → '/'.
*/
function url(string $slug = ''): string
{
return '/' . trim($slug, '/');
}
/**
* Absolute URL für Canonical, OG und Sitemap.
*/
function abs_url(string $slug = ''): string
{
return rtrim((string) config('base_url'), '/') . url($slug);
}
/**
* Asset-Pfad mit Cache-Busting über filemtime: asset('css/tokens.css').
*/
function asset(string $path): string
{
$path = ltrim($path, '/');
$file = PUBLIC_PATH . '/assets/' . $path;
$version = is_file($file) ? (string) filemtime($file) : '0';
return '/assets/' . $path . '?v=' . $version;
}
/**
* Bootstrap-Icon als Inline-SVG ausgeben (lokal aus public/assets/icons/<name>.svg).
* Standard: dekorativ (aria-hidden). Mit $label wird es als beschriftetes Bild
* (role="img") ausgegeben. Größe/Farbe steuert CSS über die Klasse .icon.
* Quelle: Bootstrap Icons (MIT) — neue Icons via bin/icons-add.php hinzufügen.
*/
function icon(string $name, string $class = '', ?string $label = null): string
{
static $cache = [];
if (!array_key_exists($name, $cache)) {
$file = PUBLIC_PATH . '/assets/icons/' . basename($name) . '.svg';
$cache[$name] = is_file($file) ? trim((string) file_get_contents($file)) : '';
}
if ($cache[$name] === '') {
return '';
}
$attrs = 'class="icon' . ($class !== '' ? ' ' . e($class) : '') . '"';
$attrs .= $label !== null && $label !== ''
? ' role="img" aria-label="' . e($label) . '"'
: ' aria-hidden="true" focusable="false"';
return preg_replace('/<svg\b/', '<svg ' . $attrs, $cache[$name], 1);
}
/**
* JSON-Datendatei laden (data/<name>.json) mit Request-weitem Cache.
* Wirft bei kaputtem JSON — Datenfehler sollen laut scheitern, nicht leise.
*/
function json_load(string $name): array
{
static $cache = [];
if (!array_key_exists($name, $cache)) {
$file = DATA_PATH . '/' . $name . '.json';
if (!is_file($file)) {
return [];
}
$cache[$name] = json_decode((string) file_get_contents($file), true, 512, JSON_THROW_ON_ERROR);
}
return $cache[$name];
}
/**
* Komponente rendern: component('hero', ['title' => …]).
* Props werden als lokale Variablen extrahiert; Komponenten sind dumme Includes.
*/
function component(string $name, array $props = []): void
{
extract($props, EXTR_SKIP);
require APP_PATH . '/components/' . $name . '.php';
}
/**
* Page-Datei ausführen: sie setzt $meta und emittiert ihren Body.
* Rückgabe: [$meta, $html].
*/
function render_page(string $file): array
{
$meta = [];
ob_start();
require $file;
return [$meta, (string) ob_get_clean()];
}
/**
* Signierten Zeitstempel für die Formular-Time-Trap erzeugen.
*/
function form_token(): string
{
$ts = (string) time();
return $ts . '.' . hash_hmac('sha256', $ts, (string) config('app_secret'));
}
/**
* Time-Trap prüfen: Signatur gültig, älter als $min Sekunden, jünger als $max.
*/
function form_token_valid(string $token, int $min = 3, int $max = 7200): bool
{
$parts = explode('.', $token);
if (count($parts) !== 2) {
return false;
}
[$ts, $sig] = $parts;
if (!hash_equals(hash_hmac('sha256', $ts, (string) config('app_secret')), $sig)) {
return false;
}
$age = time() - (int) $ts;
return $age >= $min && $age <= $max;
}