errorScope !== null) { $scopeContext = $this->errorScope->current(); if ($scopeContext !== null) { $context = $this->enrichFromScope($context, $scopeContext); } } return $context; } /** * Build context from HTTP request * * Extracts context from HTTP request, session, and authentication. * Uses cache if available to improve performance. */ public function buildFromRequest(Request $request, ?ExceptionContextData $baseContext = null): ExceptionContextData { // Try to get from cache first if ($this->contextCache !== null) { $requestId = $request instanceof \App\Framework\Http\HttpRequest ? $request->id->toString() : null; $sessionId = property_exists($request, 'session') && $request->session !== null ? $request->session->id->toString() : null; $userId = $this->extractUserId($request); $cached = $this->contextCache->get($requestId, $sessionId, $userId); if ($cached !== null) { // Merge with base context if provided if ($baseContext !== null) { return $this->mergeContexts($cached, $baseContext); } return $cached; } } $context = $baseContext ?? ExceptionContextData::empty(); // Extract request ID if ($request instanceof \App\Framework\Http\HttpRequest) { $context = $context->withRequestId($request->id->toString()); } // Extract IP address $ipAddress = $request->server->getRemoteAddr(); if ($ipAddress !== null) { $ipValue = $ipAddress->value; if (is_string($ipValue) && IpAddress::isValid($ipValue)) { $context = $context->withClientIp(IpAddress::from($ipValue)); } else { $context = $context->withClientIp($ipValue); } } // Extract user agent $userAgent = $request->server->getUserAgent(); if ($userAgent !== null) { $userAgentValue = $userAgent->value; if (is_string($userAgentValue)) { $context = $context->withUserAgent(UserAgent::fromString($userAgentValue)); } else { $context = $context->withUserAgent($userAgentValue); } } // Extract session ID if (property_exists($request, 'session') && $request->session !== null) { $sessionId = $request->session->id->toString(); try { $context = $context->withSessionId(SessionId::fromString($sessionId)); } catch (\InvalidArgumentException) { // If SessionId validation fails, keep as string for backward compatibility $context = $context->withSessionId($sessionId); } } // Extract user ID (if authenticated) $userId = $this->extractUserId($request); if ($userId !== null) { $context = $context->withUserId($userId); } // Add HTTP-specific tags $context = $context->withTags('http', 'web'); // Enrich from ErrorScope if available (may override some values) if ($this->errorScope !== null) { $scopeContext = $this->errorScope->current(); if ($scopeContext !== null) { $context = $this->enrichFromScope($context, $scopeContext); } } // Cache context if cache is available if ($this->contextCache !== null) { $requestId = $request instanceof \App\Framework\Http\HttpRequest ? $request->id->toString() : null; $sessionId = property_exists($request, 'session') && $request->session !== null ? $request->session->id->toString() : null; $userId = $this->extractUserId($request); $this->contextCache->put($context, $requestId, $sessionId, $userId); } return $context; } /** * Merge two contexts (base takes precedence) */ private function mergeContexts(ExceptionContextData $cached, ExceptionContextData $base): ExceptionContextData { $merged = $cached; // Override with base context values if present if ($base->operation !== null) { $merged = $merged->withOperation($base->operation, $base->component); } if ($base->component !== null && $merged->component === null) { $merged = $merged->withOperation($merged->operation ?? '', $base->component); } if (! empty($base->data)) { $merged = $merged->addData($base->data); } if (! empty($base->debug)) { $merged = $merged->addDebug($base->debug); } if (! empty($base->metadata)) { $merged = $merged->addMetadata($base->metadata); } if ($base->userId !== null) { $merged = $merged->withUserId($base->userId); } if ($base->requestId !== null) { $merged = $merged->withRequestId($base->requestId); } if ($base->sessionId !== null) { $merged = $merged->withSessionId($base->sessionId); } if ($base->clientIp !== null) { $merged = $merged->withClientIp($base->clientIp); } if ($base->userAgent !== null) { $merged = $merged->withUserAgent($base->userAgent); } if (! empty($base->tags)) { $merged = $merged->withTags(...array_merge($merged->tags, $base->tags)); } return $merged; } /** * Enrich context from ErrorScope */ private function enrichFromScope( ExceptionContextData $context, \App\Framework\ExceptionHandling\Scope\ErrorScopeContext $scopeContext ): ExceptionContextData { // Add operation/component from scope if not already set if ($context->operation === null && $scopeContext->operation !== null) { $context = $context->withOperation( $scopeContext->operation, $scopeContext->component ); } // Add user ID from scope if not already set if ($context->userId === null && $scopeContext->userId !== null) { $context = $context->withUserId($scopeContext->userId); } // Add request ID from scope if not already set if ($context->requestId === null && $scopeContext->requestId !== null) { $context = $context->withRequestId($scopeContext->requestId); } // Add session ID from scope if not already set if ($context->sessionId === null && $scopeContext->sessionId !== null) { if (is_string($scopeContext->sessionId)) { try { $context = $context->withSessionId(SessionId::fromString($scopeContext->sessionId)); } catch (\InvalidArgumentException) { $context = $context->withSessionId($scopeContext->sessionId); } } else { $context = $context->withSessionId($scopeContext->sessionId); } } // Extract HTTP fields from scope metadata (for HTTP scopes) if (isset($scopeContext->metadata['ip']) && $context->clientIp === null) { $ipValue = $scopeContext->metadata['ip']; if (is_string($ipValue) && IpAddress::isValid($ipValue)) { $context = $context->withClientIp(IpAddress::from($ipValue)); } elseif ($ipValue instanceof IpAddress) { $context = $context->withClientIp($ipValue); } else { $context = $context->withClientIp($ipValue); } } if (isset($scopeContext->metadata['user_agent']) && $context->userAgent === null) { $userAgentValue = $scopeContext->metadata['user_agent']; if (is_string($userAgentValue)) { $context = $context->withUserAgent(UserAgent::fromString($userAgentValue)); } elseif ($userAgentValue instanceof UserAgent) { $context = $context->withUserAgent($userAgentValue); } else { $context = $context->withUserAgent($userAgentValue); } } // Add scope metadata $context = $context->addMetadata([ 'scope_type' => $scopeContext->type->value, 'scope_id' => $scopeContext->scopeId, ]); // Add scope tags if (! empty($scopeContext->tags)) { $context = $context->withTags(...$scopeContext->tags); } return $context; } /** * Extract user ID from request */ private function extractUserId(Request $request): ?string { // Try to get from request attribute (set by auth middleware) if (method_exists($request, 'getAttribute')) { $user = $request->getAttribute('user'); if ($user !== null) { if (is_object($user) && property_exists($user, 'id')) { return (string) $user->id; } if (is_string($user)) { return $user; } } } // Try to get from request property (if available) if (property_exists($request, 'user') && $request->user !== null) { if (is_object($request->user) && property_exists($request->user, 'id')) { return (string) $request->user->id; } if (is_string($request->user)) { return $request->user; } } return null; } }