Search by

stougeiro / base-contract

stougeiro

Runtime-agnostic contracts and abstract bases for modular HTTP applications.

Package info

github.com/stougeiro/base-contract

pkg:composer/stougeiro/base-contract

Fund package maintenance!

Buymeacoffee

Statistics

Installs: 4

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-11-01 03:37 UTC

This package is auto-updated.

Last update: 2026-11-01 03:55:52 UTC


README

PHP PHPStan-Level License

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

  • ModuleProviderInterface and ModuleProviderAbstract for application modules;
  • ControllerInterface and ControllerAbstract, including the standard OPTIONS/Allow behaviour;
  • RoutingMiddlewareCollectionInterface for module and controller middleware maps;
  • HttpException, an exception that already carries an HTTP status and headers;
  • ResultInterface, ActionServiceInterface and CheckServiceInterface;
  • 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.