107 lines
3.7 KiB
PHP
107 lines
3.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/database.php';
|
|
|
|
/**
|
|
* Fester Katalog der gezaehlten oeffentlichen Seiten. Ein Request-Wert darf
|
|
* nie zum Seitenschluessel werden: So koennen insbesondere Query-Parameter,
|
|
* Token oder frei eingegebene Inhalte nicht in der Statistik landen.
|
|
*
|
|
* @return array<string, string>
|
|
*/
|
|
function app_public_page_view_labels(): array
|
|
{
|
|
return [
|
|
'landing' => 'Startseite',
|
|
'prices' => 'Preise',
|
|
'register' => 'Registrierung',
|
|
'login' => 'Login',
|
|
'privacy' => 'Datenschutz',
|
|
'imprint' => 'Impressum',
|
|
'terms' => 'AGB',
|
|
'dpa' => 'AVV',
|
|
'withdrawal_information' => 'Widerrufsbelehrung',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Erhoeht ausschliesslich einen aggregierten Tageszaehler. Es werden keine
|
|
* Request-Metadaten gelesen oder gespeichert. Schreibfehler duerfen eine
|
|
* oeffentliche Seite nicht unbenutzbar machen, etwa waehrend eines Deploys,
|
|
* bei dem die Migration wenige Sekunden nach dem Code eingespielt wird.
|
|
*/
|
|
function app_record_public_page_view(string $pageKey, ?PDO $pdo = null): void
|
|
{
|
|
// Ein explizit injiziertes PDO ist nur fuer den CLI-Regressionstest
|
|
// vorgesehen. Normale CLI-Aufrufe sollen keine Seitenansicht erzeugen.
|
|
if (($pdo === null && PHP_SAPI === 'cli') || ($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'GET') {
|
|
return;
|
|
}
|
|
if (!array_key_exists($pageKey, app_public_page_view_labels())) {
|
|
throw new InvalidArgumentException('Unbekannter öffentlicher Seitenschlüssel.');
|
|
}
|
|
|
|
try {
|
|
$stmt = ($pdo ?? app_db_pdo())->prepare(
|
|
'INSERT INTO public_page_views (view_date, page_key, view_count)
|
|
VALUES (CURRENT_DATE, ?, 1)
|
|
ON DUPLICATE KEY UPDATE view_count = view_count + 1'
|
|
);
|
|
$stmt->execute([$pageKey]);
|
|
} catch (Throwable $e) {
|
|
error_log('Anonymer Seitenzähler konnte nicht aktualisiert werden: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* totals: array{today: int, last_30_days: int, all_time: int},
|
|
* pages: list<array{page_key: string, label: string, today: int, last_30_days: int, all_time: int}>
|
|
* }
|
|
*/
|
|
function app_public_page_view_summary(PDO $pdo): array
|
|
{
|
|
$totalsRow = $pdo->query(
|
|
'SELECT
|
|
COALESCE(SUM(CASE WHEN view_date = CURRENT_DATE THEN view_count ELSE 0 END), 0) AS today,
|
|
COALESCE(SUM(CASE WHEN view_date >= CURRENT_DATE - INTERVAL 29 DAY THEN view_count ELSE 0 END), 0) AS last_30_days,
|
|
COALESCE(SUM(view_count), 0) AS all_time
|
|
FROM public_page_views'
|
|
)->fetch() ?: [];
|
|
|
|
$pageRows = $pdo->query(
|
|
'SELECT
|
|
page_key,
|
|
COALESCE(SUM(CASE WHEN view_date = CURRENT_DATE THEN view_count ELSE 0 END), 0) AS today,
|
|
COALESCE(SUM(CASE WHEN view_date >= CURRENT_DATE - INTERVAL 29 DAY THEN view_count ELSE 0 END), 0) AS last_30_days,
|
|
COALESCE(SUM(view_count), 0) AS all_time
|
|
FROM public_page_views
|
|
GROUP BY page_key
|
|
ORDER BY all_time DESC, page_key ASC'
|
|
)->fetchAll();
|
|
|
|
$labels = app_public_page_view_labels();
|
|
$pages = [];
|
|
foreach ($pageRows as $row) {
|
|
$pageKey = (string)$row['page_key'];
|
|
$pages[] = [
|
|
'page_key' => $pageKey,
|
|
'label' => $labels[$pageKey] ?? $pageKey,
|
|
'today' => (int)$row['today'],
|
|
'last_30_days' => (int)$row['last_30_days'],
|
|
'all_time' => (int)$row['all_time'],
|
|
];
|
|
}
|
|
|
|
return [
|
|
'totals' => [
|
|
'today' => (int)($totalsRow['today'] ?? 0),
|
|
'last_30_days' => (int)($totalsRow['last_30_days'] ?? 0),
|
|
'all_time' => (int)($totalsRow['all_time'] ?? 0),
|
|
],
|
|
'pages' => $pages,
|
|
];
|
|
}
|