appertly / ducts
A PSR-7 compliant middleware dispatcher for Hack/HHVM
Installs: 165
Dependents: 1
Suggesters: 0
Security: 0
Stars: 1
Watchers: 2
Forks: 0
Open Issues: 0
Language:Hack
Requires
- hhvm: >=3.12.0
- psr/http-message: ^1.0
Requires (Dev)
This package is not auto-updated.
Last update: 2024-12-21 20:36:05 UTC
README
A PSR-7 compliant middleware dispatcher for Hack/HHVM.
It's the same idea as Connect, Relay, Stratigility, Middleman, etc., but in strict mode Hack.
Installation
You can install this library using Composer:
$ composer require appertly/ducts
- The master branch (version 0.x) of this project requires HHVM 3.12 and has no dependencies.
Compliance
Releases of this library will conform to Semantic Versioning.
Our code is intended to comply with PSR-1, PSR-2, and PSR-4. If you find any issues related to standards compliance, please send a pull request!
Layers and Dispatching
The idea here is that you supply a list of functions which:
- Accept a request, a response, and a next function
- Possibly modify the request and response
- Possibly invoke the next function, which returns a response
- Possibly modify the returned response
- Return the response
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; function foobar(Request $req, Response $res, (function (Request,Response): Response) $next): Response { // 2. possibly modify the request and response // 3. invoke next $res = $next($req, $res); // 4. possibly modify response // 5. return $res; }
Layers end up interacting like this:
- → First layer
- → Second layer
- → Third layer
- ← Third layer
- ← Second layer
- → Second layer
- ← First layer
Dispatch a Request
use Ducts\Runner; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; /** * @var \Psr\Http\Message\ServerRequestInterface $request The PSR-7 HTTP request * @var \Psr\Http\Message\ResponseInterface $response The PSR-7 HTTP response */ // any Traversable will do. Use an array, Vector, etc. $runner = new Runner([ // you can use closures function(Request $req, Response $res, (function (Request,Response): Response) $next): Response { $req = $req->withAttribute('foo', 'bar'); return $next($req, $res); }, // or lambdas ($req, $res, $next) ==> $next($req, $res)->withHeader('X-Ducts', 'Yes') // or objects with an __invoke method new MyInvokableThing(), // or Hack-style callables fun('myLayer'), class_meth('MyLayer', 'staticMethod'), inst_meth($myObject, 'layer') ]); $response = $runner->run($request, $response); // Runner::run can be called multiple times if needed $anotherResponse = $runner->run($request, $response);
You can also make use of the ResolvingRunner
if you'd like to look up the layer callable.
// a function to look up names $resolver = function(mixed $name): (function (Request,Response,(function(Request,Response): Response)): Response) { // look up your callable thing in a dependency injection container $container = MyClass::getSomeContainer(); return $container->get($name); }; // Items in this traversable can be a combination of functions or items to resolve $runner = new ResolvingRunner([ ($req, $res, $next) ==> $next($req, $res)->withHeader('X-Ducts', 'Yes'), 'MyClassName', 'AnotherClass' ], $resolver);
A Runner as a Layer
You can use a Runner
as a layer by calling $runner->layer()
!
For example, you declare Runner
$bar
to be the second layer of three in Runner
$foo
.
If $bar
has three layers, it will participate in $foo
like this:
- →
$foo
layer 1- →
$bar
layer 1- →
$bar
layer 2- →
$bar
layer 3- →
$foo
layer 3 - ←
$foo
layer 3
- →
- ←
$bar
layer 3
- →
- ←
$bar
layer 2
- →
- ←
$bar
layer 1
- →
- ←
$foo
layer 1