LICENSE 0000666 00000002067 13436756072 0005577 0 ustar 00 The MIT License (MIT)
Copyright (c) 2015 Oscar Otero
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
README.md 0000666 00000046226 13436756072 0006056 0 ustar 00 # psr7-middlewares
[![Build Status](https://travis-ci.org/oscarotero/psr7-middlewares.svg)](https://travis-ci.org/oscarotero/psr7-middlewares)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/oscarotero/psr7-middlewares/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/oscarotero/psr7-middlewares/?branch=master)
Collection of [PSR-7](http://www.php-fig.org/psr/psr-7/) middlewares.
It is installable and autoloadable via Composer as oscarotero/psr7-middlewares.
## Requirements
* PHP >= 5.5
* A PSR-7 HTTP Message implementation, for example [zend-diactoros](https://github.com/zendframework/zend-diactoros)
* A PSR-7 middleware dispatcher. For example [Relay](https://github.com/relayphp/Relay.Relay) or any other compatible with the following signature:
```php
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
function (RequestInterface $request, ResponseInterface $response, callable $next) {
// ...
}
```
## Usage example:
```php
use Psr7Middlewares\Middleware;
use Relay\RelayBuilder;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\Stream;
//Set a stream factory used by some middlewares
//(Required only if Zend\Diactoros\Stream is not detected)
Middleware::setStreamFactory(function ($file, $mode) {
return new Stream($file, $mode);
});
//Create a relay dispatcher and add some middlewares:
$relay = new RelayBuilder();
$dispatcher = $relay->newInstance([
//Calculate the response time
Middleware::responseTime(),
//Add an Uuid to request
Middleware::uuid(),
//Handle errors
Middleware::errorHandler('error_handler_function')->catchExceptions(true),
//Override the method using X-Http-Method-Override header
Middleware::methodOverride(),
//Block search engines robots indexing
Middleware::robots(),
//Parse the request payload
Middleware::payload(),
//Remove the path prefix
Middleware::basePath('/my-site/web'),
//Remove the trailing slash
Middleware::trailingSlash(),
//Digest authentication
Middleware::digestAuthentication(['username' => 'password']),
//Get the client ip
Middleware::clientIp(),
//Allow only some ips
Middleware::firewall(['127.0.0.*']),
//Detects the user preferred language
Middleware::languageNegotiator(['gl', 'es', 'en']),
//Detects the format
Middleware::formatNegotiator(),
//Adds the php debug bar
Middleware::debugBar($app->get('debugbar')),
//Execute fast route
Middleware::fastRoute($app->get('dispatcher')),
//Minify the result
Middleware::minify()
//Saves the response in a file
Middleware::saveResponse('app/public')
]);
$response = $dispatcher(ServerRequestFactory::fromGlobals(), new Response());
```
## Available middlewares
* [AuraRouter](#aurarouter)
* [AuraSession](#aurasession)
* [BasePath](#basepath)
* [BasicAuthentication](#basicauthentication)
* [Cache](#cache)
* [ClientIp](#clientip)
* [Cors](#cors)
* [DebugBar](#debugbar)
* [DigestAuthentication](#digestauthentication)
* [ErrorHandler](#errorhandler)
* [FastRoute](#fastroute)
* [Firewall](#firewall)
* [FormatNegotiation](#formatnegotiation)
* [LanguageNegotiation](#languagenegotiation)
* [MethodOverride](#methodoverride)
* [Minify](#minify)
* [Payload](#payload)
* [ReadResponse](#readresponse)
* [ResponseTime](#responseTime)
* [Robots](#robots)
* [SaveResponse](#saveresponse)
* [Shutdown](#shutdown)
* [TrailingSlash](#trailingslash)
* [Uuid](#uuid)
* [When](#when)
### AuraRouter
To use [Aura.Router](https://github.com/auraphp/Aura.Router) as a middleware. You need the 3.x version, compatible with psr-7:
```php
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\AuraRouter;
use Aura\Router\RouterContainer;
//Create the router
$routerContainer = new RouterContainer();
$map = $routerContainer->getMap();
$map->get('hello', '/hello/{name}', function ($request, $response, $myApp) {
//The route parameters are stored as attributes
$name = $request->getAttribute('name');
//You can get also the route instance
$route = AuraRouter::getRoute($request);
//Write directly in the response's body
$response->getBody()->write('Hello '.$name);
//or echo the output (it will be captured and writted into body)
echo 'Hello world';
//or return a string
return 'Hello world';
//or return a new response
return $response->withStatus(200);
});
//Add to the dispatcher
$dispatcher = $relay->getInstance([
Middleware::AuraRouter()
->router($routerContainer) //Instance of Aura\Router\RouterContainer
->arguments($myApp) //(optional) append more arguments to the controller
]);
```
### AuraSession
Creates a new [Aura.Session](https://github.com/auraphp/Aura.Session) instance with the request.
```php
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\AuraSession;
$dispatcher = $relay->getInstance([
Middleware::AuraSession(),
->factory($sessionFactory) //(optional) Intance of Aura\Session\SessionFactory
->name('my-session-name'), //(optional) custom session name
function ($request, $reponse, $next) {
//Get the session instance
$session = AuraSession::getSession($request);
}
]);
```
### BasePath
Strip off the prefix from the uri path of the request. This is useful to combine with routers if the root of the website is in a subdirectory. For example, if the root of your website is `/web/public`, a request with the uri `/web/public/post/34` will be converted to `/post/34`.
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::BasePath('/web/public'),
]);
```
### BasicAuthentication
Implements the [basic http authentication](http://php.net/manual/en/features.http-auth.php). You have to provide an array with all users and password:
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::BasicAuthentication()
->users([
'username1' => 'password1',
'username2' => 'password2'
])
->realm('My realm') //(optional) change the realm value
]);
```
### Cache
To save and reuse responses based in the Cache-Control: max-age directive and Expires header. You need a cache library compatible with psr-6
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::Cache()
->cache(new Psr6CachePool()) //the psr-6 cache implementation
function($request, $response, $next) {
//Cache the response 1 hour
return $response->withHeader('Cache-Control', 'max-age=3600');
}
]);
```
### ClientIp
Detects the client ip(s).
```php
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\ClientIp;
$dispatcher = $relay->getInstance([
Middleware::ClientIp()
->headers([
'Client-Ip',
'X-Forwarded-For',
'X-Forwarded'
]), //(optional) to change the trusted headers
function ($request, $response, $next) {
//Get the user ip
$ip = ClientIp::getIp($request);
//Get all ips found in the headers
$all_ips = ClientIp::getIps($request);
return $next($request, $response);
}
]);
```
### Cors
To use the [neomerx/cors-psr7](https://github.com/neomerx/cors-psr7) library:
```php
use Neomerx\Cors\Strategies\Settings
$relay = new RelayBuilder();
$settings = (new Settings())
->setServerOrigin([
'scheme' => 'http',
'host' => 'example.com',
'port' => '123',
]);
$dispatcher = $relay->getInstance([
Middleware::Cors()
->settings($settings)
]);
```
### DebugBar
Inserts the [PHP debug bar](http://phpdebugbar.com/) in the html body. This middleware requires `Middleware::formatNegotiator` executed before, to insert the debug bar only in Html responses.
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::formatNegotiator(),
Middleware::DebugBar()
->debugBar(new DebugBar\StandardDebugBar())
]);
```
### DigestAuthentication
Implements the [digest http authentication](http://php.net/manual/en/features.http-auth.php). You have to provide an array with the users and password:
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::DigestAuthentication()
->users([
'username1' => 'password1',
'username2' => 'password2'
])
->realm('My realm') //(optional) custom realm value
->nonce(uniqid()) //(optional) custom nonce value
]);
```
### ErrorHandler
Executes a handler if the response returned by the next middlewares has any error (status code 400-599). You can catch also the exceptions throwed or even use [whoops](https://github.com/filp/whoops) as error handler.
```php
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\ErrorHandler;
function errorHandler($request, $response, $myApp) {
switch ($response->getStatusCode()) {
case 404:
return 'Page not found';
case 500:
//you can get the exception catched
$exception = ErrorHandler::getException($request);
return 'Server error: '.$exception->getMessage();
default:
return 'There was an error'
}
}
$whoops = new Whoops\Run();
$dispatcher = $relay->getInstance([
Middleware::ErrorHandler()
->handler('errorHandler') //The error handler
->arguments($myApp) //(optional) extra arguments to the handler
->whoops($whoops) //(optional) provide a whoops instance to capture errors and exceptions
->catchExceptions() //(optional) to catch exceptions if you don't use an external library for that
]);
```
### FastRoute
To use [FastRoute](https://github.com/nikic/FastRoute) as a middleware.
```php
use Psr7Middlewares\Middleware;
$router = FastRoute\simpleDispatcher(function (FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/blog/{id:[0-9]+}', function ($request, $response, $app) {
return 'This is the post number'.$request->getAttribute('id');
});
});
$dispatcher = $relay->getInstance([
Middleware::FastRoute()
->router($router) //Instance of FastRoute\Dispatcher
->argument($myApp) //(optional) arguments appended to the controller
]);
```
### Firewall
Uses [M6Web/Firewall](https://github.com/M6Web/Firewall) to provide a IP filtering. This middleware deppends of **ClientIp** (to extract the ips from the headers).
[See the ip formats allowed](https://github.com/M6Web/Firewall#entries-formats) for trusted/untrusted options:
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
//needed to capture the user ips before
Middleware::ClientIp(),
//set the firewall
Middleware::Firewall()
->trusted(['123.0.0.*']) //(optional) ips allowed
->untrusted(['123.0.0.1']) //(optional) ips not allowed
]);
```
### FormatNegotiation
Uses [willdurand/Negotiation (2.x)](https://github.com/willdurand/Negotiation) to detect and negotiate the format of the document using the url extension and/or the `Accept` http header. It also adds the `Content-Type` header to the response if it's missing.
```php
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\FormatNegotiator;
$dispatcher = $relay->getInstance([
Middleware::FormatNegotiator()
->defaultFormat('html') //(optional) default format if it's unable to detect. (by default is "html")
->addFormat('pdf', ['application/pdf', 'application/x-download']) //(optional) add new formats and mimetypes
},
function ($request, $response, $next) {
//get the format (for example: html)
$format = FormatNegotiator::getFormat($request);
return $next($request, $response);
}
]);
```
### LanguageNegotiation
Uses [willdurand/Negotiation](https://github.com/willdurand/Negotiation) to detect and negotiate the client language. You must provide an array with all available languages:
```php
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\LanguageNegotiator;
$dispatcher = $relay->getInstance([
Middleware::LanguageNegotiator()
->languages(['gl', 'en', 'es']), //Available languages
function ($request, $response, $next) {
//Get the preferred language
$language = LanguageNegotiator::getLanguage($request);
return $next($request, $response);
}
]);
```
### MethodOverride
Overrides the request method using the `X-Http-Method-Override` header. This is useful for clients unable to send other methods than GET and POST:
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::MethodOverride()
->get(['HEAD', 'CONNECT', 'TRACE', 'OPTIONS']), //(optional) to customize the allowed GET overrided methods
->post(['PATCH', 'PUT', 'DELETE', 'COPY', 'LOCK', 'UNLOCK']), //(optional) to customize the allowed POST overrided methods
]);
```
### Minify
Uses [mrclay/minify](https://github.com/mrclay/minify) to minify the html, css and js code from the responses.
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
//needed to get the format of the response
Middleware::formatNegotiator(),
Middleware::Minify()
->forCache(true) //(optional) only minify cacheable responses
->inlineCss(false) //(optional) enable/disable inline css minification
->inlineJs(false) //(optional) enable/disable inline js minification
]);
```
### Payload
Parses the body of the request if it's not parsed and the method is POST, PUT or DELETE. It has support for json, csv and url encoded format.
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::Payload()
->associative(true) //(optional) To generate associative arrays with json objects
function ($request, $response, $next) {
//Get the parsed body
$content = $request->getParsedBody();
return $next($request, $response);
}
]);
```
### ReadResponse
Read the response content from a file. It's the opposite of [SaveResponse](#saveresponse)
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::ReadResponse()
->storage('path/to/document/root') //Path where the files are stored
->basePath('public') //(optional) basepath ignored from the request uri
]);
```
### ResponseTime
Calculates the response time (in miliseconds) and saves it into `X-Response-Time` header:
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::ResponseTime()
]);
```
### Robots
Disables the robots of the search engines for non-production environment. Adds automatically the header `X-Robots-Tag: noindex, nofollow, noarchive` in all responses and returns a default body for `/robots.txt` request.
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::Robots()
]);
```
### SaveResponse
Saves the response content into a file if all of the following conditions are met:
* The method is `GET`
* The status code is `200`
* The `Cache-Control` header does not contain `no-cache` value
* The request has not query parameters.
This is useful for cache purposes
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::SaveResponse()
->storage('path/to/document/root') //Path directory where save the responses
->basePath('public') //(optional) basepath ignored from the request uri
]);
```
### Shutdown
Useful to display a 503 maintenance page. You need to specify a handler.
```php
use Psr7Middlewares\Middleware;
function shutdownHandler ($request, $response, $app) {
$response->getBody()->write('Service unavailable');
}
$dispatcher = $relay->getInstance([
Middleware::Shutdown()
->handler('shutdownHandler') //Callable that generate the response
->arguments($app) //(optional) to add extra arguments to the handler
]);
```
### TrailingSlash
Removes (or adds) the trailing slash of the path. For example, `/post/23/` will be converted to `/post/23`. If the path is `/` it won't be converted. Useful if you have problems with the router.
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::TrailingSlash()
->addSlash(true) //(optional) to add the trailing slash instead remove
->redirect(301) //(optional) to return a 301 (seo friendly) or 302 response to the new path
->basePath('public') //(optional) basepath
]);
```
### Uuid
Uses [ramsey/uuid](https://github.com/ramsey/uuid) to generate an Uuid (Universally Unique Identifiers) for each request (compatible with [RFC 4122](http://tools.ietf.org/html/rfc4122) versions 1, 3, 4 and 5). It's usefull for debugging purposes.
```php
use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\Uuid;
$dispatcher = $relay->getInstance([
Middleware::Uuid()
->version(4) //(optional) version of the identifier (1 by default). Versions 3 and 5 need more arguments (see https://github.com/ramsey/uuid#examples)
->header(false), //(optional) Name of the header to store the identifier (X-Uuid by default). Set false to don't save header
function ($request, $response, $next) {
//Get the X-Uuid header
$id = $request->getHeaderLine('X-Uuid');
//Get the Uuid instance
$uuid = Uuid::getUuid($request);
echo $uuid->toString();
return $next($request, $response);
}
]);
```
### When
Execute a middleware only if a condition is evaluated as true. This is useful to add middleware only under some circunstances or environments.
```php
use Psr7Middlewares\Middleware;
$dispatcher = $relay->getInstance([
Middleware::When()
->condition(getenv('ENV') === 'production') //The condition to be evaluated
->middleware(Middleware::minify()), //The middleware to be executed when the condition is true
//You can use also callables:
Middleware::when()
->condition(function ($request, $response) {
return $request->hasHeader('X-Foo')
})
->middleware(function ($request, $response, $next) {
//your code
return $next($request, $response);
})
]);
```
## Combine with container interop
If you're using any container compatible with the [Container Interoperability Project](https://github.com/container-interop/container-interop) you can use it to provide instances to some middlewares (routers, debugbar, psr6 cache, etc). To do that, there's the method `->from($container, $id)` available in the following middlewares:
```php
$container = new MyContainerInterop();
$dispatcher = $relay->getInstance([
Middleware::Cache()->from($container, 'cache'),
Middleware::Cors()->from($container, 'cors-settings'),
Middleware::DebugBar()->from($container, 'debug-bar'),
Middleware::AuraSession()->from($container, 'session-factory'),
Middleware::FastRouter()->from($container, 'fast-router'),
]);
```
By using containers instead creating and passing the instances directly, these instances will be created (for example `$container->get('fast-router')`) only if the middleware is executed.
## Contribution
New middlewares are appreciated. Just create a pull request.
src/Middleware.php 0000666 00000005617 13436756072 0010153 0 ustar 00 newInstanceArgs($args);
}
return new $class();
}
throw new RuntimeException("The middleware {$name} does not exits");
}
/**
* Store an attribute in the request.
*
* @param ServerRequestInterface $request
* @param string $name
* @param mixed $value
*
* @return ServerRequestInterface
*/
public static function setAttribute(ServerRequestInterface $request, $name, $value)
{
$attributes = $request->getAttribute(self::KEY, []);
$attributes[$name] = $value;
return $request->withAttribute(self::KEY, $attributes);
}
/**
* Retrieves an attribute from the request.
*
* @param ServerRequestInterface $request
* @param string $name
*
* @return mixed
*/
public static function getAttribute(ServerRequestInterface $request, $name)
{
$attributes = $request->getAttribute(self::KEY);
if (isset($attributes[$name])) {
return $attributes[$name];
}
}
/**
* Check whether an attribute exists.
*
* @param ServerRequestInterface $request
* @param string $name
*
* @return bool
*/
public static function hasAttribute(ServerRequestInterface $request, $name)
{
$attributes = $request->getAttribute(self::KEY);
if (empty($attributes)) {
return false;
}
return array_key_exists($name, $attributes);
}
}
src/Utils/FileTrait.php 0000666 00000002164 13436756072 0011053 0 ustar 00 getBasePath($request->getUri()->getPath());
$parts = pathinfo($path);
$path = '/'.(isset($parts['dirname']) ? $parts['dirname'] : '');
$filename = isset($parts['basename']) ? $parts['basename'] : '';
//if it's a directory, append "/index.html"
if (empty($parts['extension'])) {
if ($path === '/') {
$path .= $filename;
} else {
$path .= "/{$filename}";
}
$extension = strtolower(pathinfo($request->getUri()->getPath(), PATHINFO_EXTENSION)) ?: 'html';
$filename = "index.{$extension}";
}
return "{$this->storage}{$path}/{$filename}";
}
}
src/Utils/ContainerTrait.php 0000666 00000002671 13436756072 0012121 0 ustar 00 container = $container;
$this->containerId = $id;
return $this;
}
/**
* Returns the service from the container.
*
* @param string $type
* @param bool $required
*
* @return null|mixed
*/
protected function getFromContainer($type = null, $required = true)
{
if (isset($this->container)) {
$item = $this->container->get($this->containerId);
if ($type !== null && !is_a($item, $type)) {
throw new InvalidArgumentException("Invalid argument, it's not of type '{$type}'");
}
return $item;
}
if ($required) {
$class = get_class($this);
throw new RuntimeException("Missing required '{$type}' in the middleware '{$class}'");
}
}
}
src/Utils/ArgumentsTrait.php 0000666 00000000557 13436756072 0012145 0 ustar 00 arguments = func_get_args();
return $this;
}
}
src/Utils/StorageTrait.php 0000666 00000001061 13436756072 0011573 0 ustar 00 storage($storage);
}
/**
* Configure the storage.
*
* @param string $storage
*
* @return self
*/
public function storage($storage)
{
$this->storage = $storage;
return $this;
}
}
src/Utils/AuthenticationTrait.php 0000666 00000001604 13436756072 0013151 0 ustar 00 password]
*/
public function __construct(array $users = null)
{
if ($users !== null) {
$this->users($users);
}
}
/**
* Configure the users and passwords.
*
* @param array $users [username => password]
*
* @return self
*/
public function users(array $users)
{
$this->users = $users;
return $this;
}
/**
* Set the realm value.
*
* @param string $realm
*
* @return self
*/
public function realm($realm)
{
$this->realm = $realm;
return $this;
}
}
src/Utils/RouterTrait.php 0000666 00000005234 13436756072 0011455 0 ustar 00 getBody();
if ($return !== '' && $body->isWritable()) {
$body->write($return);
}
return $response;
} catch (\Exception $exception) {
throw $exception;
} finally {
if (ob_get_level() > 0) {
ob_end_clean();
}
}
}
/**
* Resolves the target of the route and returns a callable.
*
* @param mixed $target
* @param array $construct_args
*
* @throws RuntimeException If the target is not callable
*
* @return callable
*/
protected static function getCallable($target, array $construct_args)
{
if (is_string($target)) {
//is a static function
if (function_exists($target)) {
return $target;
}
//is a class "classname::method"
if (strpos($target, '::') === false) {
$class = $target;
$method = '__invoke';
} else {
list($class, $method) = explode('::', $target, 2);
}
if (!class_exists($class)) {
throw new RuntimeException("The class {$class} does not exists");
}
$class = new \ReflectionClass($class);
$instance = $class->hasMethod('__construct') ? $class->newInstanceArgs($construct_args) : $class->newInstance();
$target = [$instance, $method];
}
if (is_callable($target)) {
return $target;
}
throw new RuntimeException('The route target is not callable');
}
}
src/Utils/RedirectTrait.php 0000666 00000002403 13436756072 0011731 0 ustar 00 redirectStatus = $redirectStatus;
return $this;
}
/**
* Returns a redirect response.
*
* @param int $redirectStatus
* @param UriInterface $uri
* @param ResponseInterface $response
*/
protected static function getRedirectResponse($redirectStatus, UriInterface $uri, ResponseInterface $response)
{
return $response
->withStatus($redirectStatus)
->withHeader('Location', (string) $uri)
->withBody(Middleware::createStream());
}
}
src/Utils/CacheTrait.php 0000666 00000003063 13436756072 0011176 0 ustar 00 getMethod() !== 'GET') {
return false;
}
if ($response->getStatusCode() !== 200) {
return false;
}
//Check http headers
$cache = static::parseCacheControl($response->getHeaderLine('Cache-Control'));
if (in_array('no-cache', $cache) || in_array('no-store', $cache) || in_array('private', $cache)) {
return false;
}
return true;
}
/**
* Parses and returns the cache-control header values.
*
* @param string $header
*
* @return array
*/
protected static function parseCacheControl($header)
{
if (empty($header)) {
return [];
}
$cache = [];
foreach (array_map('trim', explode(',', strtolower($header))) as $part) {
if (strpos($part, '=') === false) {
$cache[$part] = true;
} else {
$part = array_map('trim', explode('=', $part, 2));
$cache[$part[0]] = $part[1];
}
}
return $cache;
}
}
src/Utils/BasePathTrait.php 0000666 00000001356 13436756072 0011665 0 ustar 00 basePath = $basePath;
return $this;
}
/**
* Removes the basepath from a path.
*
* @param string $path
*
* @return string
*/
protected function getBasePath($path)
{
if (!empty($this->basePath) && strpos($path, $this->basePath) === 0) {
return substr($path, strlen($this->basePath)) ?: '';
}
return $path;
}
}
src/Middleware/Uuid.php 0000666 00000005124 13436756072 0011052 0 ustar 00 version = func_get_args();
return $this;
}
/**
* Set whether the Uuid is stored in the header.
* Set false to do not store.
*
* @param false|string $header
*
* @return self
*/
public function header($header)
{
$this->header = $header;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$uuid = $this->generateUuid();
$request = Middleware::setAttribute($request, self::KEY, $uuid);
if (!empty($this->header)) {
$request = $request->withHeader($this->header, (string) $uuid);
}
return $next($request, $response);
}
/**
* Generate the uuid with the current configuration.
*
* @return string
*/
protected function generateUuid()
{
$args = $this->version;
$fn = 'uuid'.array_shift($args);
return call_user_func_array('Rhumsaa\Uuid\Uuid::'.$fn, $args);
}
}
src/Middleware/TrailingSlash.php 0000666 00000003725 13436756072 0012715 0 ustar 00 addSlash($addSlash);
}
/**
* Configure whether the path should be added or removed.
*
* @param bool $addSlash
*
* @return self
*/
public function addSlash($addSlash)
{
$this->addSlash = (boolean) $addSlash;
return $this;
}
/**
* Execute the middleware.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
$uri = $request->getUri();
$path = $uri->getPath();
if ($this->addSlash) {
if (strlen($path) > 1 && substr($path, -1) !== '/' && !pathinfo($path, PATHINFO_EXTENSION)) {
$path .= '/';
}
} else {
if (strlen($path) > 1 && substr($path, -1) === '/') {
$path = substr($path, 0, -1);
}
}
//Ensure the path has one "/"
if (empty($path) || $path === $this->basePath) {
$path .= '/';
}
//redirect
if (is_int($this->redirectStatus) && ($uri->getPath() !== $path)) {
return self::getRedirectResponse($this->redirectStatus, $uri->withPath($path), $response);
}
return $next($request->withUri($uri->withPath($path)), $response);
}
}
src/Middleware/MethodOverride.php 0000666 00000005035 13436756072 0013065 0 ustar 00 get = $methods;
return $this;
}
/**
* Set allowed method for POST.
*
* @return self
*/
public function post(array $methods)
{
$this->post = $methods;
return $this;
}
/**
* Execute the middleware.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
$method = $this->getOverrideMethod($request);
if (!empty($method)) {
$allowed = $this->getAllowedOverrideMethods($request);
if (!empty($allowed)) {
if (in_array($method, $allowed)) {
$request = $request->withMethod($method);
} else {
return $response->withStatus(405);
}
}
}
return $next($request, $response);
}
/**
* Returns the override method.
*
* @param RequestInterface $request
*
* @return string|null
*/
protected function getOverrideMethod(RequestInterface $request)
{
$method = $request->getHeaderLine(self::HEADER);
if (!empty($method) && ($method !== $request->getMethod())) {
return strtoupper($method);
}
}
/**
* Returns the allowed override methods.
*
* @param RequestInterface $request
*
* @return array
*/
protected function getAllowedOverrideMethods(RequestInterface $request)
{
switch ($request->getMethod()) {
case 'GET':
return $this->get;
case 'POST':
return $this->post;
default:
return [];
}
}
}
src/Middleware/Cors.php 0000666 00000005120 13436756072 0011046 0 ustar 00 settings($settings);
}
}
/**
* Set the settings.
*
* @param SettingsStrategyInterface $settings
*
* @return self
*/
public function settings(SettingsStrategyInterface $settings)
{
$this->settings = $settings;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$settings = $this->settings ?: $this->getFromContainer(SettingsStrategyInterface::CLASS);
$cors = Analyzer::instance($settings)->analyze($request);
switch ($cors->getRequestType()) {
case AnalysisResultInterface::ERR_NO_HOST_HEADER:
case AnalysisResultInterface::ERR_ORIGIN_NOT_ALLOWED:
case AnalysisResultInterface::ERR_METHOD_NOT_SUPPORTED:
case AnalysisResultInterface::ERR_HEADERS_NOT_SUPPORTED:
return $response->withStatus(403);
case AnalysisResultInterface::TYPE_REQUEST_OUT_OF_CORS_SCOPE:
return $next($request, $response);
case AnalysisResultInterface::TYPE_PRE_FLIGHT_REQUEST:
foreach ($cors->getResponseHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
return $response->withStatus(200);
default:
$response = $next($request, $response);
foreach ($cors->getResponseHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
return $response;
}
}
}
src/Middleware/ReadResponse.php 0000666 00000001645 13436756072 0012542 0 ustar 00 getMethod() !== 'GET') {
return $response->withStatus(405);
}
$file = $this->getFilename($request);
if (!is_file($file)) {
return $response->withStatus(404);
}
return $next($request, $response->withBody(Middleware::createStream($file)));
}
}
src/Middleware/Shutdown.php 0000666 00000002503 13436756072 0011755 0 ustar 00 handler($handler);
}
}
/**
* Set the shudown handler.
*
* @param string|callable $handler
*
* @return self
*/
public function handler($handler)
{
$this->handler = $handler;
return $this;
}
/**
* Execute the middleware.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
$response = self::executeTarget($this->handler, $this->arguments, $request, $response);
return $response->withStatus(503);
}
}
src/Middleware/BasePath.php 0000666 00000001772 13436756072 0011640 0 ustar 00 basePath($basePath);
}
}
/**
* Execute the middleware.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
$uri = $request->getUri();
$path = $this->getBasePath($uri->getPath());
$request = $request->withUri($uri->withPath($path));
return $next($request, $response);
}
}
src/Middleware/Firewall.php 0000666 00000004157 13436756072 0011716 0 ustar 00 trusted($trusted);
}
}
/**
* Set trusted ips.
*
* @return self
*/
public function trusted(array $trusted)
{
$this->trusted = $trusted;
return $this;
}
/**
* Set untrusted ips.
*
* @return self
*/
public function untrusted(array $untrusted)
{
$this->untrusted = $untrusted;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
if (!Middleware::hasAttribute($request, ClientIp::KEY)) {
throw new RuntimeException('Firewall middleware needs ClientIp executed before');
}
$ips = ClientIp::getIps($request) ?: [];
$firewall = new IpFirewall();
if (!empty($this->trusted)) {
$firewall->addList($this->trusted, 'trusted', true);
}
if (!empty($this->untrusted)) {
$firewall->addList($this->untrusted, 'untrusted', false);
}
foreach ($ips as $ip) {
$ok = $firewall->setIpAddress($ip)->handle();
if (!$ok) {
return $response->withStatus(403);
}
}
return $next($request, $response);
}
}
src/Middleware/When.php 0000666 00000003446 13436756072 0011052 0 ustar 00 condition($condition);
if ($middleware !== null) {
$this->middleware($middleware);
}
}
/**
* Set the condition.
*
* @param mixed $condition
*
* @return self
*/
public function condition($condition)
{
$this->condition = $condition;
return $this;
}
/**
* Set the middleware.
*
* @param callable $middleware
*
* @return self
*/
public function middleware(callable $middleware)
{
$this->middleware = $middleware;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$condition = $this->condition;
if (is_callable($condition)) {
$condition = $condition($request, $response);
}
if (empty($condition)) {
return $next($request, $response);
}
return call_user_func($this->middleware, $request, $response, $next);
}
}
src/Middleware/FastRoute.php 0000666 00000003542 13436756072 0012062 0 ustar 00 router($router);
}
}
/**
* Extra arguments passed to the controller.
*
* @param Dispatcher $router
*
* @return self
*/
public function router(Dispatcher $router)
{
$this->router = $router;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$router = $this->router ?: $this->getFromContainer(Dispatcher::CLASS);
$route = $router->dispatch($request->getMethod(), $request->getUri()->getPath());
if ($route[0] === Dispatcher::NOT_FOUND) {
return $response->withStatus(404);
}
if ($route[0] === Dispatcher::METHOD_NOT_ALLOWED) {
return $response->withStatus(405);
}
foreach ($route[2] as $name => $value) {
$request = $request->withAttribute($name, $value);
}
$response = self::executeTarget($route[1], $this->arguments, $request, $response);
return $next($request, $response);
}
}
src/Middleware/Minify.php 0000666 00000007547 13436756072 0011412 0 ustar 00 forCache = $forCache;
return $this;
}
/**
* Set inlineCss directive.
*
* @param bool $inlineCss
*
* @return self
*/
public function inlineCss($inlineCss = true)
{
$this->inlineCss = $inlineCss;
return $this;
}
/**
* Set inlineJs directive.
*
* @param bool $inlineJs
*
* @return self
*/
public function inlineJs($inlineJs = true)
{
$this->inlineJs = $inlineJs;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
if ($this->forCache && !static::isCacheable($request, $response)) {
return $next($request, $response);
}
if (!Middleware::hasAttribute($request, FormatNegotiator::KEY)) {
throw new RuntimeException('Minify middleware needs FormatNegotiator executed before');
}
switch (FormatNegotiator::getFormat($request)) {
case 'css':
return $next($request, $this->minifyCss($response));
case 'js':
return $next($request, $this->minifyJs($response));
case 'html':
return $next($request, $this->minifyHtml($response));
default:
return $next($request, $response);
}
}
/**
* Minify html code.
*
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
protected function minifyHtml(ResponseInterface $response)
{
$options = ['jsCleanComments' => true];
if ($this->inlineCss) {
$cssMinify = new CssMinify();
$options['cssMinifier'] = function ($css) use ($cssMinify) {
return $cssMinify->run($css);
};
}
if ($this->inlineJs) {
$options['jsMinifier'] = function ($js) {
return JsMinify::minify($js);
};
}
$stream = Middleware::createStream();
$stream->write(HtmlMinify::minify((string) $response->getBody(), $options));
return $response->withBody($stream);
}
/**
* Minify css code.
*
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
protected function minifyCss(ResponseInterface $response)
{
$stream = Middleware::createStream();
$stream->write((new CssMinify())->run((string) $response->getBody()));
return $response->withBody($stream);
}
/**
* Minify js code.
*
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
protected function minifyJs(ResponseInterface $response)
{
$stream = Middleware::createStream();
$stream->write(JsMinify::minify((string) $response->getBody()));
return $response->withBody($stream);
}
}
src/Middleware/DigestAuthentication.php 0000666 00000006466 13436756072 0014275 0 ustar 00 nonce = $nonce;
return $this;
}
/**
* Execute the middleware.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
if ($this->login($request)) {
return $next($request, $response);
}
return $response
->withStatus(401)
->withHeader('WWW-Authenticate', 'Digest realm="'.$this->realm.'",qop="auth",nonce="'.($this->nonce ?: uniqid()).'",opaque="'.md5($this->realm).'"');
}
/**
* Login or check the user credentials.
*
* @param RequestInterface $request
*
* @return bool
*/
protected function login(RequestInterface $request)
{
//Check header
$authorization = static::parseAuthorizationHeader($request->getHeaderLine('Authorization'));
if (!$authorization) {
return false;
}
//Check whether user exists
if (!isset($this->users[$authorization['username']])) {
return false;
}
//Check authentication
return $this->checkAuthentication($authorization, $request->getMethod(), $this->users[$authorization['username']]);
}
/**
* Validates the user authentication.
*
* @param array $authorization
* @param string $method
* @param string $password
*
* @return bool
*/
protected function checkAuthentication(array $authorization, $method, $password)
{
$A1 = md5("{$authorization['username']}:{$this->realm}:{$password}");
$A2 = md5("{$method}:{$authorization['uri']}");
$validResponse = md5("{$A1}:{$authorization['nonce']}:{$authorization['nc']}:{$authorization['cnonce']}:{$authorization['qop']}:{$A2}");
return ($authorization['response'] === $validResponse);
}
/**
* Parses the authorization header for a basic authentication.
*
* @param string $header
*
* @return false|array
*/
protected static function parseAuthorizationHeader($header)
{
if (strpos($header, 'Digest') !== 0) {
return false;
}
$needed_parts = ['nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1];
$data = [];
preg_match_all('@('.implode('|', array_keys($needed_parts)).')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', substr($header, 7), $matches, PREG_SET_ORDER);
if ($matches) {
foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}
}
return empty($needed_parts) ? $data : false;
}
}
src/Middleware/Robots.php 0000666 00000001667 13436756072 0011424 0 ustar 00 getUri()->getPath() === '/robots.txt') {
$body = Middleware::createStream();
$body->write("User-Agent: *\nDisallow: /");
return $response->withBody($body);
}
$response = $next($request, $response);
return $response->withHeader(self::HEADER, 'noindex, nofollow, noarchive');
}
}
src/Middleware/FormatNegotiator.php 0000666 00000010303 13436756072 0013423 0 ustar 00 ['text/html', 'application/xhtml+xml'],
'css' => ['text/css'],
'gif' => ['image/gif'],
'png' => ['image/png', 'image/x-png'],
'jpg' => ['image/jpeg', 'image/jpg'],
'jpeg' => ['image/jpeg', 'image/jpg'],
'json' => ['application/json', 'text/json', 'application/x-json'],
'jsonp' => ['text/javascript', 'application/javascript', 'application/x-javascript'],
'js' => ['text/javascript', 'application/javascript', 'application/x-javascript'],
'pdf' => ['application/pdf', 'application/x-download'],
'rdf' => ['application/rdf+xml'],
'rss' => ['application/rss+xml'],
'atom' => ['application/atom+xml'],
'xml' => ['text/xml', 'application/xml', 'application/x-xml'],
'txt' => ['text/plain'],
'mp4' => ['video/mp4'],
'ogg' => ['audio/ogg'],
'ogv' => ['video/ogg'],
'webm' => ['video/webm'],
'webp' => ['image/webp'],
'svg' => ['image/svg+xml'],
'zip' => ['application/zip', 'application/x-zip', 'application/x-zip-compressed'],
];
/**
* Returns the format.
*
* @param ServerRequestInterface $request
*
* @return string|null
*/
public static function getFormat(ServerRequestInterface $request)
{
return Middleware::getAttribute($request, self::KEY);
}
/**
* Add a new format.
*
* @param string $format
* @param array $mimeTypes
*
* @return self
*/
public function addFormat($format, array $mimeTypes)
{
$this->formats[$format] = $mimeTypes;
return $this;
}
/**
* Set the default format.
*
* @param string $format
*
* @return self
*/
public function defaultFormat($format)
{
$this->default = $format;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$format = $this->getFromExtension($request) ?: $this->getFromHeader($request) ?: $this->default;
if ($format) {
$request = Middleware::setAttribute($request, self::KEY, $format);
$response = $response->withHeader('Content-Type', $this->formats[$format][0].'; charset=utf-8');
}
return $next($request, $response);
}
/**
* Returns the format using the file extension.
*
* @return null|string
*/
protected function getFromExtension(ServerRequestInterface $request)
{
$format = strtolower(pathinfo($request->getUri()->getPath(), PATHINFO_EXTENSION));
return isset($this->formats[$format]) ? $format : null;
}
/**
* Returns the format using the Accept header.
*
* @return null|string
*/
protected function getFromHeader(ServerRequestInterface $request)
{
$accept = $request->getHeaderLine('Accept');
if (empty($accept)) {
return;
}
$priorities = call_user_func_array('array_merge', array_values($this->formats));
try {
$accept = (new Negotiator())->getBest($accept, $priorities);
} catch (\Exception $exception) {
return;
}
if ($accept) {
$accept = $accept->getValue();
foreach ($this->formats as $extension => $headers) {
if (in_array($accept, $headers)) {
return $extension;
}
}
}
}
}
src/Middleware/ResponseTime.php 0000666 00000001725 13436756072 0012564 0 ustar 00 getServerParams();
if (!isset($server['REQUEST_TIME_FLOAT'])) {
$server['REQUEST_TIME_FLOAT'] = microtime(true);
}
$response = $next($request, $response);
$time = (microtime(true) - $server['REQUEST_TIME_FLOAT']) * 1000;
return $response->withHeader(self::HEADER, sprintf('%2.3fms', $time));
}
}
src/Middleware/BasicAuthentication.php 0000666 00000003557 13436756072 0014075 0 ustar 00 getHeaderLine('Authorization'));
if ($authorization && $this->checkUserPassword($authorization['username'], $authorization['password'])) {
return $next($request, $response);
}
return $response
->withStatus(401)
->withHeader('WWW-Authenticate', 'Basic realm="'.$this->realm.'"');
}
/**
* Validate the user and password.
*
* @param string $username
* @param string $password
*
* @return bool
*/
protected function checkUserPassword($username, $password)
{
if (!isset($this->users[$username]) || $this->users[$username] !== $password) {
return false;
}
return true;
}
/**
* Parses the authorization header for a basic authentication.
*
* @param string $header
*
* @return false|array
*/
protected static function parseAuthorizationHeader($header)
{
if (strpos($header, 'Basic') !== 0) {
return false;
}
$header = explode(':', base64_decode(substr($header, 6)), 2);
return [
'username' => $header[0],
'password' => isset($header[1]) ? $header[1] : null,
];
}
}
src/Middleware/DebugBar.php 0000666 00000003711 13436756072 0011617 0 ustar 00 debugBar($debugBar);
}
}
/**
* Set the debug bar.
*
* @param Bar $debugBar
*
* @return self
*/
public function debugBar(Bar $debugBar)
{
$this->debugBar = $debugBar;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$response = $next($request, $response);
if (!Middleware::hasAttribute($request, FormatNegotiator::KEY)) {
throw new RuntimeException('DebugBar middleware needs FormatNegotiator executed before');
}
if (FormatNegotiator::getFormat($request) === 'html') {
$debugBar = $this->debugBar ?: $this->getFromContainer(Bar::CLASS);
$renderer = $debugBar->getJavascriptRenderer();
ob_start();
echo '';
echo '';
echo $renderer->render();
$response->getBody()->write(ob_get_clean());
}
return $response;
}
}
src/Middleware/Payload.php 0000666 00000006351 13436756072 0011540 0 ustar 00 associative = (boolean) $associative;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
return $next($this->handlePayload($request), $response);
}
/**
* Handle the payload.
*
* @param ServerRequestInterface $request
*
* @return ServerRequestInterface
*/
protected function handlePayload(ServerRequestInterface $request)
{
if ($request->getParsedBody() || !in_array($request->getMethod(), ['POST', 'PUT', 'DELETE'], true)) {
return $request;
}
$contentType = trim($request->getHeaderLine('Content-Type'));
//json
if (stripos($contentType, 'application/json') === 0) {
return $request
->withParsedBody($this->parseJson($request->getBody()))
->withBody(Middleware::createStream());
}
//urlencoded
if (stripos($contentType, 'application/x-www-form-urlencoded') === 0) {
return $request
->withParsedBody($this->parseUrlEncoded($request->getBody()))
->withBody(Middleware::createStream());
}
//csv
if (stripos($contentType, 'text/csv') === 0) {
return $request
->withParsedBody($this->parseCsv($request->getBody()))
->withBody(Middleware::createStream());
}
return $request;
}
/**
* Parses json.
*
* @param StreamInterface $body
*
* @return array
*/
protected function parseJson(StreamInterface $body)
{
return json_decode((string) $body, $this->associative);
}
/**
* Parses url-encoded strings.
*
* @param StreamInterface $body
*
* @return array
*/
protected function parseUrlEncoded(StreamInterface $body)
{
parse_str((string) $body, $data);
return $data ?: [];
}
/**
* Parses csv.
*
* @param StreamInterface $body
*
* @return array
*/
protected function parseCsv(StreamInterface $body)
{
if ($body->isSeekable()) {
$body->rewind();
}
$stream = $body->detach();
$data = [];
while (($row = fgetcsv($stream)) !== false) {
$data[] = $row;
}
fclose($stream);
return $data;
}
}
src/Middleware/ClientIp.php 0000666 00000005547 13436756072 0011664 0 ustar 00 headers($headers);
}
}
/**
* Configure the trusted headers.
*
* @param array $headers
*
* @return self
*/
public function headers(array $headers)
{
$this->headers = $headers;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$request = Middleware::setAttribute($request, self::KEY, $this->scanIps($request));
return $next($request, $response);
}
/**
* Detect and return all ips found.
*
* @param ServerRequestInterface $request
*
* @return array
*/
protected function scanIps(ServerRequestInterface $request)
{
$server = $request->getServerParams();
$ips = [];
if (!empty($server['REMOTE_ADDR']) && filter_var($server['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
$ips[] = $server['REMOTE_ADDR'];
}
foreach ($this->headers as $name) {
$header = $request->getHeaderLine($name);
if (!empty($header)) {
foreach (array_map('trim', explode(',', $header)) as $ip) {
if ((array_search($ip, $ips) === false) && filter_var($ip, FILTER_VALIDATE_IP)) {
$ips[] = $ip;
}
}
}
}
return $ips;
}
}
src/Middleware/AuraRouter.php 0000666 00000005400 13436756072 0012232 0 ustar 00 router($router);
}
}
/**
* Extra arguments passed to the controller.
*
* @param RouterContainer $router
*
* @return self
*/
public function router(RouterContainer $router)
{
$this->router = $router;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$router = $this->router ?: $this->getFromContainer(RouterContainer::CLASS);
if (empty($router)) {
throw new RuntimeException('No RouterContainer instance has been provided');
}
$matcher = $router->getMatcher();
$route = $matcher->match($request);
if (!$route) {
$failedRoute = $matcher->getFailedRoute();
switch ($failedRoute->failedRule) {
case 'Aura\Router\Rule\Allows':
return $response->withStatus(405); // 405 METHOD NOT ALLOWED
case 'Aura\Router\Rule\Accepts':
return $response->withStatus(406); // 406 NOT ACCEPTABLE
default:
return $response->withStatus(404); // 404 NOT FOUND
}
}
$request = Middleware::setAttribute($request, self::KEY, $route);
foreach ($route->attributes as $name => $value) {
$request = $request->withAttribute($name, $value);
}
$response = self::executeTarget($route->handler, $this->arguments, $request, $response);
return $next($request, $response);
}
}
src/Middleware/LanguageNegotiator.php 0000666 00000004460 13436756072 0013725 0 ustar 00 languages($languages);
}
}
/**
* Configure the available languages.
*
* @param array $languages
*
* @return self
*/
public function languages(array $languages)
{
$this->languages = $languages;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$language = $this->getFromHeader($request);
if (empty($language)) {
$language = isset($this->languages[0]) ? $this->languages[0] : null;
}
$request = Middleware::setAttribute($request, self::KEY, $language);
return $next($request, $response);
}
/**
* Returns the language using the Accept-Language header.
*
* @return null|string
*/
protected function getFromHeader(ServerRequestInterface $request)
{
$accept = $request->getHeaderLine('Accept-Language');
if (empty($accept) || empty($this->languages)) {
return;
}
$language = (new Negotiator())->getBest($accept, $this->languages);
if ($language) {
return $language->getValue();
}
}
}
src/Middleware/AuraSession.php 0000666 00000004246 13436756072 0012404 0 ustar 00 factory($factory);
}
}
/**
* Set the session name.
*
* @param string $name
*
* @return self
*/
public function name($name)
{
$this->name = $name;
return $this;
}
/**
* Set the session factory.
*
* @param SessionFactory $factory
*
* @return self
*/
public function factory(SessionFactory $factory)
{
$this->factory = $factory;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$factory = $this->factory ?: $this->getFromContainer(SessionFactory::CLASS, false) ?: new SessionFactory();
$session = $factory->newInstance($request->getCookieParams());
if ($this->name !== null) {
$session->setName($this->name);
}
$request = Middleware::setAttribute($request, self::KEY, $session);
return $next($request, $response);
}
}
src/Middleware/ErrorHandler.php 0000666 00000006452 13436756072 0012540 0 ustar 00 handler($handler);
}
}
/**
* Set the error handler.
*
* @param string|callable $handler
*
* @return self
*/
public function handler($handler)
{
$this->handler = $handler;
return $this;
}
/**
* Set an instance of Whoops.
*
* @param Run $whoops
*
* @return self
*/
public function whoops(Run $whoops)
{
$this->whoops = $whoops;
return $this;
}
/**
* Configure the catchExceptions.
*
* @param bool $catch
*
* @return self
*/
public function catchExceptions($catch = true)
{
$this->catchExceptions = (boolean) $catch;
return $this;
}
/**
* Execute the middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
if ($this->whoops) {
$this->whoops->pushHandler(function ($exception) use ($request, $response) {
try {
echo self::executeTarget($this->handler, $this->arguments, Middleware::setAttribute($request, self::KEY, $exception), $response)->getBody();
} catch (\Exception $exception) {}
});
}
ob_start();
try {
$response = $next($request, $response);
} catch (\Exception $exception) {
if (!$this->catchExceptions) {
throw $exception;
}
$request = Middleware::setAttribute($request, self::KEY, $exception);
$response = $response->withStatus(500);
}
ob_end_clean();
if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 600) {
try {
return self::executeTarget($this->handler, $this->arguments, $request, $response);
} catch (\Exception $exception) {}
}
if ($this->whoops) {
$this->whoops->popHandler();
}
return $response;
}
}
src/Middleware/SaveResponse.php 0000666 00000003034 13436756072 0012557 0 ustar 00 getUri()->getQuery()) && static::isCacheable($request, $response)) {
static::writeStream($response->getBody(), $this->getFilename($request));
}
return $response;
}
/**
* Write the stream to the given path.
*
* @param StreamInterface $stream
* @param string $path
*/
protected static function writeStream(StreamInterface $stream, $path)
{
$dir = dirname($path);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$handle = fopen($path, 'wb+');
if (false === $handle) {
throw new RuntimeException('Unable to write to designated path');
}
$stream->rewind();
while (!$stream->eof()) {
fwrite($handle, $stream->read(4096));
}
fclose($handle);
}
}
src/Middleware/Cache.php 0000666 00000006457 13436756072 0011161 0 ustar 00 cache($cache);
}
}
/**
* Set the psr-6 cache pool used.
*
* @param CacheItemPoolInterface $cache
*/
public function cache(CacheItemPoolInterface $cache)
{
$this->cache = $cache;
}
/**
* Execute the middleware.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
$cache = $this->cache ?: $this->getFromContainer(CacheItemPoolInterface::CLASS);
$item = $cache->getItem(static::getCacheKey($request));
if ($item->isHit()) {
list($headers, $body) = $item->get();
$response = $response->withBody(Middleware::createStream());
$response->getBody()->write($body);
foreach ($headers as $name => $header) {
$response = $response->withHeader($name, $header);
}
return $response;
}
$response = $next($request, $response);
if (static::isCacheable($request, $response)) {
$item->set([
$response->getHeaders(),
(string) $response->getBody(),
]);
if (($time = static::getExpiration($response)) !== null) {
$item->expiresAt($time);
}
$cache->save($item);
}
return $response;
}
/**
* Check the cache headers and return the expiration time.
*
* @param ResponseInterface $response
*
* @return Datetime|null
*/
protected static function getExpiration(ResponseInterface $response)
{
//Cache-Control
$cacheControl = $response->getHeaderLine('Cache-Control');
if (!empty($cacheControl)) {
$cacheControl = static::parseCacheControl($cacheControl);
//Max age
if (isset($cacheControl['max-age'])) {
return new Datetime('@'.(time() + (int) $cacheControl['max-age']));
}
}
//Expires
$expires = $response->getHeaderLine('Expires');
if (!empty($expires)) {
return new Datetime($expires);
}
}
/**
* Returns the id used to cache a request.
*
* @param RequestInterface $request
*
* @return string
*/
protected function getCacheKey(RequestInterface $request)
{
return $request->getMethod().md5((string) $request->getUri());
}
}
composer.json 0000666 00000003177 13436756072 0007317 0 ustar 00 {
"name": "oscarotero/psr7-middlewares",
"type": "library",
"description": "Collection of HTTP middlewares compatible with PSR-7",
"keywords": ["PSR-7", "http", "middlewares", "psr-messages"],
"homepage": "https://github.com/oscarotero/psr7-middlewares",
"license": "MIT",
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/oscarotero/psr7-middlewares/issues"
},
"require": {
"php": ">=5.5.0",
"container-interop/container-interop": "^1.1"
},
"require-dev": {
"zendframework/zend-diactoros": "1.*",
"relay/relay": "1.*",
"nikic/fast-route": "0.*",
"aura/router": "3.0.0-beta2",
"aura/session": "2.*",
"phpunit/phpunit": "~4.0",
"willdurand/negotiation": "~2.0",
"m6web/firewall": "~1.0",
"mrclay/minify": "^2.2",
"matthiasmullie/scrapbook": "dev-master",
"ramsey/uuid": "^2.8",
"neomerx/cors-psr7": "^0.2.0",
"maximebf/debugbar": "~1.10"
},
"suggest": {
"aura/router": "To use Middleware::auraRouter",
"aura/session": "To use Middleware::auraSession",
"nikic/fast-route": "To use Middleware::fastRoute",
"willdurand/negotiation": "To use Middleware::formatNegotiator() and Middleware::languageNegotiator()",
"m6web/firewall": "To use Middleware::firewall()",
"mrclay/minify": "To use Middleware::minify()",
"ramsey/uuid": "To use Middleware::uuid()",
"neomerx/cors-psr7": "To use Middleware::cors()",
"maximebf/debugbar": "To use Middleware::debugBar()"
},
"autoload": {
"psr-4": {
"Psr7Middlewares\\": "src/"
}
}
}