PHPackages                             valbeat/result - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. valbeat/result

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

valbeat/result
==============

A Result type implementation for PHP inspired by Rust

v0.2.0(1mo ago)7853↓76.9%[2 issues](https://github.com/valbeat/php-result/issues)[2 PRs](https://github.com/valbeat/php-result/pulls)MITPHPPHP ^8.3CI passing

Since Jul 15Pushed 3w agoCompare

[ Source](https://github.com/valbeat/php-result)[ Packagist](https://packagist.org/packages/valbeat/result)[ Docs](https://github.com/valbeat/php-result)[ RSS](/packages/valbeat-result/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (9)Dependencies (12)Versions (22)Used By (0)

PHP Result
==========

[](#php-result)

[![Packagist Version](https://camo.githubusercontent.com/980aac8b6c90b8af619ea952853f0db47c8d913746bcaf882de7789478facdad/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f76616c626561742f726573756c74)](https://camo.githubusercontent.com/980aac8b6c90b8af619ea952853f0db47c8d913746bcaf882de7789478facdad/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f76616c626561742f726573756c74)[![codecov](https://camo.githubusercontent.com/7511f59a5fa294ac08ec4871f81fbde740594ac22afb239c88a5b19e86c02f50/68747470733a2f2f636f6465636f762e696f2f67682f76616c626561742f7068702d726573756c742f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://codecov.io/gh/valbeat/php-result)

A Result type implementation for PHP inspired by Rust's `Result` type.

This library provides a robust way to handle operations that might fail, without relying on exceptions. It encourages explicit error handling by making the error case part of the return type, so failures are visible in function signatures instead of being hidden control flow.

Installation
------------

[](#installation)

```
composer require valbeat/result
```

Requirements
------------

[](#requirements)

- PHP 8.3 or higher (tested on PHP 8.3, 8.4 and 8.5)
- Composer

Basic Usage
-----------

[](#basic-usage)

```
use Valbeat\Result\Ok;
use Valbeat\Result\Err;

// Creating Results
$success = new Ok(42);
$failure = new Err("Something went wrong");

// Pattern matching with the match() method
$message = $success->match(
    ok: fn($value) => "Success: $value",
    err: fn($error) => "Error: $error"
);
echo $message; // "Success: 42"

// Checking if a Result is Ok or Err
if ($success->isOk()) {
    echo "Operation succeeded!";
}

if ($failure->isErr()) {
    echo "Operation failed!";
}

// Unwrapping values (throws exception on error)
$value = $success->unwrap(); // 42
// $failure->unwrap(); // throws UnwrapException

// Safe unwrapping with default values
$value = $failure->unwrapOr(0); // 0
$value = $failure->unwrapOrElse(fn($err) => strlen($err)); // 20
```

Advanced Usage
--------------

[](#advanced-usage)

### Transforming Results

[](#transforming-results)

```
// Map over success values
$result = (new Ok(5))
    ->map(fn($x) => $x * 2)
    ->map(fn($x) => $x + 1);
echo $result->unwrap(); // 11

// Map over error values
$result = (new Err("error"))
    ->mapErr(fn($e) => strtoupper($e));
echo $result->unwrapErr(); // "ERROR"
```

### Chaining Operations

[](#chaining-operations)

```
// Chain operations that might fail
$result = (new Ok(10))
    ->andThen(fn($x) => $x > 5 ? new Ok($x * 2) : new Err("Too small"))
    ->andThen(fn($x) => new Ok($x + 5));
echo $result->unwrap(); // 25

// Short-circuit on first error
$result = (new Ok(2))
    ->andThen(fn($x) => $x > 5 ? new Ok($x * 2) : new Err("Too small"));
echo $result->unwrapErr(); // "Too small"
```

Each step in a chain may fail with a **different error type**. The error types are composed into a union, so PHPStan tracks every error the chain can produce:

```
final class ValidationError {}
final class NotFoundError {}

/** @return Result */
function validateUserId(string $raw): Result
{
    return ctype_digit($raw) ? new Ok((int) $raw) : new Err(new ValidationError());
}

/** @return Result */
function findUserNameById(int $id): Result
{
    return $id === 42 ? new Ok('Alice') : new Err(new NotFoundError());
}

// PHPStan infers Result
$userName = validateUserId('42')->andThen(findUserNameById(...));
echo $userName->unwrap(); // "Alice"
```

### Combining Results

[](#combining-results)

```
// Use first Ok value
$result = (new Err("first error"))
    ->or(new Err("second error"))
    ->or(new Ok(42));
echo $result->unwrap(); // 42

// Use first Ok or call function
$result = (new Err("error"))
    ->orElse(fn($e) => new Ok(strlen($e)));
echo $result->unwrap(); // 5
```

### Side Effects

[](#side-effects)

```
// Inspect values without consuming the Result
$result = (new Ok(42))
    ->inspect(fn($x) => print("Value is: $x\n"))
    ->map(fn($x) => $x * 2);

// Inspect errors
$result = (new Err("oops"))
    ->inspectErr(fn($e) => error_log("Error occurred: $e"));
```

### Helpers (Results class)

[](#helpers-results-class)

```
use Valbeat\Result\Results;

// Wrap exception-throwing code: Ok on success, Err on throw
$result = Results::try(fn() => json_decode($raw, flags: JSON_THROW_ON_ERROR));

// Combine many Results: Ok with all values, or the first Err
$result = Results::combine([new Ok(1), new Ok(2), new Ok(3)]);
echo implode(',', $result->unwrap()); // "1,2,3"

// Flatten a nested Result into Result
$result = Results::flatten(new Ok(new Ok(42)));
echo $result->unwrap(); // 42
```

Type Safety
-----------

[](#type-safety)

This library is designed to be used with [PHPStan](https://phpstan.org/) at level max and leans on several of its generics features:

- **Sealed interface** — `Result` is annotated with `@phpstan-sealed Ok|Err`, so PHPStan knows `Ok` and `Err` are the only implementations. A `match (true)` over `instanceof` checks is recognized as exhaustive, and the `else` branch of an `instanceof Ok` check narrows to `Err`. Note that `instanceof` narrowing loses the type arguments (a known PHPStan limitation: `Result` narrows to plain `Ok`, so `unwrap()` becomes `mixed`), so use `instanceof` in a `match (true)` purely for exhaustiveness. When you also need the values, the `match()` method handles both cases and keeps `T`/`E` (it requires both an `ok`and an `err` arm), or narrow with `isOk()`/`isErr()`. These two goals are a trade-off in PHPStan 2.2.2: *enforced* exhaustiveness — where adding a new `Result` variant would turn every unhandled site into an analysis error — comes only from `instanceof` in a `match (true)`, which is exactly the form that drops the type arguments. The `match()` method and `isOk()`/`isErr()` keep the generics but are not checked against variant additions (`isOk()`/`isErr()` arms in a `match (true)` also need a `default`). So per call site you currently pick one: enforced exhaustiveness *or* preserved generics. (In practice `Result` is fixed at `Ok|Err`, so the `match()` method covering both is total for all real cases.)
- **Covariant type parameters** — `T` and `E` are declared `@template-covariant`, so `Ok` (which is `Result`) and `Err` (which is `Result`) are assignable to any `Result`. A function declared to return `Result` can simply `return new Ok($user);`.
- **Error-type composition** — `andThen()`/`and()` widen the error channel to `E|F` and `orElse()`/`or()` widen the success channel to `T|U`, so chains that mix failure types stay precisely typed. (This deliberately diverges from Rust, whose `and`/`or` family keeps the other channel's type fixed.)
- **Type narrowing** — `isOk()`/`isErr()` narrow `$result` to `Ok`/`Err`via `@phpstan-assert-if-true`; `unwrap()`/`unwrapErr()` use conditional return types (`never` on the impossible side), and `unwrapOr()`/`unwrapOrElse()`resolve to `T` on `Ok` and to the default's type on `Err`.
- **Exhaustive error matching** — when the error type `E` is a native `enum` or a `@phpstan-sealed` union, the error value can be matched exhaustively (over the enum cases, or `instanceof` arms for a sealed union) and PHPStan enforces it — a missing case becomes an analysis error. Reach the error through `isErr()` + `unwrapErr()`, or the `match()` method's `err` arm; narrowing with `instanceof Err` drops `E` to `mixed` and loses the enum/sealed type (the same `instanceof` limitation as above). As long as the error classes are themselves non-generic, their own `instanceof` checks have no type arguments to lose (unlike the generic `Ok`/`Err`).
- **Precise concrete receivers** — when the receiver is statically `Ok` or `Err`, no-op methods keep their exact type (`$ok->orElse(...)` stays `Ok`, `$err->andThen(...)` stays `Err`) instead of widening to a union.

Note: because the templates are covariant, PHPStan preserves constant value types (`new Ok(10)` is `Ok`, not `Ok`). Type a variable or parameter as `int`if you want the widened type.

API Reference
-------------

[](#api-reference)

### Result Methods

[](#result-methods)

All Result types (both Ok and Err) implement these methods:

#### Type Checking

[](#type-checking)

- `isOk(): bool` - Returns true if the Result is Ok
- `isOkAnd(callable $fn): bool` - Returns true if the Result is Ok and the predicate returns true
- `isErr(): bool` - Returns true if the Result is Err
- `isErrAnd(callable $fn): bool` - Returns true if the Result is Err and the predicate returns true

#### Value Extraction

[](#value-extraction)

- `unwrap(): mixed` - Returns the success value or throws UnwrapException (extends LogicException)
- `unwrapErr(): mixed` - Returns the error value or throws UnwrapException (extends LogicException)
- `expect(string $message): mixed` - Returns the success value or throws UnwrapException with the given message and a summary of the error value
- `expectErr(string $message): mixed` - Returns the error value or throws UnwrapException with the given message and a summary of the success value
- `unwrapOr(mixed $default): mixed` - Returns the success value or a default
- `unwrapOrElse(callable $fn): mixed` - Returns the success value or computes it from the error

#### Transformation

[](#transformation)

- `map(callable $fn): Result` - Maps a Result&lt;T, E&gt; to Result&lt;U, E&gt; by applying a function to the success value
- `mapErr(callable $fn): Result` - Maps a Result&lt;T, E&gt; to Result&lt;T, F&gt; by applying a function to the error value
- `mapOr(mixed $default, callable $fn): mixed` - Maps the success value or returns a default
- `mapOrElse(callable $defaultFn, callable $fn): mixed` - Maps the success value or computes a default from the error

#### Combination

[](#combination)

- `and(Result $res): Result` - Returns the second Result if the first is Ok, otherwise returns the first Err
- `andThen(callable $fn): Result` - Chains another operation that returns a Result
- `or(Result $res): Result` - Returns the first Ok or the second Result if the first is Err
- `orElse(callable $fn): Result` - Returns the first Ok or calls a function with the error to produce a Result

#### Side Effects

[](#side-effects-1)

- `inspect(callable $fn): Result` - Calls a function with the success value if Ok
- `inspectErr(callable $fn): Result` - Calls a function with the error value if Err

#### Pattern Matching

[](#pattern-matching)

- `match(callable $ok, callable $err): mixed` - Pattern match on the Result

### Results Helpers (static)

[](#results-helpers-static)

- `Results::try(callable $fn): Result` - Runs a callable and wraps the outcome: Ok with the return value, or Err with the thrown Throwable
- `Results::combine(iterable $results): Result` - Combines `iterable` into `Result`, short-circuiting on the first Err
- `Results::flatten(Result $result): Result` - Flattens `Result` into `Result`

License
-------

[](#license)

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

###  Health Score

46

—

FairBetter than 92% of packages

Maintenance88

Actively maintained with recent releases

Popularity23

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 76.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~44 days

Recently: every ~12 days

Total

9

Last Release

43d ago

PHP version history (2 changes)v0.0.0PHP ^8.4

v0.2.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/da8040b74364fe9a4f820bea150770a248a2b00cc9b67f658f6524507e174dc8?d=identicon)[valbeat](/maintainers/valbeat)

---

Top Contributors

[![valbeat](https://avatars.githubusercontent.com/u/3125309?v=4)](https://github.com/valbeat "valbeat (163 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (45 commits)")[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (4 commits)")

---

Tags

phpresult-patternresult-typetyperesultfunctionalerror handlingmonadrust

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/valbeat-result/health.svg)

```
[![Health](https://phpackages.com/badges/valbeat-result/health.svg)](https://phpackages.com/packages/valbeat-result)
```

###  Alternatives

[jetbrains/phpstorm-stubs

PHP runtime &amp; extensions header files for PhpStorm

1.4k34.7M92](/packages/jetbrains-phpstorm-stubs)[marc-mabe/php-enum

Simple and fast implementation of enumerations with native PHP

49863.5M114](/packages/marc-mabe-php-enum)[symfony/type-info

Extracts PHP types information.

20077.1M314](/packages/symfony-type-info)[chippyash/monad

Functional programming Monad support

2728.6k8](/packages/chippyash-monad)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
