Matomo-Tracking und Aufrufzaehlung absichern
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
<?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,
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user