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

dev-master 2026-01-20 22:24 UTC

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 result
  • isSuccess(): bool - Check if result is a success
  • isFail(): bool - Check if result is a failure
  • getValue(): 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 exist
  • InvalidStepReturnType - Thrown when a step doesn't return an Either instance

Development

# Install dependencies
composer install

# Run tests
composer test

# Run static analysis
composer analyse

License

MIT