Search by

solophp / contracts

Vitaliy Olos

Contracts and interfaces for Solo ecosystem

Package info

github.com/SoloPHP/Contracts

pkg:composer/solophp/contracts

Statistics

Installs: 988

Dependents: 9

Suggesters: 0

Stars: 0

Open Issues: 0

v1.9.0 2026-11-01 16:07 UTC

README

Latest Version on Packagist License PHP Version

A collection of interfaces and contracts for the SoloPHP ecosystem. This package provides standardized contracts that enable interoperability between different SoloPHP components and allow developers to create custom implementations.

Requirements

  • PHP 8.1 or higher

Installation

Install the package via Composer:

composer require solophp/contracts

Available Contracts

Router

Deprecated since 1.6, removed in 2.0. solophp/router 4 does not implement these interfaces and solophp/application 4.0.1 does not use them: routes are registered on Solo\Router\RouteCollector, and Solo\Router\Router::resolve() returns Matched, NotFound or MethodNotAllowed with the allowed methods. They remain for solophp/router 3.x and solophp/application up to 4.0.0.

RouterInterface - Framework-agnostic interface for HTTP routing implementations.

use Solo\Contracts\Router\RouterInterface;

class MyRouter implements RouterInterface
{
    public function addRoute(string $method, string $path, $handler, array $options = []): object
    {
        // Your implementation
        return $this; // Enables fluent interface
    }

    public function match(string $method, string $uri): array|false
    {
        // Your implementation
    }
}

match() returns false or ['handler' => ..., 'params' => [...], 'middlewares' => [...]], optionally with name and attributes. Method names are case-sensitive (RFC 9110), and parameters are returned as they appear in the URI path: decoding them is up to the caller.

AllowedMethodsInterface - Optional router capability: the HTTP methods a path is routed for, to answer 405 Method Not Allowed with an Allow header instead of 404 Not Found.

use Solo\Contracts\Router\AllowedMethodsInterface;
use Solo\Contracts\Router\RouterInterface;

class MyRouter implements RouterInterface, AllowedMethodsInterface
{
    // addRoute() and match() as above

    public function getAllowedMethods(string $uri): array
    {
        return ['GET', 'HEAD']; // or [] when no route matches the path
    }
}

Job Queue

JobInterface - Interface for executable job classes.

use Solo\Contracts\JobQueue\JobInterface;

class SendEmailJob implements JobInterface
{
    public function handle(): void
    {
        // Job execution logic
    }
}

JobProducerInterface - Enqueues jobs: push() and pushMany(). Type-hint it in services that only schedule work.

use Solo\Contracts\JobQueue\JobProducerInterface;

class SignupService
{
    public function __construct(private JobProducerInterface $jobs)
    {
    }

    public function register(string $email): void
    {
        $this->jobs->push(new SendEmailJob($email), 'email');
    }
}

JobProcessorInterface - Executes queued jobs. processJobs() runs maintenance and then up to $limit jobs, processNext() runs one job without maintenance, and maintain() recovers attempts abandoned by stopped workers and expires overdue jobs. Workers and schedulers depend on it.

JobQueueInterface - Deprecated since 1.9. The broad v1 queue interface; use JobProducerInterface and JobProcessorInterface instead.

JobQueueListener - Observability hook for queue events. Implement to ship metrics (Datadog, Prometheus), open tracing spans, or enrich log context per job.

use Solo\Contracts\JobQueue\JobQueueListener;

class MetricsListener implements JobQueueListener
{
    public function onClaimed(int $jobId, string $jobClass): void { /* ... */ }
    public function onCompleted(int $jobId): void { /* ... */ }
    public function onFailed(int $jobId, \Throwable|string $error, bool $permanent): void { /* ... */ }
    public function onReclaimed(int $requeuedCount, int $permanentlyFailedCount): void { /* ... */ }
}

Validator

ValidatorInterface - Interface for data validation implementations.

A field missing from the data, or null in it, has no value: only the rules that decide whether a value is needed — such as required, filled, present or required_if — check it. Any other value, '' and [] included, goes through all of the field's rules, unless its rules say otherwise. So 'email' alone accepts a missing or null email, and 'required|email' refuses both.

use Solo\Contracts\Validator\ValidatorInterface;

class MyValidator implements ValidatorInterface
{
    public function validate(array $data, array $rules): array
    {
        // Validation logic; an absent or null field meets presence rules only
        // Returns structured errors: ['field' => [['rule' => 'required', 'params' => [...]], ...]]
        return $errors;
    }
}

solophp/validator implements it from 4.2; this package conflicts with its earlier versions, which check null with every rule.

Container

WritableContainerInterface - Extends PSR-11 ContainerInterface with write capability for service registration. Services are shared: the factory receives the container, runs on the first get() of the id, and later calls return the same instance.

An id that names a class or interface resolves to an instance of it: an implementation throws a ContainerExceptionInterface rather than return anything else. get() is typed accordingly, so static analysis sees $container->get(Mailer::class) as a Mailer and any other id as mixed, and a factory's untyped parameter as the container:

use Solo\Contracts\Container\WritableContainerInterface;

function register(WritableContainerInterface $container): void
{
    $container->set(Mailer::class, fn($c) => new Mailer($c->get(Transport::class)));
}
use Solo\Contracts\Container\WritableContainerInterface;

class MyContainer implements WritableContainerInterface
{
    public function set(string $id, callable $factory): void
    {
        // Register service factory
    }

    public function get(string $id): mixed
    {
        // Retrieve service; an id naming a class must yield an instance of it
    }

    public function has(string $id): bool
    {
        // Check if service exists
    }
}

solophp/container implements it from 4.0; this package conflicts with its earlier versions, which do not check the class of an entry.

Http

EmitterInterface - Interface for HTTP response emitters that send PSR-7 responses to the client.

use Solo\Contracts\Http\EmitterInterface;

class SapiEmitter implements EmitterInterface
{
    public function emit(ResponseInterface $response): void
    {
        // Emit headers and body to client
    }
}

Usage

{
    "require": {
        "solophp/contracts": "^1.7"
    }
}

Benefits

  • Interoperability - Components built on these contracts work together seamlessly
  • Flexibility - Swap implementations without changing dependent code
  • Type Safety - Strong typing ensures contract compliance
  • Framework Agnostic - Use in any PHP project

License

MIT