khasinski / php-solid-use-case
SolidUseCase pattern in PHP - Railway-oriented programming with Either/Success/Fail monads
Installs: 7
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/khasinski/php-solid-use-case
Requires
- php: ^8.1
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2026-01-20 22:26:22 UTC
README
A PHP implementation of the SolidUseCase pattern - Railway-oriented programming with Either/Success/Fail monads.
Requirements
- PHP 8.1 or higher
Installation
composer require khasinski/php-solid-use-case
Usage
Basic Example
Create a use case class that uses the SolidUseCase trait and define your steps:
<?php use KHasinski\SolidUseCase\Either; use KHasinski\SolidUseCase\Fail; use KHasinski\SolidUseCase\SolidUseCase; use KHasinski\SolidUseCase\Success; class CreateUser { use SolidUseCase; protected static array $steps = [ 'validateInput', 'checkEmailUnique', 'createUser', 'sendWelcomeEmail', ]; private function validateInput(array $params): Either { if (empty($params['email'])) { return new Fail('validation_error', 'Email is required'); } if (empty($params['name'])) { return new Fail('validation_error', 'Name is required'); } return new Success($params); } private function checkEmailUnique(array $params): Either { // Check if email exists in database $emailExists = false; // Your logic here if ($emailExists) { return new Fail('duplicate_email', $params['email']); } return new Success($params); } private function createUser(array $params): Either { // Create user in database $user = [ 'id' => 1, 'name' => $params['name'], 'email' => $params['email'], ]; return new Success($user); } private function sendWelcomeEmail(array $user): Either { // Send email return new Success($user); } }
Running a Use Case
$useCase = new CreateUser(); $result = $useCase->run([ 'name' => 'John Doe', 'email' => 'john@example.com', ]); // Pattern matching on the result $result->match( success: fn($user) => "Created user: {$user['name']}", failures: [ 'validation_error' => fn($message) => "Validation failed: {$message}", 'duplicate_email' => fn($email) => "Email already exists: {$email}", ] );
Checking Result Type
if ($result->isSuccess()) { $user = $result->getValue(); // Handle success } if ($result->isFail()) { $error = $result->getValue(); $errorType = $result->getType(); // Handle failure }
API Reference
Either Interface
The base interface for both Success and Fail types:
match(callable $success, array $failures = []): mixed- Pattern match on the resultisSuccess(): bool- Check if result is a successisFail(): bool- Check if result is a failuregetValue(): mixed- Get the wrapped value
Success Class
Represents a successful result:
$success = new Success($value); $success->value; // The wrapped value (public readonly) $success->getValue(); // Same as above
Fail Class
Represents a failed result with an optional type:
$fail = new Fail('error_type', $errorData); $fail->type; // The error type (public readonly) $fail->value; // The error data (public readonly) $fail->getType(); // Same as $fail->type $fail->getValue(); // Same as $fail->value
SolidUseCase Trait
The main trait that provides the run() method:
class MyUseCase { use SolidUseCase; // Option 1: Override getSteps() method protected function getSteps(): array { return ['step1', 'step2']; } // Option 2: Define static $steps property // protected static array $steps = ['step1', 'step2']; private function step1(mixed $input): Either { /* ... */ } private function step2(mixed $input): Either { /* ... */ } } $result = (new MyUseCase())->run($initialParams);
Exceptions
UnimplementedStep- Thrown when a step method doesn't existInvalidStepReturnType- Thrown when a step doesn't return anEitherinstance
Development
# Install dependencies composer install # Run tests composer test # Run static analysis composer analyse
License
MIT