context = $context; $this->errorCode = $errorCode; $this->retryAfter = $retryAfter ?? $errorCode?->getRetryAfterSeconds(); } public function getContext(): ExceptionContext { return $this->context; } public function __clone(): void { // Allow cloning of the exception for immutable modifications } public function withContext(ExceptionContext $context): self { return new static( $this->getMessage(), $context, $this->getCode(), $this->getPrevious(), $this->errorCode, $this->retryAfter ); } public function withOperation(string $operation, ?string $component = null): self { return $this->withContext( $this->context->withOperation($operation, $component) ); } public function withData(array $data): self { return $this->withContext( $this->context->withData($data) ); } public function withDebug(array $debug): self { return $this->withContext( $this->context->withDebug($debug) ); } public function withMetadata(array $metadata): self { return $this->withContext( $this->context->withMetadata($metadata) ); } public function toArray(): array { $array = [ 'class' => static::class, 'message' => $this->getMessage(), 'code' => $this->getCode(), 'file' => $this->getFile(), 'line' => $this->getLine(), 'context' => $this->context->toArray(), 'trace' => $this->getTraceAsString(), ]; // ErrorCode-spezifische Daten hinzufügen wenn vorhanden if ($this->errorCode) { $array['error_code'] = $this->errorCode->value; $array['error_category'] = $this->errorCode->getCategory(); $array['description'] = $this->errorCode->getDescription(); $array['recovery_hint'] = $this->errorCode->getRecoveryHint(); $array['is_recoverable'] = $this->errorCode->isRecoverable(); $array['retry_after'] = $this->retryAfter; } return $array; } // === Factory Methods === /** * Einfache Exception ohne ErrorCode - für schnelle Verwendung */ public static function simple( string $message, ?\Throwable $previous = null, int $code = 0 ): static { return new static( message: $message, context: ExceptionContext::empty(), code: $code, previous: $previous ); } /** * Exception mit ErrorCode und automatischer Beschreibung */ public static function create( ErrorCode $errorCode, ?string $message = null, ?ExceptionContext $context = null, ?\Throwable $previous = null, int $code = 0 ): static { $finalMessage = $message ?? $errorCode->getDescription(); $finalContext = $context ?? ExceptionContext::empty(); return new static( message: $finalMessage, context: $finalContext, code: $code, previous: $previous, errorCode: $errorCode ); } /** * Exception mit Operation Context */ public static function forOperation( string $operation, ?string $component = null, ?string $message = null, ?ErrorCode $errorCode = null, ?\Throwable $previous = null, int $code = 0 ): static { $context = ExceptionContext::forOperation($operation, $component); $finalMessage = $message ?? $errorCode?->getDescription() ?? "Operation failed: $operation"; return new static( message: $finalMessage, context: $context, code: $code, previous: $previous, errorCode: $errorCode ); } /** * Exception mit vollständigem Context und Daten */ public static function fromContext( string $message, ExceptionContext $context, ?ErrorCode $errorCode = null, ?\Throwable $previous = null, int $code = 0 ): static { return new static( message: $message, context: $context, code: $code, previous: $previous, errorCode: $errorCode ); } // === ErrorCode Getter Methods === public function getErrorCode(): ?ErrorCode { return $this->errorCode; } public function getRetryAfter(): ?int { return $this->retryAfter; } public function isRecoverable(): bool { return $this->errorCode?->isRecoverable() ?? false; } public function getRecoveryHint(): ?string { return $this->errorCode?->getRecoveryHint(); } // === ErrorCode Utility Methods === /** * Prüft ob Exception von bestimmtem Error Code Typ ist */ public function isErrorCode(ErrorCode $errorCode): bool { return $this->errorCode === $errorCode; } /** * Prüft ob Exception zu bestimmter Kategorie gehört */ public function isCategory(string $category): bool { return $this->errorCode?->getCategory() === strtoupper($category); } /** * Setzt ErrorCode nachträglich */ public function withErrorCode(ErrorCode $errorCode): self { $new = clone $this; $new->errorCode = $errorCode; $new->retryAfter = $errorCode->getRetryAfterSeconds(); return $new; } /** * Setzt Custom Retry-Zeit */ public function withRetryAfter(int $seconds): self { $new = clone $this; $new->retryAfter = $seconds; return $new; } /** * Get exception data from context */ public function getData(): array { return $this->context->data; } /** * String-Representation für Logging */ public function __toString(): string { $errorCodePart = $this->errorCode ? '[' . $this->errorCode->value . ']' : ''; return sprintf( '%s %s: %s in %s:%d', static::class, $errorCodePart, $this->getMessage(), $this->getFile(), $this->getLine() ); } }