Helper classes for Expressive.
Install this library using composer:
$ composer require zendframework/zend-expressive-helpers
Zend\Expressive\Helper\UrlHelper
provides the ability to generate a URI path
based on a given route defined in the Zend\Expressive\Router\RouterInterface
.
The provided Zend\Expressive\Helper\UrlHelperMiddleware
can look for a
Zend\Expressive\Router\RouteResult
request attribute, and, if present, inject
the UrlHelper
with it; when this occurs, if the route being used to generate
a URI was also the one matched during routing, you can provide a subset of
routing parameters, and any not provided will be pulled from those matched.
To register the UrlHelperMiddleware
:
use Zend\Expressive\Helper\UrlHelperMiddleware;
use Zend\Expressive\Router\Middleware\DispatchMiddleware;
use Zend\Expressive\Router\Middleware\RouteMiddleware;
$app->pipe(RouteMiddleware::class);
$app->pipe(UrlHelperMiddleware::class);
$app->pipe(DispatchMiddleware::class);
// Or use configuration:
// [
// 'middleware_pipeline' => [
// 'routing' => [
// 'middleware' => [
// RouteMiddleware::class,
// UrlHelperMiddleware::class,
// DispatchMiddleware::class,
// ],
// 'priority' => 1,
// ],
// ],
// ]
Compose the helper in your handler (or elsewhere), and then use it to generate URI paths:
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response;
use Zend\Expressive\Helper\UrlHelper;
class FooHandler implements RequestHandlerInterface
{
private $helper;
public function __construct(UrlHelper $helper)
{
$this->helper = $helper;
}
public function handle(ServerRequestInterface $request) : ResponseInterface
{
$response = new Response();
return $response->withHeader(
'Link',
$this->helper->generate('resource', ['id' => 'sha1'])
);
}
}
You can use the methods generate()
and __invoke()
interchangeably (i.e., you
can use the helper as a function if desired). The signature is:
function (
string $routeName = null,
array $routeParams = [],
array $queryParams = [],
string $fragmentIdentifier = null,
array $options = []
) : string
Where:
$routeName
is the name of a route defined in the composed router. You may omit this argument if you want to generate the path for the currently matched request.$routeParams
is an array of substitutions to use for the provided route, with the following behavior:- If a
RouteResult
is composed in the helper, and the$routeName
matches it, the provided$params
will be merged with any matched parameters, with those provided taking precedence. - If a
RouteResult
is not composed, or if the composed result does not match the provided$routeName
, then only the$params
provided will be used for substitutions. - If no
$params
are provided, and the$routeName
matches the currently matched route, then any matched parameters found will be used. parameters found will be used. - If no
$params
are provided, and the$routeName
does not match the currently matched route, or if no route result is present, then no substitutions will be made.
- If a
Each method will raise an exception if:
- No
$routeName
is provided, and noRouteResult
is composed. - No
$routeName
is provided, aRouteResult
is composed, but that result represents a matching failure. - The given
$routeName
is not defined in the router.
If your application is running under a subdirectory, or if you are running
pipeline middleware that is intercepting on a subpath, the paths generated
by the router may not reflect the base path, and thus be invalid. To
accommodate this, the UrlHelper
supports injection of the base path; when
present, it will be prepended to the path generated by the router.
As an example, perhaps you have middleware running to intercept a language
prefix in the URL; this middleware could then inject the UrlHelper
with the
detected language, before stripping it off the request URI instance to pass on
to the router:
use Locale;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Expressive\Helper\UrlHelper;
class LocaleMiddleware implements MiddlewareInterface
{
private $helper;
public function __construct(UrlHelper $helper)
{
$this->helper = $helper;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
$uri = $request->getUri();
$path = $uri->getPath();
if (! preg_match('#^/(?P<lang>[a-z]{2})/#', $path, $matches)) {
return $handler->handle($request);
}
$lang = $matches['lang'];
Locale::setDefault($lang);
$this->helper->setBasePath($lang);
return $handler->handle(
$request->withUri(
$uri->withPath(substr($path, 3))
)
);
}
}
(Note: if the base path injected is not prefixed with /
, the helper will add
the slash.)
Paths generated by the UriHelper
from this point forward will have the
detected language prefix.
Zend\Expressive\Helper\ServerUrlHelper
provides the ability to generate a full
URI by passing only the path to the helper; it will then use that path with the
current Psr\Http\Message\UriInterface
instance provided to it in order to
generate a fully qualified URI.
In order to use the helper, you will need to inject it with the current
UriInterface
from the request instance. To automate this, we provide
Zend\Expressive\Helper\ServerUrlMiddleware
, which composes a ServerUrl
instance, and, when invoked, injects it with the URI instance.
As such, you will need to:
- Register the
ServerUrlHelper
as a service in your container. - Register the
ServerUrlMiddleware
as a service in your container. - Register the
ServerUrlMiddleware
early in your middleware pipeline.
To register the ServerUrlMiddleware
in your middleware pipeline:
use Zend\Expressive\Helper\ServerUrlMiddleware;
use Zend\Expressive\Router\Middleware\DispatchMiddleware;
use Zend\Expressive\Router\Middleware\RouteMiddleware;
// Do this early, before piping other middleware or routes:
$app->pipe(ServerUrlMiddleware::class);
/* ... */
$app->pipe(RouteMiddleware::class);
$app->pipe(DispatchMiddleware::class);
// Or use configuration:
// [
// 'middleware_pipeline' => [
// [
// 'middleware' => ServerUrlMiddleware::class,
// 'priority' => PHP_INT_MAX,
// ],
// ],
// ]
Compose the helper in your hanlder (or elsewhere), and then use it to generate URI paths:
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response;
use Zend\Expressive\Helper\ServerUrlHelper;
class FooHandler implements RequestHandlerInterface
{
private $helper;
public function __construct(ServerUrlHelper $helper)
{
$this->helper = $helper;
}
public function handle(ServerRequestInterface $request) : ResponseInterface
{
$response = new Response();
return $response->withHeader(
'Link',
$this->helper->generate() . '; rel="self"'
);
}
}
You can use the methods generate()
and __invoke()
interchangeably (i.e., you
can use the helper as a function if desired). The signature is:
function (string $path = null) : string
Where:
$path
, when provided, can be a string path to use to generate a URI.
One aspect of PSR-7 is that it allows you to parse the raw request body, and
then create a new instance with the results of parsing that later processes can
fetch via getParsedBody()
. It does not provide any actual facilities for
parsing, which means you must write middleware to do so.
This package provides such facilities via Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware
.
By default, this middleware will detect the following content types:
application/x-www-form-urlencoded
(standard web-based forms, without file uploads)application/json
,application/*+json
(JSON payloads)
You can register it manually:
use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;
$app->pipe(BodyParamsMiddleware::class);
or, if using Expressive, as pipeline middleware:
// config/autoload/middleware-pipeline.global.php
use Zend\Expressive\Helper;
return [
'dependencies' => [
'invokables' => [
Helper\BodyParams\BodyParamsMiddleware::class => Helper\BodyParams\BodyParamsMiddleware::class,
/* ... */
],
'factories' => [
/* ... */
],
],
'middleware_pipeline' => [
[
'middleware' => Helper\BodyParams\BodyParamsMiddleware::class,
'priority' => 1000,
],
'routing' => [
/* ... */
],
],
];
If you want to intercept and parse other payload types, you can add strategies
to the middleware. Strategies implement Zend\Expressive\Helper\BodyParams\StrategyInterface
:
namespace Zend\Expressive\Helper\BodyParams;
use Psr\Http\Message\ServerRequestInterface;
interface StrategyInterface
{
/**
* Match the content type to the strategy criteria.
*
* @param string $contentType
* @return bool Whether or not the strategy matches.
*/
public function match($contentType);
/**
* Parse the body content and return a new response.
*
* @param ServerRequestInterface $request
* @return ServerRequestInterface
*/
public function parse(ServerRequestInterface $request);
}
You then register them with the middleware using the addStrategy()
method:
$bodyParams->addStrategy(new MyCustomBodyParamsStrategy());
To automate the registration, we recommend writing a factory for the
BodyParamsMiddleware
, and replacing the invokables
registration with a
registration in the factories
section of the middleware-pipeline.config.php
file:
use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;
class MyCustomBodyParamsStrategyFactory
{
public function __invoke($container)
{
$bodyParams = new BodyParamsMiddleware();
$bodyParams->addStrategy(new MyCustomBodyParamsStrategy());
return $bodyParams;
}
}
// In config/autoload/middleware-pipeline.config.php:
use Zend\Expressive\Helper;
return [
'dependencies' => [
'invokables' => [
// Remove this line:
Helper\BodyParams\BodyParamsMiddleware::class => Helper\BodyParams\BodyParamsMiddleware::class,
/* ... */
],
'factories' => [
// Add this line:
Helper\BodyParams\BodyParamsMiddleware::class => MyCustomBodyParamsStrategy::class,
/* ... */
],
],
];
If you do not want to use the default strategies (form data and JSON), you can
clear them from the middleware using clearStrategies()
:
$bodyParamsMiddleware->clearStrategies();
Note: if you do this, all strategies will be removed! As such, we recommend doing this only immediately before registering any custom strategies you might be using.
In some cases, you may want to include an explicit Content-Length
response
header, without having to inject it manually. To facilitate this, we provide
Zend\Expressive\Helper\ContentLengthMiddleware
.
This middleware delegates the request, and operates on the returned response. It
will return a new response with the Content-Length
header injected under the
following conditions:
- No
Content-Length
header is already present AND - the body size is non-null.
To register it in your application, you will need to do two things: register the middleware with the container, and register the middleware in either your application pipeline, or within routed middleware.
To add it to your container, add the following configuration:
// In a `config/autoload/*.global.php` file, or a `ConfigProvider` class:
use Zend\Expressive\Helper;
return [
'dependencies' => [
'invokables' => [
Helper\ContentLengthMiddleware::class => Helper\ContentLengthMiddleware::class,
],
],
];
To register it as pipeline middleware to execute on any request:
// In `config/pipeline.php`:
use Zend\Expressive\Helper;
$app->pipe(Helper\ContentLengthMiddleware::class);
To register it within a routed middleware pipeline:
// In `config/routes.php`:
use Zend\Expressive\Helper;
$app->get('/download/tarball', [
Helper\ContentLengthMiddleware::class,
Download\Tarball::class,
], 'download-tar');
See the zend-expressive documentation tree, or browse online at https://docs.zendframework.com/zend-expressive/features/helpers/intro/