PHPackages                             front-interop/front-interop - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. front-interop/front-interop

Abandoned → [front-interop/interface](/?search=front-interop%2Finterface)Library[Utility &amp; Helpers](/categories/utility)

front-interop/front-interop
===========================

Interoperable FrontController interfaces for PHP.

0.x-dev(3mo ago)57MITPHP

Since Jan 15Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/front-interop/front-interop)[ Packagist](https://packagist.org/packages/front-interop/front-interop)[ RSS](/packages/front-interop-front-interop/feed)WikiDiscussions 0.x Synced 1mo ago

READMEChangelog (1)Dependencies (11)Versions (2)Used By (0)

`front-interop`
===============

[](#front-interop)

The `front-interop` project defines a set of interoperable interfaces for the [FrontController pattern](https://martinfowler.com/eaaCatalog/frontController.html) in PHP.

The following two interfaces define the request-receiving and response-sending behaviors at the outer boundary of your HTTP presentation layer:

- `RequestHandlerInterface::handleRequest() : ResponseHandler` encapsulates the logic to receive an incoming HTTP request and return the logic to send or emit an HTTP response.
- `ResponseHandlerInterface::handleResponse() : void` encapsulates the logic to send or emit an outgoing response.

Further, the *DelegatorInterface* defines the process for determining which logic will fulfill the request and return a response, using two methods:

- `DelegatorInterface::delegateRequest() : Delegate` encapsulates the logic to determine which controller, action, middleware stack, or other element will process the request.
- `DelegatorInterface::delegateThrowable(Throwable $e) : Delegate` encapsulates the logic to generate a response for any *Throwable* caught by the front controller system.

The *DelegateInterface* describes the delegated callable:

- `DelegateInterface::getCallable() : callable` returns the callable to invoke for processing.
- `DelegateInterface::getArguments() : array` returns the arguments to pass to the callable.

> N.b.: The delegated callable is entirely undefined by `front-interop`, and may use a router, middleware, Model-View-Controller presentation, Action-Domain-Responder presentation, or any other combination of components and collaborations.

Finally, the *ExceptionInterface* marker indicates an exception thrown by the front controller system.

Background
----------

[](#background)

The outer boundary of presentation logic in PHP frameworks tends to follow the same order of events: do some setup work, then create and invoke logic to process a request, and finally they send the resulting response.

The relevant Laravel [`public/index.php`](https://github.com/laravel/laravel/blob/10.x/public/index.php) code:

```
$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Kernel::class);

$response = $kernel->handle(
    $request = Request::capture()
)->send();
```

The Slim [`App::run()`](https://github.com/slimphp/Slim/blob/4.x/Slim/App.php) method:

```
public function run(?ServerRequestInterface $request = null): void
{
    if (!$request) {
        $serverRequestCreator = ServerRequestCreatorFactory::create();
        $request = $serverRequestCreator->createServerRequestFromGlobals();
    }

    $response = $this->handle($request);
    $responseEmitter = new ResponseEmitter();
    $responseEmitter->emit($response);
}
```

The relevant Symfony [Create Your Own PHP Framework](https://symfony.com/doc/current/create_framework/dependency_injection.html) `web/front.php` code:

```
$container = include __DIR__.'/../src/container.php';
$request = Request::createFromGlobals();
$response = $container->get('framework')->handle($request);
$response->send();
```

The relevant Kevin Smith no-framework [`public/index.php`](https://github.com/kevinsmith/no-framework/blob/master/public/index.php) code:

```
/** @noinspection PhpUnhandledExceptionInspection */
$requestHandler = new Relay($middlewareQueue);
$response = $requestHandler->handle(ServerRequestFactory::fromGlobals());

$emitter = new SapiEmitter();
/** @noinspection PhpVoidFunctionResultUsedInspection */
return $emitter->emit($response);
```

These systems are all very different internally, but their outer boundary logic is remarkably similar, as is that logic in many other systems.

Problem, Part 1: Request and Response
-------------------------------------

[](#problem-part-1-request-and-response)

Each of the above examples uses different request/response libraries. Laravel and Symfony use the Symfony HttpFoundation library, whereas Slim and the no-framework use the PSR-7 interfaces. Likewise, other frameworks may use some other library.

This raises a problem for interoperability, because the request and response objects are passed into and out of the front controller logic directly as method arguments and return values. No typehint can cover all the different possibilities, thus preventing interoperability between the different front controller implementations.

### Solution: *RequestHandlerInterface* and *ResponseHandlerInterface*

[](#solution-requesthandlerinterface-and-responsehandlerinterface)

The interoperability solution to this problem is twofold:

1. Instead of passing the request object as a method argument, move the request *creation* logic into the request *handling* logic, whether as a constructor parameter fulfilled by dependency injection, or via a request factory, or some other approach.
2. Instead of returning the response object directly, return a response *sending* object that encapsulates the response.

These two very minor changes allow for a pair of interfaces that are interoperable across a wide range of systems. These interfaces are completely independent of any particular request/response library; that is, they will work with PSR-7, Symfony HttpFoundation, Sapien, or any other request/response library.

### Example

[](#example)

Using the above interfaces, the outer boundary logic at a `public/index.php` bootstrap might look like this:

```
use FrontInterop\RequestHandlerInterface;
use FrontInterop\ResponseHandlerInterface;
use Psr\Container\ContainerInterface;

require dirname(__DIR__) . '/vendor/autoload.php';

/** @var ContainerInterface */
$container = require dirname(__DIR__) . '/config/container.php';

/** @var RequestHandlerInterface */
$requestHandler = $container->get(RequestHandlerInterface::class);

/** @var ResponseHandlerInterface */
$responseHandler = $requestHandler->handleRequest();
$responseHandler->handleResponse();
```

The *RequestHandlerInterface* and *ResponseHandlerInterface* implementations can now use any request and response objects they like via dependency injection, along with any router, middleware, or other libraries they need to pass a request to a controller or action and get back a response.

A condensed variation of the bootstrap might look like this:

```
use FrontInterop\RequestHandlerInterface;
use Psr\Container\ContainerInterface;

require dirname(__DIR__) . '/vendor/autoload.php';

/** @var ContainerInterface */
$container = require dirname(__DIR__) . '/config/container.php';

$container
    ->get(RequestHandlerInterface::class)
    ->handleRequest()
    ->handleResponse();
```

Aside from non-container setup, that would be the entire outer boundary code at `public/index.php`.

### *RequestHandlerInterface* Implementation

[](#requesthandlerinterface-implementation)

This [example RequestHandler.php](./example/RequestHandler.php) implementation uses [FastRoute](https://github.com/nikic/FastRoute) and callable action objects to process a Sapien request.

Note that the implementation does not return a Sapien response object directly; instead, it returns that response composed into an *SapienResponseHandler* implementation.

The *RequestHandlerInterface* implementation could be completely replaced by one that uses any combination of router, middleware dispatcher, controller or action invocation, and request/response objects, without changing any of the `public/index.php` bootstrap logic.

### *ResponseHandlerInterface* Implementation

[](#responsehandlerinterface-implementation)

Likewise, the *ResponseHandlerInterface* can encapsulate any response object and implement the appropriate response-sending logic. The `front-interop` project provides *ResponseHandlerInterface* implementations for these response objects ...

- [PSR-7](./impl/PsrResponseHandler.php)
- [Sapien](./impl/SapienResponseHandler.php)
- [Symfony](./impl/SymfonyResponseHandler.php)

... though of course consumers can write any implementation they desire.

The *ResponseHandlerInterface* implementation could be completely replaced without changing any of the bootstrap logic above.

Problem, Part 2: Routing and Middleware
---------------------------------------

[](#problem-part-2-routing-and-middleware)

The *RequestHandlerInterface* must direct the incoming request to some target logic that will fulfill the request and return a response. Typically this is achieved via ...

- a router subsystem that picks a controller method or action class to build a response; or,
- a middleware subsystem that processes the request on the way in and returns a response on the way out.

The problem is that these subsystems are not themselves compatible. For example, different routers define routes in different ways, and adhere to no common specification. Likewise, different middleware subsystems may use different middleware signatures.

The *RequestHandlerInterface* itself might manage the routing or middleware subsystem directly, as in the above example. However, by delegating the concern of subsystem management to a separate element, different subsystem implementations may be swapped out, leaving the rest of the *RequestHandlerInterface* logic unchanged.

### Solution: *DelegatorInterface* and *DelegateInterface*

[](#solution-delegatorinterface-and-delegateinterface)

The solution is to care *not* about the routing or middleware subsystems per se, but *instead* about **what is to be invoked as a result** of the subsystem operations. That is, to care about the *target* for request processing chosen by that subsytem. This *DelegateInterface* is composed of:

- a callable, such as a controller object method, an invokable action object, or a middleware stack; and,
- the arguments to pass to that callable, typically derived from the incoming request.

The *DelegatorInterface* implementation may use a routing system internally to determine the route, then use the route information to build and return a *DelegateInterface*. Alternatively, the *DelegatorInterface* implementation may just return a middleware stack as the *DelegateInterface*.

The *RequestHandlerInterface* implementaton then invokes the *DelegateInterface* callable and arguments to get back a response, and wraps that response in an appropriate *ResponseHandlerInterface*.

### *DelegatorInterface* Implementation

[](#delegatorinterface-implementation)

This [Delegator.php](./tests/Fake/Delegator.php) extracts the routing and object creation logic from the [example RequestHandler.php](./example/RequestHandler.php).

As a result, this revised [RequestHandler.php](./impl/RequestHandler.php) implementation can use any delegation subsystem, allowing a complete replacement of the router implementation with any other implementation, or even with any middleware implementation.

This means the *RequestHandlerInterface* no longer needs to know how to *choose* the logic to fulfill the request; it only needs to to *invoke* that logic to get back a response.

Components and Collaborations
-----------------------------

[](#components-and-collaborations)

- `index.php` calls *RequestHandlerInterface*
    - *RequestHandlerInterface* calls *DelegatorInterface*
        - *DelegatorInterface* returns a *DelegateInterface*
    - *RequestHandlerInterface* calls the identified *DelegateInterface* logic
        - That logic returns a response
    - *RequestHandlerInterface* returns a *ResponseHandlerInterface* with that response
- `index.php` invokes *ResponseHandlerInterface* to send the response

Prior Art
---------

[](#prior-art)

The [Laminas Mezzio](https://docs.mezzio.dev/mezzio/) project makes allowances for different DI containers, templating, routers, and error handlers, but requires PSR-7 at the outer boundary. As such, it does not achieve the interoperability goal of `front-interop`.

###  Health Score

32

—

LowBetter than 72% of packages

Maintenance81

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity27

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~752 days

Total

2

Last Release

100d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/25754?v=4)[Paul M. Jones](/maintainers/pmjones)[@pmjones](https://github.com/pmjones)

---

Top Contributors

[![pmjones](https://avatars.githubusercontent.com/u/25754?v=4)](https://github.com/pmjones "pmjones (14 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/front-interop-front-interop/health.svg)

```
[![Health](https://phpackages.com/badges/front-interop-front-interop/health.svg)](https://phpackages.com/packages/front-interop-front-interop)
```

###  Alternatives

[acirtautas/oxid-module-internals

OXID eShop module system helper tools

274.5k](/packages/acirtautas-oxid-module-internals)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
