Files
mood-tracking/src/helpers.php
T

385 lines
8.4 KiB
PHP

<?php
declare(strict_types=1);
function base_path(string $path = ''): string
{
$base = dirname(__DIR__);
if ($path === '') {
return $base;
}
return $base . '/' . ltrim($path, '/');
}
function storage_path(string $path = ''): string
{
return base_path('storage/' . ltrim($path, '/'));
}
function e(mixed $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
function redirect(string $path): never
{
header('Location: ' . $path);
exit;
}
function request_path(): string
{
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
$path = is_string($path) ? $path : '/';
if ($path !== '/') {
$path = rtrim($path, '/');
}
return $path === '' ? '/' : $path;
}
function flash(string $type, string $message): void
{
$_SESSION['_flash'][] = [
'type' => $type,
'message' => $message,
];
}
function pull_flashes(): array
{
$flashes = $_SESSION['_flash'] ?? [];
unset($_SESSION['_flash']);
return is_array($flashes) ? $flashes : [];
}
function csrf_token(): string
{
if (empty($_SESSION['_csrf'])) {
$_SESSION['_csrf'] = bin2hex(random_bytes(32));
}
return (string) $_SESSION['_csrf'];
}
function csrf_field(): string
{
return '<input type="hidden" name="_token" value="' . e(csrf_token()) . '">';
}
function verify_csrf(?string $token): bool
{
if (!is_string($token) || $token === '') {
return false;
}
return hash_equals(csrf_token(), $token);
}
function is_active_path(string $path): bool
{
return request_path() === $path;
}
function format_points(float $value): string
{
$rounded = round($value, 1);
if (abs($rounded - round($rounded)) < 0.05) {
return (string) (int) round($rounded);
}
return number_format($rounded, 1, ',', '.');
}
function normalize_username(string $username): string
{
return strtolower(trim($username));
}
function today(): string
{
return date('Y-m-d');
}
function decode_json_file(string $path, array $fallback = []): array
{
if (!is_file($path)) {
return $fallback;
}
$decoded = json_decode((string) file_get_contents($path), true);
return is_array($decoded) ? $decoded : $fallback;
}
function encode_payload(array $payload): string
{
return base64_encode((string) json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
function shift_date(string $date, int $days): string
{
$current = DateTimeImmutable::createFromFormat('Y-m-d', $date);
if ($current === false) {
return today();
}
return $current->modify(($days >= 0 ? '+' : '') . $days . ' day')->format('Y-m-d');
}
function format_display_date(string $date, bool $withWeekday = true): string
{
$current = DateTimeImmutable::createFromFormat('Y-m-d', $date);
if ($current === false) {
return $date;
}
$weekdays = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'];
$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',
];
$label = $current->format('j.') . ' ' . $months[(int) $current->format('n')] . ' ' . $current->format('Y');
if (!$withWeekday) {
return $label;
}
return $weekdays[(int) $current->format('w')] . ', ' . $label;
}
function icon_path(string $name): string
{
return '/assets/icons/' . $name . '.svg';
}
function mood_icon_path(string $sentiment): string
{
return icon_path('mood-' . $sentiment);
}
function request_is_secure(): bool
{
$forwardedProto = strtolower((string) ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? ''));
$forwardedSsl = strtolower((string) ($_SERVER['HTTP_X_FORWARDED_SSL'] ?? ''));
return (
(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| $forwardedProto === 'https'
|| $forwardedSsl === 'on'
);
}
function remember_me_lifetime(): int
{
return 60 * 60 * 24 * 30;
}
function session_cookie_params_for(int $lifetime = 0): array
{
return [
'lifetime' => $lifetime,
'path' => '/',
'domain' => '',
'secure' => request_is_secure(),
'httponly' => true,
'samesite' => 'Lax',
];
}
function session_cookie_options_for(int $expires = 0): array
{
return [
'expires' => $expires,
'path' => '/',
'domain' => '',
'secure' => request_is_secure(),
'httponly' => true,
'samesite' => 'Lax',
];
}
function sport_icon_path(string $icon): string
{
return icon_path('sport-' . $icon);
}
function sport_icon_options(): array
{
return [
'strength' => 'Krafttraining',
'bike' => 'Radfahren',
'run' => 'Joggen',
'hike' => 'Wandern',
'swim' => 'Schwimmen',
'yoga' => 'Yoga',
'hiit' => 'HIIT / Workout',
'row' => 'Rudergerät',
'dance' => 'Tanzen',
'core' => 'Core',
'strength-home' => 'Krafttraining Zuhause',
'strength-gym' => 'Krafttraining Auswärts',
];
}
function normalize_sport_type_id(string $value): string
{
$value = trim(strtr($value, [
'Ä' => 'ae',
'Ö' => 'oe',
'Ü' => 'ue',
'ä' => 'ae',
'ö' => 'oe',
'ü' => 'ue',
'ß' => 'ss',
]));
$value = strtolower($value);
$value = strtr($value, [
'--' => '-',
]);
$value = preg_replace('/[^a-z0-9]+/u', '-', $value) ?? '';
return trim($value, '-');
}
function normalized_sport_types(array $settings): array
{
$types = $settings['sport_types'] ?? [];
$normalized = [];
$usedIds = [];
$iconOptions = sport_icon_options();
foreach ($types as $index => $type) {
if (!is_array($type)) {
continue;
}
$label = trim((string) ($type['label'] ?? ''));
if ($label === '') {
continue;
}
$candidateId = trim((string) ($type['id'] ?? ''));
if ($candidateId === '') {
$candidateId = normalize_sport_type_id($label);
}
if ($candidateId === '') {
$candidateId = 'sportart';
}
$id = $candidateId;
$suffix = 2;
while (isset($usedIds[$id])) {
$id = $candidateId . '-' . $suffix;
$suffix++;
}
$usedIds[$id] = true;
$icon = trim((string) ($type['icon'] ?? 'run'));
if (!array_key_exists($icon, $iconOptions)) {
$icon = 'run';
}
$group = trim((string) ($type['recovery_group'] ?? ''));
if ($group === '') {
$group = $id;
}
$normalized[] = [
'id' => $id,
'label' => $label,
'icon' => $icon,
'recovery_group' => normalize_sport_type_id($group) ?: $id,
'bonus_points' => max(0, min(20, (int) ($type['bonus_points'] ?? 0))),
'allow_consecutive' => !empty($type['allow_consecutive']),
];
}
return $normalized;
}
function normalize_sport_type_selection(mixed $value): array
{
if (is_string($value)) {
$value = trim($value);
if ($value === '') {
return [];
}
if (str_contains($value, ',')) {
$value = array_map('trim', explode(',', $value));
} else {
$value = [$value];
}
}
if (!is_array($value)) {
return [];
}
$normalized = [];
foreach ($value as $item) {
if (!is_string($item)) {
continue;
}
$id = trim($item);
if ($id === '' || isset($normalized[$id])) {
continue;
}
$normalized[$id] = true;
}
return array_keys($normalized);
}
function find_sport_type(array $settings, ?string $id): ?array
{
if (!is_string($id) || trim($id) === '') {
return null;
}
foreach (normalized_sport_types($settings) as $type) {
if (($type['id'] ?? '') === $id) {
return $type;
}
}
return null;
}
function find_sport_types(array $settings, array $ids): array
{
$types = [];
foreach (normalize_sport_type_selection($ids) as $id) {
$type = find_sport_type($settings, $id);
if ($type !== null) {
$types[] = $type;
}
}
return $types;
}