Files
michaelschiemer/src/Framework/Http/ServerEnvironment.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

228 lines
5.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Http;
use App\Framework\Http\Url\Url;
use App\Framework\Http\Url\UrlFactory;
use App\Framework\UserAgent\UserAgent;
/**
* Kapselt Server-Environment-Daten aus $_SERVER
*/
final readonly class ServerEnvironment
{
public function __construct(
private array $serverData = []
) {
}
public static function fromGlobals(): self
{
return new self($_SERVER);
}
public function get(string|ServerKey $key, mixed $default = null): mixed
{
if ($key instanceof ServerKey) {
$key = $key->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);
}
}