stougeiro / base-contract
Runtime-agnostic contracts and abstract bases for modular HTTP applications.
Fund package maintenance!
Requires
- php: >=8.2
- stougeiro/container-contract: ^4.0
- stougeiro/http-contract: ^4.1
- stougeiro/immutable: ^2.0
- stougeiro/router-contract: ^2.1
Requires (Dev)
- pestphp/pest: ^3.8
- phpstan/phpstan: ^2.2
- stougeiro/container: ^4.0
- stougeiro/http: ^2.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-01 03:55:52 UTC
README
Base Contract
Contracts and abstract bases shared by modular HTTP runtimes and their modules. This package defines only the boundaries; it does not provide a kernel, router, views or transport implementation.
✨ Features
ModuleProviderInterfaceandModuleProviderAbstractfor application modules;ControllerInterfaceandControllerAbstract, including the standardOPTIONS/Allowbehaviour;RoutingMiddlewareCollectionInterfacefor module and controller middleware maps;HttpException, an exception that already carries an HTTP status and headers;ResultInterface,ActionServiceInterfaceandCheckServiceInterface;LogInterface.
Concrete kernels, routers, views and transport-specific code belong to each runtime.
📦 Installation
Install via Composer:
composer require stougeiro/base-contract
Requirements
- PHP 8.2 or newer.
🚀 Usage
This package has no runtime. The snippets below are implementation suggestions; the runtime is responsible for instantiating providers and dispatching controllers.
Module provider
use Sourcerer\Contract\Module\ModuleProviderAbstract; final class BlogModule extends ModuleProviderAbstract { public function configure(): void { $this->routes(__DIR__ . '/routes.php'); $this->middlewares(__DIR__ . '/middlewares.php'); } }
Controller
use Sourcerer\Contract\Http\ControllerAbstract; use STDW\Contract\Http\ResponseInterface; final class HealthController extends ControllerAbstract { /** @var list<string> */ protected array $allowed = ['get']; public function get(): ResponseInterface { return $this->response->withBody('ok'); } }
OPTIONS is always answered by ControllerAbstract, with 204 No Content and
the Allow header. A verb outside $allowed raises a 405 HttpException.
🧠 Why?
Modules need to declare routes, controllers and middlewares without depending on a concrete runtime. Keeping these contracts in a separate package:
- keeps modules dependent only on the boundaries they actually use;
- allows the same module to run on different HTTP runtimes (FPM, Swoole, ...);
- leaves transport, kernel, views and bootstrapping to each runtime;
- avoids circular dependencies between modules, runtimes and applications.
🤝 Contributions
Contributions are welcome. Feel free to open issues or submit pull requests.