*/ function app_matomo_configuration_errors(): array { $url = trim((string)app_env('MATOMO_URL', '')); $siteId = trim((string)app_env('MATOMO_SITE_ID', '')); $privacyConfirmed = trim((string)app_env('MATOMO_PRIVACY_CONFIRMED', '')); $cookieDomain = trim((string)app_env('MATOMO_CONSENT_COOKIE_DOMAIN', '')); $hasAnySetting = $url !== '' || $siteId !== '' || $cookieDomain !== ''; if (!$hasAnySetting) { return []; } $errors = []; $urlParts = $url !== '' ? parse_url($url) : false; if ( $url === '' || filter_var($url, FILTER_VALIDATE_URL) === false || !is_array($urlParts) || !in_array(strtolower((string)($urlParts['scheme'] ?? '')), ['http', 'https'], true) || trim((string)($urlParts['host'] ?? '')) === '' || isset($urlParts['user']) || isset($urlParts['pass']) || isset($urlParts['query']) || isset($urlParts['fragment']) ) { $errors[] = 'MATOMO_URL muss eine vollständige HTTP(S)-Basis-URL ohne Query oder Fragment sein.'; } elseif (!app_is_dev() && strtolower((string)$urlParts['scheme']) !== 'https') { $errors[] = 'MATOMO_URL muss außerhalb der Entwicklungsumgebung HTTPS verwenden.'; } if (preg_match('/^[1-9][0-9]*$/', $siteId) !== 1) { $errors[] = 'MATOMO_SITE_ID muss eine positive ganze Zahl sein.'; } if ($privacyConfirmed !== '1') { $errors[] = 'MATOMO_PRIVACY_CONFIRMED muss nach Prüfung der Matomo-Datenschutzeinstellungen auf 1 gesetzt werden.'; } $cookieDomainHostname = ltrim(strtolower($cookieDomain), '.'); if ($cookieDomain !== '' && filter_var($cookieDomainHostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) { $errors[] = 'MATOMO_CONSENT_COOKIE_DOMAIN ist keine gültige Cookie-Domain.'; } elseif ($cookieDomain !== '') { $hostBelongsToCookieDomain = static function (string $host) use ($cookieDomainHostname): bool { $host = strtolower($host); return $host === $cookieDomainHostname || str_ends_with($host, '.' . $cookieDomainHostname); }; $appHost = trim((string)app_primary_host()); $marketingHost = (string)parse_url((string)app_env('APP_MARKETING_URL', ''), PHP_URL_HOST); if ($appHost !== '' && !$hostBelongsToCookieDomain($appHost)) { $errors[] = 'MATOMO_CONSENT_COOKIE_DOMAIN passt nicht zu APP_HOST.'; } if ($marketingHost !== '' && !$hostBelongsToCookieDomain($marketingHost)) { $errors[] = 'MATOMO_CONSENT_COOKIE_DOMAIN passt nicht zu APP_MARKETING_URL.'; } } return $errors; } /** * @return array{matomoUrl: string, siteId: string, consentCookie: string, visitorCookie: string, consentVersion: string, consentMaxAge: int, cookieDomain: string, secureCookies: bool}|null */ function app_matomo_configuration(): ?array { $url = trim((string)app_env('MATOMO_URL', '')); $siteId = trim((string)app_env('MATOMO_SITE_ID', '')); $privacyConfirmed = trim((string)app_env('MATOMO_PRIVACY_CONFIRMED', '')); if ($url === '' && $siteId === '') { return null; } if (app_matomo_configuration_errors() !== []) { return null; } return [ 'matomoUrl' => rtrim($url, '/') . '/', 'siteId' => $siteId, 'consentCookie' => APP_ANALYTICS_CONSENT_COOKIE, 'visitorCookie' => APP_ANALYTICS_VISITOR_COOKIE, 'consentVersion' => APP_ANALYTICS_CONSENT_VERSION, 'consentMaxAge' => APP_ANALYTICS_CONSENT_MAX_AGE, 'cookieDomain' => trim((string)app_env('MATOMO_CONSENT_COOKIE_DOMAIN', '')), 'secureCookies' => app_is_https(), ]; } function app_analytics_head_html(): string { if (app_matomo_configuration() === null) { return ''; } return '' . "\n"; } function app_analytics_body_html(): string { $configuration = app_matomo_configuration(); if ($configuration === null) { return ''; } // Matomo-Host und Site-ID bleiben serverseitig. Der Browser sendet nach // Einwilligung nur an den First-Party-Endpunkt; dieser leitet die eng // validierte Seitenansicht an die fest konfigurierte Matomo-Site weiter. $browserConfiguration = [ 'consentCookie' => $configuration['consentCookie'], 'visitorCookie' => $configuration['visitorCookie'], 'consentVersion' => $configuration['consentVersion'], 'consentMaxAge' => $configuration['consentMaxAge'], 'cookieDomain' => $configuration['cookieDomain'], 'secureCookies' => $configuration['secureCookies'], 'trackingEndpoint' => 'nutzungsanalyse.php', ]; $json = json_encode( $browserConfiguration, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); if (!is_string($json)) { return ''; } return <<<'HTML' HTML . "\n\n" . '' . "\n"; }