mehr-it / eli_dispatcher
Flexible and lightweight request dispatcher (and emitter) for PHP applications based on PSR-7 and PSR-15 standards
Requires
- php: >=7.1.0
- dflydev/fig-cookies: ^1.0
- narrowspark/http-emitter: ^1.0.0
- nyholm/psr7: ^1.0
- nyholm/psr7-server: ^0.4.1
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- guzzle/guzzle: ^3.9@dev
- phpunit/phpunit: 7.5.x-dev
- symfony/process: ^4.3@dev
This package is auto-updated.
Last update: 2024-10-21 23:49:28 UTC
README
Fast and lightweight request dispatcher (and emitter) for PHP applications using PSR-7 and PSR-15.
Basic concepts
The dispatcher can be configured with a custom PSR-15 request handler to resolve requests. Any PSR-15 middleware (or closures) may be added to the processing chain.
The request is created from globals (or passed as argument), processed by middleware, handled over to the request handler and then emitted (or returned).
Basic usage
Following example shows the basic and most common way to use the dispatcher:
(new Dispatcher())
->middleware(new SessionMiddleware())
->middleware(function($request, $next) {
// implement middleware code here
})
->middleware(function($request) {
// return response
})
->dispatch();
A dispatcher instance is created, middleware is added and the request is dispatched. You might miss a request handler in the example. In fact you don't need one if you implement a middleware which does not call the "next" but instead return a response. This means any middleware can act as request handler.
Adding middleware
If you want to add middleware for request processing, you may either pass a PSR-15 middleware
$dispatcher->middleware(new MyMiddleware())
or a callable, eg. a closure:
$dispatcher->middleware(function(ServerRequestInterface $request, RequestHandlerInterface $next) {
// this middleware does nothing, only return the
// response generated by the next handler
return $next->handle();
})
Middleware, even closures must always return a response. But it's up to the middleware if it invokes the next handler and processes it's response or generates a response without invoking any further handlers.
Request handlers
As mentioned above, you don't have to use request handlers. In fact a request handler is a middleware without the ability to be chained. So there is nothing you can't do with a middleware instead.
However the request processing chain needs a end. By default a request handler returning a 404 response without body is appended to the chain.
If you want to change this behaviour, you can set your own request handler:
$dispatcher->handler(new RequestHandler());
Passing requests
If calling the dispatch()
method without any parameters,
the request is captured from PHP's globals.
$dispatcher->dispatch();
However you may also pass a custom PSR-7 server request instance to be dispatched:
$dispatcher->dispatch($serverRequest);
Returning responses
By default the dispatcher emits the generated response. If
you don't want the response to be emitted, but only to be returned
you can set the second parameter to false
:
$response = dispatcher->dispatch(null, false);
Termination handlers
Sometimes there is some work to do after the request has been emitted. Common examples are writing log entries or persisting session data.
Performing these actions after sending the request to the client can improve response time a lot.
To register a handler to be called after request is sent,
you can use the onTerminate
method:
$dispatcher->onTerminate(function($request, $response) {
// implement your code here
});
Often middleware needs to add termination handlers. Since
middleware cannot access the current dispatcher instance on
its own, the onTerminate()
method can be called statically:
Dispatcher::onTerminate($handler, true);
Static calls are only possible while dispatching and are forwarded to the dispatcher instance currently dispatching.
Typically middleware would set the second parameter to true
, which
causes the handler to be only invoked once. This allows to reuse
the dispatcher and add termination handlers per request only.
Streaming responses
By default the dispatcher "streams" the response in chunks to
the client. You may configure the chunk size using the
sendBuffer()
method and even disable streaming by passing
send buffer size 0
:
$dispatcher->sendBuffer(1024 * 1024);
or
Dispatcher::sendBuffer(0);
When streaming is enabled, the client my request only a certain range of the response.
However you should add the accept-ranges: bytes
header
to responses, where you want to indicate this feature to
clients.
Content length
If the response body size is known, the dispatcher will
automatically add the content-length
header, if yet not
existing.
Boot scripts
If you need to configure different middleware based on the execution environment, boot scripts are very handy:
You can pass in the boot script for the current environment to the dispatcher constructor. Within the boot script you can configure the dispatcher:
// index.php
if (/* condition* /)
$bootScript = 'local.php';
else
$bootScript = 'production.php';
(new Dispatcher($bootScript))->disptach();
The boot script can access the dispatcher instance via the
$dispatcher
variable and configure it:
// local.php
$dispatcher->middleware(/* ... */);
Chains
Sometimes middleware needs to inject other middleware dynamically. You can use the Chain
class to easily create a middleware processing chain. See following example:
$dispatcher->middleware(function($request, $next) {
if (/* ... */) {
$chain = new Chain([
new MiddlewareA(),
new MiddlewareB(),
], $next);
}
else {
$chain = new Chain([
new MiddlewareC(),
function($request, $next) {
/* ... */
},
], $next);
}
return $chain->handle($request);
});