Files
michaelschiemer/src/Framework/Exception/FrameworkException.php
Michael Schiemer e30753ba0e fix: resolve RedisCache array offset error and improve discovery diagnostics
- Fix RedisCache driver to handle MGET failures gracefully with fallback
- Add comprehensive discovery context comparison debug tools
- Identify root cause: WEB context discovery missing 166 items vs CLI
- WEB context missing RequestFactory class entirely (52 vs 69 commands)
- Improved exception handling with detailed binding diagnostics
2025-09-12 20:05:18 +02:00

274 lines
6.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Exception;
class FrameworkException extends \RuntimeException
{
protected ExceptionContext $context;
protected ?ErrorCode $errorCode;
protected ?int $retryAfter;
public function __construct(
string $message,
ExceptionContext $context,
int $code = 0,
?\Throwable $previous = null,
?ErrorCode $errorCode = null,
?int $retryAfter = null
) {
parent::__construct($message, $code, $previous);
$this->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()
);
}
}