Files
michaelschiemer/src/Framework/Analytics/AnalyticsCollector.php

330 lines
8.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Analytics;
use App\Framework\Analytics\Storage\AnalyticsStorage;
use App\Framework\Http\ServerEnvironment;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Random\RandomGenerator;
/**
* Moderner Analytics-Collector basierend auf dem Performance-System
*
* Sammelt Business- und User-Analytics-Daten und nutzt das bewährte
* Performance-Framework für effiziente Datensammlung.
*/
final class AnalyticsCollector
{
/** @var array<string, mixed> */
private array $sessionData = [];
/** @var array<string, int> */
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);
}
}