prewk/option
Lightweight Option type for PHP providing Some/None to avoid nulls. Adds map/flatMap/filter, unwrap with defaults, and safe chaining inspired by functional programming. Handy for Laravel and general PHP codebases where nullable values cause bugs.
Option type enforces explicit handling of absence, aligning with Rust-inspired functional patterns. This is a strong fit for Laravel applications where null checks are pervasive (e.g., API request handling, Eloquent queries, or middleware). The package’s design mitigates "null hell" by replacing implicit null with explicit Some/None variants, reducing runtime errors and improving code clarity.map, filter, and flatMap methods enable declarative pipelines, which are particularly useful in Laravel’s request/response cycle (e.g., chaining validations or transformations). This complements Laravel’s existing functional tools (e.g., Collection methods) and reduces nested if-else or isset() logic.User::find($id)->toOption()).Option-returning controllers (e.g., Option<JsonResource>).match() or unwrapOr() may introduce boilerplate compared to native PHP syntax (e.g., ?: or null coalescing).$option->map(fn($x) => ...)).Option types.Option::Some($user)). Custom toOption() methods can be added to Eloquent query builders.return null with return Option::None and using match() for HTTP responses.prewk/result: Hard dependency. Assess whether Result is needed (e.g., for error handling) or if Option alone suffices. If Result is unnecessary, consider forking the package to remove it.Option or creating helper methods for common use cases).Option types if configured to recognize them (e.g., via custom type mappings).| Risk Area | Severity | Mitigation Strategy |
|---|---|---|
| Breaking Changes | Medium | Test thoroughly with PHP 8.1–8.3; monitor updates to prewk/result. |
| Performance Overhead | Low | Benchmark critical paths; Option is a thin wrapper with negligible overhead. |
| Adoption Resistance | High | Pilot in a single module first; provide training and migration guides. |
| Type System Gaps | Medium | Use PHP 8.1 attributes (e.g., #[\ReturnTypeWillChange]) for gradual adoption. |
| Debugging Complexity | Medium | Add custom __toString() methods for Option in development. |
| Tooling Limitations | Medium | Configure Psalm/PHPStan to recognize Option types; create custom IDE plugins if needed. |
Option replace all null returns in the codebase, or only specific domains (e.g., API layer, Eloquent queries)?Option types for better error detection?Nullable trait or interface.Collection methods (e.g., first() + manual checks).Nette\Utils\Callback.Option types (e.g., mocking Some/None)?toOption() methods) if the package evolves?prewk/result justified, or can the package be forked to remove it if unnecessary?return null with return Option::None and use match() for HTTP responses.Option in method signatures (e.g., findUser(): Option<User>) and leverage map/filter for functional pipelines.toOption() method for query results:
$user = User::find(1)->toOption(); // Option::Some($user) or Option::None
Option<JsonResource>) for consistent error handling.Option to explicitly handle missing data (e.g., Option::fromRequest($request->input('token'))).Symfony\Component\OptionsResolver for configuration.Option in DTOs or repository methods (e.g., Option<User>).Phase 1: Proof of Concept (1–2 weeks)
Option for nullable values (e.g., replace ?User with Option<User>).// Before
public function findUser(?int $id): ?User { ... }
// After
public function findUser(?int $id): Option { ... }
toOption() for Eloquent) and test thoroughly.Phase 2: Core Services (2–4 weeks)
null complexity (e.g., payment processing, caching).isset($var) with Option::isSome($var) and null coalescing with getOrElse().// Before
$name = $user->profile->name ?? 'Anonymous';
// After
$name = $user->profile()
->map(fn($profile) => $profile->name)
->getOrElse('Anonymous');
Phase 3: API Layer (3–6 weeks)
Option<JsonResource>).Option to HTTP responses:
return match ($user) {
Option::Some($user) => new UserResource($user),
Option::None => response()->json(['error' => 'Not found'], 404),
};
null-returning endpoints via Laravel’s API resources or middleware.Phase 4: Full Adoption (Ongoing)
Option in new features and gradually refactor legacy code.#[\ReturnTypeWillChange]) to mark deprecated null-returning methods.map/filter chaining).null-returning code will fail if not updated. Mitigate with adapter methods:
Option::fromNullable($var); // Converts ?T to Option<T>
$var = $option->unwrapOr(null); // Falls back to null
How can I help you explore Laravel packages today?