value; } return $this->serverData[$key] ?? $default; } public function has(string|ServerKey $key): bool { ! $key instanceof ServerKey ?: $key = $key->value; return array_key_exists($key, $this->serverData); } // Häufig verwendete Server-Informationen als typisierte Methoden public function getRemoteAddr(): IpAddress { $ip = $this->get(ServerKey::REMOTE_ADDR, '0.0.0.0'); return new IpAddress($ip); } public function getUserAgent(): UserAgent { $userAgentString = $this->get(ServerKey::HTTP_USER_AGENT, ''); return UserAgent::fromString($userAgentString); } public function getServerName(): string { return $this->get(ServerKey::SERVER_NAME, ''); } public function getServerPort(): int { return (int) $this->get(ServerKey::SERVER_PORT, 80); } public function getRequestUri(): Url { $uriString = $this->get(ServerKey::REQUEST_URI, '/'); return UrlFactory::parse($uriString); } public function getScriptName(): string { return $this->get(ServerKey::SCRIPT_NAME, ''); } public function getHttpHost(): string { return $this->get(ServerKey::HTTP_HOST, ''); } public function isHttps(): bool { return $this->get(ServerKey::HTTPS) === 'on' || $this->get(ServerKey::SERVER_PORT) === '443' || $this->get(ServerKey::HTTP_X_FORWARDED_PROTO) === 'https'; } public function getRequestMethod(): Method { $methodString = $this->get(ServerKey::REQUEST_METHOD, 'GET'); return Method::tryFrom($methodString) ?? Method::GET; } public function getQueryString(): string { return $this->get(ServerKey::QUERY_STRING, ''); } public function getClientIp(): IpAddress { return new IpAddress($this->getClientIpString()); } public function getProtocol(): ServerProtocol { $protocol = $this->get(ServerKey::SERVER_PROTOCOL); // Handle null values in CLI context if ($protocol === null) { return ServerProtocol::HTTP_1_0; } return ServerProtocol::tryFrom($protocol) ?? ServerProtocol::HTTP_1_0; } private function getClientIpString(): string { // Priorisierte IP-Erkennung $candidates = [ ServerKey::HTTP_X_REAL_IP, ServerKey::HTTP_X_FORWARDED_FOR, ServerKey::REMOTE_ADDR, ]; foreach ($candidates as $key) { $ip = $this->get($key); if ($ip) { // Bei X-Forwarded-For kann es mehrere IPs geben if ($key === ServerKey::HTTP_X_FORWARDED_FOR) { $ips = explode(',', $ip); return trim($ips[0]); } return $ip; } } return '0.0.0.0'; } public function getReferer(): string { return $this->get(ServerKey::HTTP_REFERER, ''); } public function getRefererUri(): Url { $referer = $this->getReferer(); return UrlFactory::parse($referer); } /** * Prüft ob ein Referer gesetzt ist */ public function hasReferer(): bool { return ! empty($this->get(ServerKey::HTTP_REFERER, null)); } /** * Prüft ob der Referer von der gleichen Domain stammt */ public function isRefererSameDomain(): bool { $referer = $this->getReferer(); if (empty($referer)) { return false; } $refererHost = parse_url($referer, PHP_URL_HOST); $currentHost = $this->getHttpHost(); return $refererHost === $currentHost; } /** * Sichere Referer-URL für Redirects */ public function getSafeRefererUrl(string $fallback = '/'): string { $referer = $this->getReferer(); // Kein Referer gesetzt if (empty($referer)) { return $fallback; } // Nur interne Referer erlauben (CSRF-Schutz) if (! $this->isRefererSameDomain()) { return $fallback; } return $referer; } /** * Get WebSocket key for WebSocket handshake */ public function getWebSocketKey(): ?string { return $this->get(ServerKey::HTTP_SEC_WEBSOCKET_KEY); } /** * Check if request is XMLHttpRequest (AJAX) */ public function isXmlHttpRequest(): bool { return $this->get(ServerKey::HTTP_X_REQUESTED_WITH) === 'XMLHttpRequest'; } /** * Check if request has SPA header */ public function isSpaRequest(): bool { return $this->has(ServerKey::HTTP_X_SPA_REQUEST); } }