valbeat/result
Composer 安装命令:
composer require valbeat/result
包简介
A Result type implementation for PHP inspired by Rust
README 文档
README
A Result type implementation for PHP inspired by Rust's Result<T, E> 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
composer require valbeat/result
Requirements
- PHP 8.3 or higher (tested on PHP 8.3, 8.4 and 8.5)
- Composer
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
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
// 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<int, ValidationError> */ function validateUserId(string $raw): Result { return ctype_digit($raw) ? new Ok((int) $raw) : new Err(new ValidationError()); } /** @return Result<string, NotFoundError> */ function findUserNameById(int $id): Result { return $id === 42 ? new Ok('Alice') : new Err(new NotFoundError()); } // PHPStan infers Result<string, ValidationError|NotFoundError> $userName = validateUserId('42')->andThen(findUserNameById(...)); echo $userName->unwrap(); // "Alice"
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
// 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)
use Valbeat\Result\Results; // Wrap exception-throwing code: Ok on success, Err<Throwable> 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<Result<T, E2>, E1> into Result<T, E1|E2> $result = Results::flatten(new Ok(new Ok(42))); echo $result->unwrap(); // 42
Type Safety
This library is designed to be used with PHPStan at level max and leans on several of its generics features:
- Sealed interface —
Resultis annotated with@phpstan-sealed Ok|Err, so PHPStan knowsOkandErrare the only implementations. Amatch (true)overinstanceofchecks is recognized as exhaustive, and theelsebranch of aninstanceof Okcheck narrows toErr. Note thatinstanceofnarrowing loses the type arguments (a known PHPStan limitation:Result<int, E>narrows to plainOk, sounwrap()becomesmixed), so useinstanceofin amatch (true)purely for exhaustiveness. When you also need the values, thematch()method handles both cases and keepsT/E(it requires both anokand anerrarm), or narrow withisOk()/isErr(). These two goals are a trade-off in PHPStan 2.2.2: enforced exhaustiveness — where adding a newResultvariant would turn every unhandled site into an analysis error — comes only frominstanceofin amatch (true), which is exactly the form that drops the type arguments. Thematch()method andisOk()/isErr()keep the generics but are not checked against variant additions (isOk()/isErr()arms in amatch (true)also need adefault). So per call site you currently pick one: enforced exhaustiveness or preserved generics. (In practiceResultis fixed atOk|Err, so thematch()method covering both is total for all real cases.) - Covariant type parameters —
TandEare declared@template-covariant, soOk<T>(which isResult<T, never>) andErr<E>(which isResult<never, E>) are assignable to anyResult<T, E>. A function declared to returnResult<User, DbError>can simplyreturn new Ok($user);. - Error-type composition —
andThen()/and()widen the error channel toE|FandorElse()/or()widen the success channel toT|U, so chains that mix failure types stay precisely typed. (This deliberately diverges from Rust, whoseand/orfamily keeps the other channel's type fixed.) - Type narrowing —
isOk()/isErr()narrow$resulttoOk<T>/Err<E>via@phpstan-assert-if-true;unwrap()/unwrapErr()use conditional return types (neveron the impossible side), andunwrapOr()/unwrapOrElse()resolve toTonOkand to the default's type onErr. - Exhaustive error matching — when the error type
Eis a nativeenumor a@phpstan-sealedunion, the error value can be matched exhaustively (over the enum cases, orinstanceofarms for a sealed union) and PHPStan enforces it — a missing case becomes an analysis error. Reach the error throughisErr()+unwrapErr(), or thematch()method'serrarm; narrowing withinstanceof ErrdropsEtomixedand loses the enum/sealed type (the sameinstanceoflimitation as above). As long as the error classes are themselves non-generic, their owninstanceofchecks have no type arguments to lose (unlike the genericOk/Err). - Precise concrete receivers — when the receiver is statically
Ok<T>orErr<E>, no-op methods keep their exact type ($ok->orElse(...)staysOk<T>,$err->andThen(...)staysErr<E>) instead of widening to a union.
Note: because the templates are covariant, PHPStan preserves constant value types
(new Ok(10) is Ok<10>, not Ok<int>). Type a variable or parameter as int
if you want the widened type.
API Reference
Result Methods
All Result types (both Ok and Err) implement these methods:
Type Checking
isOk(): bool- Returns true if the Result is OkisOkAnd(callable $fn): bool- Returns true if the Result is Ok and the predicate returns trueisErr(): bool- Returns true if the Result is ErrisErrAnd(callable $fn): bool- Returns true if the Result is Err and the predicate returns true
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 valueexpectErr(string $message): mixed- Returns the error value or throws UnwrapException with the given message and a summary of the success valueunwrapOr(mixed $default): mixed- Returns the success value or a defaultunwrapOrElse(callable $fn): mixed- Returns the success value or computes it from the error
Transformation
map(callable $fn): Result- Maps a Result<T, E> to Result<U, E> by applying a function to the success valuemapErr(callable $fn): Result- Maps a Result<T, E> to Result<T, F> by applying a function to the error valuemapOr(mixed $default, callable $fn): mixed- Maps the success value or returns a defaultmapOrElse(callable $defaultFn, callable $fn): mixed- Maps the success value or computes a default from the error
Combination
and(Result $res): Result- Returns the second Result if the first is Ok, otherwise returns the first ErrandThen(callable $fn): Result- Chains another operation that returns a Resultor(Result $res): Result- Returns the first Ok or the second Result if the first is ErrorElse(callable $fn): Result- Returns the first Ok or calls a function with the error to produce a Result
Side Effects
inspect(callable $fn): Result- Calls a function with the success value if OkinspectErr(callable $fn): Result- Calls a function with the error value if Err
Pattern Matching
match(callable $ok, callable $err): mixed- Pattern match on the Result
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 ThrowableResults::combine(iterable $results): Result- Combinesiterable<Result<T, E>>intoResult<list<T>, E>, short-circuiting on the first ErrResults::flatten(Result $result): Result- FlattensResult<Result<T, E2>, E1>intoResult<T, E1|E2>
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
valbeat/result 适用场景与选型建议
valbeat/result 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 811 次下载、GitHub Stars 达 7, 最近一次更新时间为 2025 年 07 月 15 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「type」 「functional」 「monad」 「error-handling」 「result」 「rust」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 valbeat/result 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 valbeat/result 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 valbeat/result 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A bundle providing fields for image upload with jquery upload and image cropping with jcrop for symfony2
Option type (Some/None) replacing nullable types with explicit presence semantics
Result type capturing success or failure as a value for controlled error handling
Connection 'Db' codeception module to 'Yii2' module database settings
A simple Symfony bundle that adds boolean form field type.
Functional library for php with proper currying
统计信息
- 总下载量: 811
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 8
- 点击次数: 18
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-07-15