*/ private array $sessionData = []; /** @var array */ private array $counters = []; public function __construct( private readonly PerformanceCollectorInterface $performanceCollector, private readonly AnalyticsStorage $storage, private readonly RandomGenerator $random, private readonly ServerEnvironment $serverEnvironment, private readonly bool $enabled = true, private readonly float $samplingRate = 1.0 ) { } /** * Trackt eine Benutzeraktion */ public function trackAction( string $action, AnalyticsCategory $category, array $properties = [] ): void { error_log("Analytics: trackAction called with action={$action}, category={$category->value}"); if (! $this->shouldTrack()) { error_log("Analytics: trackAction skipped (shouldTrack=false)"); return; } $context = array_merge([ 'action' => $action, 'category' => $category->value, 'session_id' => $this->getSessionId(), 'timestamp' => time(), 'user_agent' => (string) $this->serverEnvironment->getUserAgent(), 'ip' => (string) $this->serverEnvironment->getClientIp(), ], $properties); // Nutze Performance-System für Analytics $this->performanceCollector->recordMetric( "analytics_action_{$action}", PerformanceCategory::CUSTOM, 1, $context ); $this->performanceCollector->increment( 'analytics_actions_total', PerformanceCategory::CUSTOM, 1, $context ); // Speichere in Analytics-Storage error_log("Analytics: About to call storage->storeRawData and storeAggregatedData"); $this->storage->storeRawData($context, $this->samplingRate); $this->storeAggregatedData('user_actions', $action, $context); } /** * Trackt eine Seitenansicht */ public function trackPageView( string $path, string $title = '', array $properties = [] ): void { if (! $this->shouldTrack()) { return; } $context = array_merge([ 'path' => $path, 'title' => $title, 'referer' => $this->serverEnvironment->getReferer(), 'session_id' => $this->getSessionId(), 'timestamp' => time(), ], $properties); $this->performanceCollector->recordMetric( 'analytics_page_view', PerformanceCategory::CUSTOM, 1, $context ); $this->performanceCollector->increment( 'analytics_page_views_total', PerformanceCategory::CUSTOM, 1, $context ); // Speichere in Analytics-Storage $this->storage->storeRawData($context, $this->samplingRate); $this->storeAggregatedData('page_views', $path, $context); } /** * Trackt einen Fehler */ public function trackError( string $errorType, string $message, array $context = [] ): void { if (! $this->shouldTrack()) { return; } $errorContext = array_merge([ 'error_type' => $errorType, 'message' => $message, 'session_id' => $this->getSessionId(), 'timestamp' => time(), 'url' => (string) $this->serverEnvironment->getRequestUri(), ], $context); $this->performanceCollector->recordMetric( "analytics_error_{$errorType}", PerformanceCategory::CUSTOM, 1, $errorContext ); $this->performanceCollector->increment( 'analytics_errors_total', PerformanceCategory::CUSTOM, 1, $errorContext ); } /** * Trackt ein Business-Event (Conversion, Purchase, etc.) */ public function trackBusinessEvent( string $event, float $value = 0.0, string $currency = 'EUR', array $properties = [] ): void { if (! $this->shouldTrack()) { return; } $context = array_merge([ 'event' => $event, 'value' => $value, 'currency' => $currency, 'session_id' => $this->getSessionId(), 'timestamp' => time(), ], $properties); $this->performanceCollector->recordMetric( "analytics_business_{$event}", PerformanceCategory::CUSTOM, $value, $context ); $this->performanceCollector->increment( 'analytics_business_events_total', PerformanceCategory::CUSTOM, 1, $context ); } /** * Trackt API-Nutzung */ public function trackApiCall( string $endpoint, string $method, int $responseCode, float $responseTime, array $properties = [] ): void { if (! $this->shouldTrack()) { return; } $context = array_merge([ 'endpoint' => $endpoint, 'method' => $method, 'response_code' => $responseCode, 'response_time' => $responseTime, 'session_id' => $this->getSessionId(), 'timestamp' => time(), ], $properties); $this->performanceCollector->recordMetric( 'analytics_api_call', PerformanceCategory::CUSTOM, $responseTime, $context ); $this->performanceCollector->increment( 'analytics_api_calls_total', PerformanceCategory::CUSTOM, 1, $context ); } /** * Setzt Session-Daten */ public function setSessionData(string $key, mixed $value): void { $this->sessionData[$key] = $value; } /** * Holt Session-Daten */ public function getSessionData(string $key): mixed { return $this->sessionData[$key] ?? null; } /** * Erhöht einen Counter */ public function incrementCounter(string $name, int $amount = 1): void { if (! isset($this->counters[$name])) { $this->counters[$name] = 0; } $this->counters[$name] += $amount; } /** * Holt Counter-Wert */ public function getCounter(string $name): int { return $this->counters[$name] ?? 0; } /** * Holt alle Counter */ public function getCounters(): array { return $this->counters; } /** * Prüft ob Tracking durchgeführt werden soll (Sampling) */ private function shouldTrack(): bool { error_log("Analytics: shouldTrack check - enabled={$this->enabled}, samplingRate={$this->samplingRate}"); if (! $this->enabled) { error_log("Analytics: Tracking disabled"); return false; } if ($this->samplingRate >= 1.0) { error_log("Analytics: Tracking enabled (no sampling)"); return true; } return $this->random->float(0, 1) <= $this->samplingRate; } /** * Generiert oder holt Session-ID */ private function getSessionId(): string { if (session_status() === PHP_SESSION_ACTIVE) { return session_id(); } // Fallback: Cookie-basierte Session-ID if (isset($_COOKIE['analytics_session'])) { return $_COOKIE['analytics_session']; } // Generiere neue Session-ID (Cookie-Setzung erfolgt über Response-System) // Hier nur ID generieren, nicht setzen um "headers already sent" zu vermeiden return bin2hex($this->random->bytes(16)); } /** * Speichert aggregierte Daten im Storage */ private function storeAggregatedData(string $category, string $action, array $context): void { $period = 'hour'; // Standard-Aggregations-Periode $aggregatedData = [ "{$category}_total" => 1, "{$category}_{$action}" => 1, 'unique_sessions' => [$context['session_id'] ?? 'unknown' => 1], ]; $this->storage->storeAggregated($period, $aggregatedData); } }