php-standard-library/result
A lightweight Result type for PHP that represents success or failure as a value, enabling controlled error handling without exceptions. Helps you return, compose, and inspect outcomes explicitly for safer, predictable application flow.
Installation Add the package via Composer:
composer require php-standard-library/result
No additional configuration is required—it’s a standalone type.
First Use Case: Basic Result Handling
Import the Result class and use it to wrap operations that may fail:
use PhpStandardLibrary\Result\Result;
function divide($a, $b) {
if ($b === 0) {
return Result::fail("Division by zero");
}
return Result::ok($a / $b);
}
$result = divide(10, 0);
if ($result->isOk()) {
echo "Result: " . $result->unwrap();
} else {
echo "Error: " . $result->unwrapErr();
}
Where to Look First
Result::ok(), Result::fail(), and methods like isOk(), unwrap(), and unwrapErr().Result.php for edge cases (e.g., unwrap() on a failure).Use Result to avoid exceptions for expected failures (e.g., validation, external API calls):
function fetchUser($id) {
$user = User::find($id);
return $user ? Result::ok($user) : Result::fail("User not found");
}
$fetchResult = fetchUser(999);
$fetchResult->map(fn($user) => $user->name); // Returns Result<null> on failure
map/flatMapCompose operations safely:
$finalResult = fetchUser(1)
->flatMap(fn($user) => validateUser($user)) // Result<ValidationResult>
->map(fn($validation) => $validation->save());
Result from services instead of throwing exceptions.Result to JSON responses:
return response()->json($result->isOk()
? ['success' => true, 'data' => $result->unwrap()]
: ['success' => false, 'error' => $result->unwrapErr()]
);
Result in rules() or authorize():
public function rules() {
$validation = $this->validateCustomRule();
if ($validation->isFail()) {
throw new \Exception($validation->unwrapErr());
}
return [...];
}
Mock Result in unit tests to isolate success/failure paths:
public function test_success_path() {
$mockResult = Result::ok(new User());
$this->assertEquals("John", $mockResult->map(fn($u) => $u->name)->unwrap());
}
unwrap() on Failure
Calling unwrap() or unwrapErr() on a Result with the opposite state throws an exception. Always check isOk() first or use match():
$result->match(
fn($value) => $value->name, // Success case
fn($error) => "Error: $error" // Failure case
);
Immutable Results
Result is immutable. Avoid modifying the underlying value after creation.
No Null Safety
Unlike Laravel’s Maybe, Result does not distinguish between null and a failed operation. Use Result::fail(null) explicitly if needed.
dd($result->isOk(), $result->unwrapOr('default')) to debug.Result interactions interactively:
php artisan tinker
>>> $result = Result::fail("Test");
>>> $result->match(fn($v) => $v, fn($e) => "Caught: $e");
// Output: "Caught: Test"
Custom Error Types
Extend Result to support domain-specific errors:
class DomainError extends \Exception {}
Result::fail(new DomainError("Custom error"));
Laravel Exception Handler
Convert Result::fail() into HTTP exceptions in a middleware:
public function handle($request, Closure $next) {
$response = $next($request);
if ($response instanceof Result && $response->isFail()) {
throw new \HttpResponseException(response()->json([
'error' => $response->unwrapErr()
]));
}
return $response;
}
Performance
For high-throughput code (e.g., bulk operations), prefer match() over chained map() calls to avoid intermediate objects.
How can I help you explore Laravel packages today?