80 lines
2.5 KiB
PHP
80 lines
2.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Budget;
|
|
|
|
use PDO;
|
|
|
|
final class Support
|
|
{
|
|
public static function month(?string $month): string
|
|
{
|
|
$month = $month ?: date('Y-m');
|
|
if (!preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $month)) {
|
|
return date('Y-m');
|
|
}
|
|
return $month;
|
|
}
|
|
|
|
public static function money(float|int|string $amount, string $symbol = '$'): string
|
|
{
|
|
$value = (float) $amount;
|
|
return ($value < 0 ? '-' : '') . $symbol . number_format(abs($value), 2);
|
|
}
|
|
|
|
public static function csrf(): string
|
|
{
|
|
if (empty($_SESSION['csrf_token'])) {
|
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
}
|
|
return (string) $_SESSION['csrf_token'];
|
|
}
|
|
|
|
public static function verifyCsrf(?string $token): void
|
|
{
|
|
if (!is_string($token) || !hash_equals(self::csrf(), $token)) {
|
|
throw new \RuntimeException('The form expired. Please try again.');
|
|
}
|
|
}
|
|
|
|
public static function flash(string $type, string $message): void
|
|
{
|
|
$_SESSION['flash'][] = ['type' => $type, 'message' => $message];
|
|
}
|
|
|
|
public static function pullFlash(): array
|
|
{
|
|
$messages = $_SESSION['flash'] ?? [];
|
|
unset($_SESSION['flash']);
|
|
return is_array($messages) ? $messages : [];
|
|
}
|
|
|
|
public static function audit(PDO $pdo, ?int $userId, string $action, ?string $entityType = null, ?int $entityId = null, array $metadata = []): void
|
|
{
|
|
$statement = $pdo->prepare(
|
|
'INSERT INTO audit_logs (user_id, action, entity_type, entity_id, metadata)
|
|
VALUES (:user_id, :action, :entity_type, :entity_id, :metadata)'
|
|
);
|
|
$statement->execute([
|
|
'user_id' => $userId,
|
|
'action' => $action,
|
|
'entity_type' => $entityType,
|
|
'entity_id' => $entityId,
|
|
'metadata' => $metadata === [] ? null : json_encode($metadata, JSON_UNESCAPED_SLASHES),
|
|
]);
|
|
}
|
|
|
|
public static function trackVisit(PDO $pdo, string $route): void
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
|
return;
|
|
}
|
|
$ip = (string) ($_SERVER['REMOTE_ADDR'] ?? 'local');
|
|
$agent = (string) ($_SERVER['HTTP_USER_AGENT'] ?? 'unknown');
|
|
$hash = hash('sha256', date('Y-m-d') . '|' . $ip . '|' . $agent);
|
|
$statement = $pdo->prepare('INSERT INTO visits (route, visitor_hash) VALUES (:route, :visitor_hash)');
|
|
$statement->execute(['route' => $route, 'visitor_hash' => $hash]);
|
|
}
|
|
}
|