Created
January 26, 2026 22:30
-
-
Save jaketoolson/daaea7df12e435366b88b58b1a7f5128 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| declare(strict_types=1); | |
| namespace Platform\Application; | |
| use Throwable; | |
| /** | |
| * @template T | |
| */ | |
| readonly class Result | |
| { | |
| /** | |
| * @param T|null $value | |
| */ | |
| public function __construct( | |
| private bool $isSuccess = false, | |
| private mixed $value = null, | |
| private ?string $errorMessage = null, | |
| private ?Throwable $exception = null, | |
| private ?int $code = null | |
| ) {} | |
| /** | |
| * Create a successful result with a value | |
| * | |
| * @template TValue | |
| * | |
| * @param TValue|null $value | |
| * @return Result<TValue> | |
| */ | |
| public static function ok(mixed $value = null): self | |
| { | |
| return new self(true, $value); | |
| } | |
| /** | |
| * Alias for ok() | |
| * | |
| * @template TValue | |
| * | |
| * @param TValue|null $value | |
| * @return Result<TValue> | |
| */ | |
| public static function success(mixed $value = null): self | |
| { | |
| return self::ok($value); | |
| } | |
| /** | |
| * Create a failed result with an error message | |
| * | |
| * @template TValue | |
| * | |
| * @param string $errorMessage The error message | |
| * @param int|TValue|null $codeOrValue HTTP status code (int) or value (mixed). If int, treated as code; otherwise as value | |
| * @param TValue|Throwable|null $valueOrException Value or exception. If previous param was int (code), this is value or exception. If previous was value, this must be exception or null | |
| * @return Result<TValue> | |
| */ | |
| public static function fail(string $errorMessage, mixed $codeOrValue = null, mixed $valueOrException = null): self | |
| { | |
| // Handle different call signatures: | |
| // 1. fail('message') -> code: null, value: null, exception: null | |
| // 2. fail('message', 404) -> code: 404, value: null, exception: null | |
| // 3. fail('message', 404, $exception) -> code: 404, value: null, exception: $exception | |
| // 4. fail('message', ['data']) -> code: null, value: ['data'], exception: null | |
| // 5. fail('message', ['data'], $exception) -> code: null, value: ['data'], exception: $exception | |
| $code = null; | |
| $value = null; | |
| $exception = null; | |
| if ($codeOrValue !== null) { | |
| if (is_int($codeOrValue)) { | |
| // Parameter is HTTP status code | |
| $code = $codeOrValue; | |
| // Third parameter could be value or exception | |
| if ($valueOrException !== null) { | |
| if ($valueOrException instanceof Throwable) { | |
| $exception = $valueOrException; | |
| } else { | |
| $value = $valueOrException; | |
| } | |
| } | |
| } else { | |
| // Parameter is value (not a code) | |
| $value = $codeOrValue; | |
| // Third parameter must be exception if provided | |
| if ($valueOrException !== null) { | |
| if ($valueOrException instanceof Throwable) { | |
| $exception = $valueOrException; | |
| } else { | |
| // Invalid usage - third param should be exception | |
| throw new \InvalidArgumentException('When second parameter is a value, third parameter must be a Throwable or null'); | |
| } | |
| } | |
| } | |
| } | |
| return new self(false, $value, $errorMessage, $exception, $code); | |
| } | |
| /** | |
| * Alias for fail() | |
| * | |
| * @template TValue | |
| * | |
| * @param int|TValue|null $codeOrValue | |
| * @param TValue|Throwable|null $valueOrException | |
| * @return Result<TValue> | |
| */ | |
| public static function error(string $errorMessage, mixed $codeOrValue = null, mixed $valueOrException = null): self | |
| { | |
| return self::fail($errorMessage, $codeOrValue, $valueOrException); | |
| } | |
| final public function isSuccess(): bool | |
| { | |
| return $this->isSuccess === true; | |
| } | |
| final public function isError(): bool | |
| { | |
| return $this->isSuccess === false; | |
| } | |
| final public function getErrorMessage(): ?string | |
| { | |
| return $this->errorMessage; | |
| } | |
| final public function getErrorException(): ?Throwable | |
| { | |
| return $this->exception; | |
| } | |
| /** | |
| * Get the value of the result | |
| * | |
| * @return T|null | |
| */ | |
| final public function getValue(): mixed | |
| { | |
| return $this->value; | |
| } | |
| /** | |
| * Get the HTTP status code (if set) | |
| */ | |
| final public function code(): ?int | |
| { | |
| return $this->code; | |
| } | |
| /** | |
| * Get the error message (alias for getErrorMessage()) | |
| * | |
| * Note: Cannot use error() as method name since there's already a static error() factory method | |
| */ | |
| final public function getError(): ?string | |
| { | |
| return $this->errorMessage; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment