solophp / contracts
Contracts and interfaces for Solo ecosystem
Requires
- php: ^8.1
- psr/container: ^2.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
- solophp/container: <4.0
- solophp/validator: <4.2
Replaces
None
This package is auto-updated.
Last update: 2026-11-01 17:47:15 UTC
README
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/router4 does not implement these interfaces andsolophp/application4.0.1 does not use them: routes are registered onSolo\Router\RouteCollector, andSolo\Router\Router::resolve()returnsMatched,NotFoundorMethodNotAllowedwith the allowed methods. They remain forsolophp/router3.x andsolophp/applicationup 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