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.1.0(2w ago)6811↓75%[1 PRs](https://github.com/valbeat/php-result/pulls)MITPHPPHP ^8.4CI passing

Since Jul 15Pushed 5d 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 2d ago

READMEChangelog (8)Dependencies (9)Versions (21)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 and makes it impossible to accidentally ignore errors.

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

[](#installation)

```
composer require valbeat/result
```

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

[](#requirements)

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

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

[](#basic-usage)

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

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

// Pattern matching with match expression
$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 LogicException

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

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"));
```

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 LogicException
- `unwrapErr(): mixed` - Returns the error value or throws LogicException
- `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 $okFn, callable $errFn): mixed` - Pattern match on the 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

48

—

FairBetter than 93% of packages

Maintenance98

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 73.3% 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 ~48 days

Total

8

Last Release

16d ago

### 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 (129 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (43 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

[phpoption/phpoption

Option Type for PHP

2.7k579.0M176](/packages/phpoption-phpoption)[graham-campbell/result-type

An Implementation Of The Result Type

554414.2M10](/packages/graham-campbell-result-type)[jetbrains/phpstorm-stubs

PHP runtime &amp; extensions header files for PhpStorm

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

Simple and fast implementation of enumerations with native PHP

50458.3M110](/packages/marc-mabe-php-enum)[symfony/type-info

Extracts PHP types information.

20069.8M270](/packages/symfony-type-info)[chippyash/monad

Functional programming Monad support

2828.5k8](/packages/chippyash-monad)

PHPackages © 2026

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