PSR-7 Request and Method Utilities
We all know the standard HTTP request methods and status codes, right? Or do we?
We definitely know whether or not they should be integers or strings, and/or how string values should be normalized, right?
And our IDEs can totally autocomplete them, right?
Oh, that's not the case?
Some time ago, a few folks floated the idea of creating a utility repository related to the PSR-7 psr/http-message package, but containing some useful bits such as constants for HTTP request methods and status codes.
Six months ago, we released it... but didn't publicize it. I remembered that fact today while writing some unit tests that were utilizing the package, and thought I'd finally write it up.
The package is fig/http-message-util, and is available via Composer and Packagist:
$ composer require fig/http-message-util
It provides two interfaces:
-
Fig\Http\Message\RequestMethodInterface
, containing constants for HTTP request method values. -
Fig\Http\Message\StatusCodeInterface
, containing constants for HTTP status code values.
The constants are prefixed with METHOD_
and STATUS_
, respectively, and use
the standard names as presented in the various IETF specifications that
originally define them.
As an example, I could write middleware that looks like this:
use Fig\Http\Message\RequestMethodInterface as RequestMethod;
use Fig\Http\Message\StatusCodeInterface as StatusCode;
use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\EmptyResponse;
class RequestMethodNegotiation implements MiddlewareInterface
{
private $alwaysAllowed = [
RequestMethod::METHOD_HEAD,
RequestMethod::METHOD_OPTIONS,
];
private $map;
public function __construct(array $map)
{
$this->map = $map;
}
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
$path = $request->getUri()->getPath();
if (! isset($this->map[$path])) {
return $delegate->process($request);
}
$method = $request->getMethod();
if (in_array($method, $this->alwaysAllowed, true)) {
// Always allowed
return $delegate->process($request);
}
if (in_array($method, $this->map[$path], true)) {
// In map; proceed
return $delegate->process($request);
}
// Not allowed!
return new EmptyResponse(StatusCode::STATUS_METHOD_NOT_ALLOWED, [
'Allow' => implode(',', $this->map[$path]);
]);
}
}
The things to notice in the above are:
-
$alwaysAllowed
uses theRequestMethodInterface
constants in order to provide a list of always allowed HTTP methods; it doesn't use strings, which are prone to typos. -
When a dis-allowed method is encountered, we use a
StatusCodeInterface
constant to provide the status. This allows us to use code completion, but also signify the intent of the code. Integer values are great, but unless you have all the status codes memorized, it's often easy to forget what they mean.
The other thing to notice is that I alias the interfaces to shorter names.
We require interfaces to have the Interface
suffix in FIG, but in situations
like these, I don't particularly care that the constants are defined in an
interface; I just want to consume them. This is one of the reasons PHP
supports aliasing.
If you're not already using this package, and use PSR-7 middleware, I highly recommend checking the package out!