Files
michaelschiemer/src/Framework/ErrorBoundaries/BoundaryResult.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

207 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries;
use Throwable;
/**
* Result wrapper for error boundary operations
*
* @template T
*/
final readonly class BoundaryResult
{
private function __construct(
private mixed $value,
private ?Throwable $error,
private bool $isSuccess,
private ?string $boundaryName = null
) {
}
/**
* Create successful result
*
* @template U
* @param U $value
* @return BoundaryResult<U>
*/
public static function success(mixed $value): self
{
return new self($value, null, true);
}
/**
* Create failed result
*
* @template U
* @param Throwable $error
* @param string|null $boundaryName
* @return BoundaryResult<U>
*/
public static function failure(Throwable $error, ?string $boundaryName = null): self
{
return new self(null, $error, false, $boundaryName);
}
/**
* Check if operation was successful
*/
public function isSuccess(): bool
{
return $this->isSuccess;
}
/**
* Check if operation failed
*/
public function isFailure(): bool
{
return ! $this->isSuccess;
}
/**
* Get the result value (only if successful)
*
* @return T
* @throws BoundaryFailedException
*/
public function getValue(): mixed
{
if (! $this->isSuccess) {
throw new BoundaryFailedException(
'Cannot get value from failed result',
$this->boundaryName ?? 'unknown',
$this->error
);
}
return $this->value;
}
/**
* Get the error (only if failed)
*/
public function getError(): ?Throwable
{
return $this->error;
}
/**
* Get boundary name
*/
public function getBoundaryName(): ?string
{
return $this->boundaryName;
}
/**
* Get value or return default if failed
*
* @param T $default
* @return T
*/
public function getValueOrDefault(mixed $default): mixed
{
return $this->isSuccess ? $this->value : $default;
}
/**
* Get value or execute fallback function if failed
*
* @param callable(): T $fallback
* @return T
*/
public function getValueOrElse(callable $fallback): mixed
{
return $this->isSuccess ? $this->value : $fallback();
}
/**
* Transform the result if successful
*
* @template U
* @param callable(T): U $mapper
* @return BoundaryResult<U>
*/
public function map(callable $mapper): self
{
if (! $this->isSuccess) {
return new self(null, $this->error, false, $this->boundaryName);
}
try {
$transformed = $mapper($this->value);
return new self($transformed, null, true, $this->boundaryName);
} catch (Throwable $e) {
return new self(null, $e, false, $this->boundaryName);
}
}
/**
* Chain another operation if current result is successful
*
* @template U
* @param callable(T): BoundaryResult<U> $mapper
* @return BoundaryResult<U>
*/
public function flatMap(callable $mapper): self
{
if (! $this->isSuccess) {
return new self(null, $this->error, false, $this->boundaryName);
}
try {
return $mapper($this->value);
} catch (Throwable $e) {
return new self(null, $e, false, $this->boundaryName);
}
}
/**
* Execute callback if result is successful
*
* @param callable(T): void $callback
* @return self
*/
public function onSuccess(callable $callback): self
{
if ($this->isSuccess) {
$callback($this->value);
}
return $this;
}
/**
* Execute callback if result failed
*
* @param callable(Throwable): void $callback
* @return self
*/
public function onFailure(callable $callback): self
{
if (! $this->isSuccess && $this->error) {
$callback($this->error);
}
return $this;
}
/**
* Convert to array representation
*/
public function toArray(): array
{
return [
'success' => $this->isSuccess,
'value' => $this->isSuccess ? $this->value : null,
'error' => $this->error?->getMessage(),
'boundary' => $this->boundaryName,
];
}
}