slug(trim($value)); return $slug !== '' ? $slug : 'article'; } function app_date_fr(string $value): string { static $formatter = null; $value = trim($value); if ($value === '') { return ''; } try { $date = new DateTimeImmutable($value, new DateTimeZone('UTC')); $date = $date->setTimezone(new DateTimeZone(date_default_timezone_get())); if (!$formatter instanceof IntlDateFormatter) { $formatter = new IntlDateFormatter( 'fr_FR', IntlDateFormatter::LONG, IntlDateFormatter::SHORT, date_default_timezone_get(), IntlDateFormatter::GREGORIAN, "d MMMM yyyy 'à' HH:mm" ); } return (string) ($formatter->format($date) ?: $value); } catch (Throwable) { return $value; } } function app_error_meta(int $code): array { return match ($code) { 400 => ['title' => 'Requête invalide', 'message' => 'La requête est invalide.'], 403 => ['title' => 'Accès refusé', 'message' => 'Tu n’as pas accès à cette page.'], 404 => ['title' => 'Page introuvable', 'message' => 'La page est introuvable.'], default => ['title' => 'Erreur serveur', 'message' => 'Une erreur est survenue.'], }; } function app_request_is_secure(): bool { if (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off') { return true; } if (strtolower((string) ($_SERVER['REQUEST_SCHEME'] ?? '')) === 'https') { return true; } $forwardedProto = strtolower(trim((string) ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? ''))); if ($forwardedProto !== '') { return explode(',', $forwardedProto)[0] === 'https'; } return false; } function app_ensure_dir(string $path): void { if (!is_dir($path)) { mkdir($path, Base::MODE, true); } }