LICENSE 0000666 00000002067 13436747160 0005575 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 00000031227 13436747160 0006047 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 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 similar.
## 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
Middleware::setStreamFactory(function ($file, $mode) {
return new Stream($file, $mode);
});
//Create a relay dispatcher and add some middlewares:
$relay = new RelayBuilder();
$dispatcher = $relay->newInstance([
//Handle errors
Middleware::errorHandler()
->handler('error_handler_function')
->catchExceptions(true)
//Remove the path prefix
Middleware::BasePath('/my-site/web'),
//Digest authentication
Middleware::DigestAuthentication()
->users([
'username' => 'password'
]),
//Get the client ip
Middleware::ClientIp(),
//Allow only some ips
Middleware::Firewall()
->trusted(['127.0.0.*']),
//Detects the user preferred language
Middleware::LanguageNegotiator()
->languages(['gl', 'es', 'en']),
//Detects the format
Middleware::FormatNegotiator(),
//Execute fast route
Middleware::FastRoute()
->router($app->get('dispatcher')),
//Minify the result
Middleware::Minify()
//Saves the response in a file
Middleware::saveResponse()
->storage('app/public')
]);
$response = $dispatcher(ServerRequestFactory::fromGlobals(), new Response());
```
## Available middlewares
* [AuraRouter](#aurarouter)
* [AuraSession](#aurasession)
* [BasePath](#basepath)
* [BasicAuthentication](#basicauthentication)
* [Cache](#cache)
* [ClientIp](#clientip)
* [DigestAuthentication](#digestauthentication)
* [ErrorHandler](#errorhandler)
* [FastRoute](#fastroute)
* [Firewall](#firewall)
* [FormatNegotiation](#formatnegotiation)
* [LanguageNegotiation](#languagenegotiation)
* [Minify](#minify)
* [SaveResponse](#saveresponse)
* [TrailingSlash](#trailingslash)
### AuraRouter
To use [Aura.Router](https://github.com/auraphp/Aura.Router) as a middleware. You need to use 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 body's response
$response->getBody()->write('Hello '.$name);
//or echo the output (it will be captured and passed to body stream)
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 pass an array with all users and password allowed:
```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). You can configure the allowed headers.
```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 = array_implode(', ', ClientIp::getIps($request));
return $next($request, $response);
}
]);
```
### DigestAuthentication
Implements the [digest http authentication](http://php.net/manual/en/features.http-auth.php). You have to pass an array with all users and password allowed as first argument:
```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. To use it with an error handler like [whoops](https://github.com/filp/whoops), you can register it to catch exceptions and other errors using the methods `before` and `after`. These methods receives a callable wrapping your handler that returns a ResponseInterface object.
```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()
//My error handler
->handler('errorHandler')
//(optional) append arguments to the handler
->arguments($myApp)
//(optional) register your function to catch exceptions or other errors
->before(function ($handler) use ($whoops) {
$whoops->pushHandler(function () use ($handler) {
echo $handler()->getBody();
});
})
//(optional) unregister the error catcher
->after(function ($handler) use ($whoops) {
$whoops->popHandler();
})
//(optional) catch exceptions, if you don't use an external library for that
->catchExceptions(true)
]);
```
### 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
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](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\FormatNegotiation;
$dispatcher = $relay->getInstance([
Middleware::FormatNegotiation()
->negotiator($negotiator) //(optional) Instance of Negotiation\FormatNegotiator
->addFormat('pdf', ['application/pdf', 'application/x-download']) //(optional) add new formats and mimetypes
},
function ($request, $response, $next) {
//get the format (for example: html)
$format = FormatNegotiation::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\LanguageNegotiation;
$dispatcher = $relay->getInstance([
Middleware::LanguageNegotiation()
->languages(['gl', 'en', 'es']), //Available languages
function ($request, $response, $next) {
//Get the preferred language
$language = LanguageNegotiation::getLanguage($request);
return $next($request, $response);
}
]);
```
### 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([
Middleware::Minify()
->forCache(true) //(optional) only minify cacheable responses (see SaveResponse)
->inlineCss(false) //(optional) enable/disable inline css minification
->inlineJs(false) //(optional) enable/disable inline js minification
]);
```
### 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 where save the responses
->basePath('public') //(optional) basepath ignored from the request uri
]);
```
### TrailingSlash
Removes 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
->basePath('public') //(optional) basepath
]);
```
## Contribution
New middlewares are appreciated. Just create a pull request.
phpunit.xml 0000666 00000000245 13436747160 0006775 0 ustar 00
./tests/
src/Middleware.php 0000666 00000004153 13436747160 0010143 0 ustar 00 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];
}
}
}
src/Utils/ArgumentsTrait.php 0000666 00000000554 13436747160 0012140 0 ustar 00 arguments = func_get_args();
return $this;
}
}
src/Utils/StorageTrait.php 0000666 00000001055 13436747160 0011574 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 00000001600 13436747160 0013143 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 00000005266 13436747160 0011460 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/CacheTrait.php 0000666 00000003204 13436747160 0011171 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 00000001352 13436747160 0011657 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/TrailingSlash.php 0000666 00000003305 13436747160 0012705 0 ustar 00 addSlash($addSlash);
}
/**
* Configure whether the path should be added or removed
*
* @param boolean $addSlash
*
* @return self
*/
public function addSlash($addSlash)
{
$this->addSlash = (boolean) $addSlash;
return $this;
}
/**
* Execute the middleware
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $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 .= '/';
}
$request = $request->withUri($uri->withPath($path));
return $next($request, $response);
}
}
src/Middleware/BasePath.php 0000666 00000001756 13436747160 0011640 0 ustar 00 basePath($basePath);
}
}
/**
* Execute the middleware
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $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 00000003613 13436747160 0011710 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
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$ips = ClientIp::getIps($request);
if ($ips === null) {
throw new RuntimeException('Firewall middleware needs ClientIp executed before');
}
$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/FastRoute.php 0000666 00000003526 13436747160 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
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
if ($this->router === null) {
throw new RuntimeException('No FastRoute\\Dispatcher instance has been provided');
}
$route = $this->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 00000007267 13436747160 0011407 0 ustar 00 forCache = $forCache;
return $this;
}
/**
* Set inlineCss directive
*
* @param boolean $inlineCss
*
* @return self
*/
public function inlineCss($inlineCss = true)
{
$this->inlineCss = $inlineCss;
return $this;
}
/**
* Set inlineJs directive
*
* @param boolean $inlineJs
*
* @return self
*/
public function inlineJs($inlineJs = true)
{
$this->inlineJs = $inlineJs;
return $this;
}
/**
* Execute the middleware
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
if ($this->forCache && !static::isCacheable($request, $response)) {
return $next($request, $response);
}
$header = $response->getHeaderLine('Content-Type');
$extension = strtolower(pathinfo($request->getUri()->getPath(), PATHINFO_EXTENSION));
if ($extension === 'css' || strpos($header, 'txt/css') !== false) {
return $next($request, $this->minifyCss($response));
}
if ($extension === 'js' || strpos($header, '/javascript') !== false) {
return $next($request, $this->minifyJs($response));
}
if ($extension === 'html' || strpos($header, 'html') !== false) {
return $next($request, $this->minifyHtml($response));
}
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 = Factory::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 = Factory::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 = Factory::createStream();
$stream->write(JsMinify::minify((string) $response->getBody()));
return $response->withBody($stream);
}
}
src/Middleware/DigestAuthentication.php 0000666 00000006410 13436747160 0014260 0 ustar 00 nonce = $nonce;
return $this;
}
/**
* Execute the middleware
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $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 ServerRequestInterface $request
*
* @return boolean
*/
protected function login(ServerRequestInterface $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 boolean
*/
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/FormatNegotiator.php 0000666 00000007417 13436747160 0013435 0 ustar 00 ['application/atom+xml'],
'css' => ['text/css'],
'html' => ['text/html', 'application/xhtml+xml'],
'gif' => ['image/gif'],
'jpg' => ['image/jpeg', 'image/jpg'],
'jpeg' => ['image/jpeg', 'image/jpg'],
'js' => ['text/javascript', 'application/javascript', 'application/x-javascript'],
'jsonp' => ['text/javascript', 'application/javascript', 'application/x-javascript'],
'json' => ['application/json', 'text/json', 'application/x-json'],
'png' => ['image/png', 'image/x-png'],
'pdf' => ['application/pdf', 'application/x-download'],
'rdf' => ['application/rdf+xml'],
'rss' => ['application/rss+xml'],
'txt' => ['text/plain'],
'xml' => ['text/xml', 'application/xml', 'application/x-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);
}
/**
* Constructor. Defines de available formats.
*
* @param Negotiator $negotiator
*/
public function __construct(Negotiator $negotiator = null)
{
if ($negotiator !== null) {
$this->negotiator($negotiator);
}
}
/**
* Set the negotiator used
*
* @param Negotiator $negotiator
*
* @return self
*/
public function negotiator(Negotiator $negotiator)
{
$this->negotiator = $negotiator;
return $this;
}
/**
* Add a new format
*
* @param string $format
* @param array $mimeTypes
*
* @return self
*/
public function addFormat($format, array $mimeTypes)
{
$this->formats[$format] = $mimeTypes;
return $this;
}
/**
* Execute the middleware
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
//Calculate using the extension
$format = strtolower(pathinfo($request->getUri()->getPath(), PATHINFO_EXTENSION));
//Calculate using the header
$negotiator = $this->getNegotiator();
if (!$negotiator->normalizePriorities([$format])) {
$format = $negotiator->getBestFormat($request->getHeaderLine('Accept'));
}
//Save the format as attribute
$request = Middleware::setAttribute($request, self::KEY, $format);
//Set the content-type to the response
if (($mime = $negotiator->normalizePriorities([$format]))) {
return $next($request, $response->withHeader('Content-Type', $mime[0].'; charset=utf-8'));
}
return $next($request, $response);
}
/**
* Returns the negotiator
*
* @return Negotiator
*/
protected function getNegotiator()
{
if ($this->negotiator === null) {
$this->negotiator = new Negotiator();
foreach ($this->formats as $name => $mimeTypes) {
$this->negotiator->registerFormat($name, $mimeTypes, true);
}
}
return $this->negotiator;
}
}
src/Middleware/BasicAuthentication.php 0000666 00000003555 13436747160 0014071 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 boolean
*/
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/ClientIp.php 0000666 00000005400 13436747160 0011646 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
*
* @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 00000005117 13436747160 0012235 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
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
if ($this->router === null) {
throw new RuntimeException('No RouterContainer instance has been provided');
}
$matcher = $this->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 00000003607 13436747160 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
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$negotiator = new Negotiator();
$language = $negotiator->getBest($request->getHeaderLine('Accept-Language'), $this->languages);
if ($language) {
$language = strtolower(substr($language->getValue(), 0, 2));
} else {
$language = isset($this->languages[0]) ? $this->languages[0] : null;
}
$request = Middleware::setAttribute($request, self::KEY, $language);
return $next($request, $response);
}
}
src/Middleware/AuraSession.php 0000666 00000003630 13436747160 0012376 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
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$factory = $this->factory ?: 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 00000006231 13436747160 0012531 0 ustar 00 handler($handler);
}
}
/**
* Set the error handler
*
* @param string|callable $handler
*
* @return self
*/
public function handler($handler)
{
$this->handler = $handler;
return $this;
}
/**
* Configure the catchExceptions
*
* @param boolean $catch
*
* @return self
*/
public function catchExceptions($catch = true)
{
$this->catchExceptions = (boolean) $catch;
return $this;
}
/**
* Register a handler executed before
*
* @param callable $handler
*
* @return self
*/
public function before(callable $handler)
{
$this->before = $handler;
return $this;
}
/**
* Register a handler executed after
*
* @param callable $handler
*
* @return self
*/
public function after(callable $handler)
{
$this->after = $handler;
return $this;
}
/**
* Execute the middleware
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$handler = function () use ($request, $response) {
return self::executeTarget($this->handler, $this->arguments, $request, $response);
};
if ($this->before !== null) {
call_user_func($this->before, $handler);
}
try {
$response = $next($request, $response);
} catch (\Exception $exception) {
if (!$this->catchExceptions) {
throw $exception;
}
$request = Middleware::setAttribute($request, self::KEY, $exception);
$response = $response->withStatus(500);
}
if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 600) {
return self::executeTarget($this->handler, $this->arguments, $request, $response);
}
if ($this->after !== null) {
call_user_func($this->after, $handler);
}
return $response;
}
}
src/Middleware/SaveResponse.php 0000666 00000004742 13436747160 0012564 0 ustar 00 getQueryParams()) && static::isCacheable($request, $response)) {
static::writeStream($response->getBody(), $this->getCacheFilename($request));
}
return $response;
}
/**
* Returns the filename of the response cache file
*
* @param ServerRequestInterface $request
*
* @return string
*/
protected function getCacheFilename(ServerRequestInterface $request)
{
$path = $this->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;
}
$filename = 'index.'.(FormatNegotiator::getFormat($request) ?: 'html');
}
return $this->storage.$path.'/'.$filename;
}
/**
* 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 00000006256 13436747160 0011154 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 ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$item = $this->cache->getItem(static::getCacheKey($request));
if ($item->isHit()) {
list($headers, $body) = $item->get();
$response = $response->withBody(Factory::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);
}
$this->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 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 ServerRequestInterface $request
*
* @return string
*/
protected function getCacheKey(ServerRequestInterface $request)
{
return $request->getMethod().md5((string) $request->getUri());
}
}
composer.json 0000666 00000002530 13436747160 0007305 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"
},
"require-dev": {
"zendframework/zend-diactoros": "1.*",
"relay/relay": "0.2",
"nikic/fast-route": "0.*",
"aura/router": "3.0.0-beta2",
"aura/session": "2.*",
"phpunit/phpunit": "~4.0",
"willdurand/negotiation": "~1.3",
"m6web/firewall": "~1.0",
"mrclay/minify": "^2.2",
"matthiasmullie/scrapbook": "dev-master"
},
"suggest": {
"aura/router": "To use the AuraRouter middleware",
"aura/session": "To use the AuraSession middleware",
"nikic/fast-route": "To use the FastRoute middleware",
"willdurand/negotiation": "To use the FormatNegotiator or LanguageNegotiator middleware",
"m6web/firewall": "To use the Firewall middleware",
"mrclay/minify": "To use the Minify middleware"
},
"autoload": {
"psr-4": {
"Psr7Middlewares\\": "src/"
}
}
}
.travis.yml 0000666 00000000314 13436747160 0006672 0 ustar 00 language: php
php:
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
allow_failures:
- php: 7.0
- php: hhvm
before_install:
- composer self-update
- composer install
script: ./vendor/bin/phpunit
tests/TrailingSlashTest.php 0000666 00000001644 13436747160 0012047 0 ustar 00 execute(
[
Middleware::trailingSlash()
->basePath($basePath),
function ($request, $response, $next) {
$response->getBody()->write($request->getUri()->getPath());
return $response;
},
],
$url
);
$this->assertEquals($result, (string) $response->getBody());
}
}
tests/Base.php 0000666 00000001723 13436747160 0007313 0 ustar 00 withUri(new Uri($uri));
}
protected function response(array $headers = array())
{
return new Response('php://temp', 200, $headers);
}
protected function dispatch(array $middlewares, ServerRequest $request, Response $response)
{
$dispatcher = (new RelayBuilder())->newInstance($middlewares);
return $dispatcher($request, $response);
}
protected function execute(array $middlewares, $url = '', array $headers = array())
{
$request = $this->request($url, $headers);
$response = $this->response();
return $this->dispatch($middlewares, $request, $response);
}
}
tests/ClientIpTest.php 0000666 00000003006 13436747160 0011004 0 ustar 00 'unknow,123.456.789.10,123.234.123.10',
'X-Forwarded' => '123.234.123.10',
],
['123.234.123.10'],
'123.234.123.10',
],[
[
'Client-Ip' => 'unknow,123.456.789.10,123.234.123.10',
'X-Forwarded' => '123.234.123.11',
],
['123.234.123.10', '123.234.123.11'],
'123.234.123.10'
]
];
}
/**
* @dataProvider ipsProvider
*/
public function testIps(array $headers, array $CLIENT_IPS, $CLIENT_IP)
{
$response = $this->execute(
[
Middleware::ClientIp(),
function ($request, $response, $next) {
$response->getBody()->write(json_encode([
'CLIENT_IPS' => ClientIp::getIps($request),
'CLIENT_IP' => ClientIp::getIp($request),
]));
return $response;
},
],
'',
$headers
);
$body = json_decode((string) $response->getBody(), true);
$this->assertEquals($body['CLIENT_IPS'], $CLIENT_IPS);
$this->assertEquals($body['CLIENT_IP'], $CLIENT_IP);
}
}
tests/ErrorHandlerTest.php 0000666 00000002663 13436747160 0011674 0 ustar 00 execute(
[
Middleware::ErrorHandler(function ($request, $response) {
$response->getBody()->write('Page not found');
}),
function ($request, $response, $next) {
return $response->withStatus(404);
},
]
);
$this->assertEquals(404, $response->getStatusCode());
$this->assertEquals('Page not found', (string) $response->getBody());
}
public function testException()
{
$exception = new \Exception("Error Processing Request");
$response = $this->execute(
[
Middleware::ErrorHandler()
->handler(function ($request, $response) {
$exception = ErrorHandler::getException($request);
$response->getBody()->write((string) $exception);
})
->catchExceptions(),
function ($request, $response, $next) use ($exception) {
throw $exception;
},
]
);
$this->assertEquals(500, $response->getStatusCode());
$this->assertEquals((string) $exception, (string) $response->getBody());
}
}
tests/FirewallTest.php 0000666 00000003577 13436747160 0011057 0 ustar 00 'unknow,123.456.789.10,123.234.123.10',
'X-Forwarded' => '123.234.123.10',
],
[],
[],
403,
],[
[
'Client-Ip' => 'unknow,123.456.789.10,123.234.123.10',
'X-Forwarded' => '123.234.123.10',
],
['123.234.123.10'],
[],
200
],[
[
'Client-Ip' => 'unknow,123.456.789.10,123.234.123.11',
'X-Forwarded' => '123.234.123.10',
],
['123.234.123.11'],
['123.234.123.10'],
403
],[
[
'Client-Ip' => '123.0.0.10,123.0.0.11',
'X-Forwarded' => '123.0.0.12',
],
['123.0.0.*'],
[],
200
],[
[
'Client-Ip' => '123.0.0.10,123.0.0.11',
'X-Forwarded' => '123.0.0.12',
],
['123.0.0.*'],
['123.0.0.12'],
403
]
];
}
/**
* @dataProvider ipsProvider
*/
public function testFirewall(array $headers, array $trusted, array $untrusted, $status)
{
$response = $this->execute(
[
Middleware::ClientIp(),
Middleware::Firewall()
->trusted($trusted)
->untrusted($untrusted),
],
'',
$headers
);
$this->assertEquals($status, $response->getStatusCode());
}
}
tests/CacheTest.php 0000666 00000004707 13436747160 0010311 0 ustar 00 getBody()->write(uniqid('test', true));
++$used;
return $next($request, $response);
}
];
//Test
$response1 = $this->dispatch($middlewares, $this->request(), $this->response());
$response2 = $this->dispatch($middlewares, $this->request(), $this->response());
$response3 = $this->dispatch($middlewares, $this->request()->withMethod('POST'), $this->response());
$response4 = $this->dispatch($middlewares, $this->request()->withMethod('POST'), $this->response());
$response5 = $this->dispatch($middlewares, $this->request()->withMethod('GET'), $this->response());
$this->assertEquals((string) $response1->getBody(), (string) $response2->getBody());
$this->assertNotEquals((string) $response2->getBody(), (string) $response3->getBody());
$this->assertNotEquals((string) $response3->getBody(), (string) $response4->getBody());
$this->assertNotEquals((string) $response4->getBody(), (string) $response5->getBody());
$this->assertEquals((string) $response1->getBody(), (string) $response5->getBody());
$this->assertSame(3, $used);
}
public function testExpiration()
{
$cache = new Pool(new MemoryStore());
$used = 0;
$middlewares = [
Middleware::Cache($cache),
function ($request, $response, $next) use (&$used) {
$response->getBody()->write(uniqid('test', true));
++$used;
return $next($request, $response->withHeader('Expires', (new \Datetime('-1 day'))->format('D, d M Y H:i:s')));
}
];
//Test
$response1 = $this->dispatch($middlewares, $this->request(), $this->response());
$response2 = $this->dispatch($middlewares, $this->request(), $this->response());
$this->assertNotEquals((string) $response1->getBody(), (string) $response2->getBody());
$this->assertSame(2, $used);
}
}
tests/AuraRouterTest.php 0000666 00000001716 13436747160 0011374 0 ustar 00 getMap();
$map->get('index', '/user/{name}/{id}', function ($request, $response) {
$this->assertEquals('oscarotero', $request->getAttribute('name'));
$this->assertEquals('35', $request->getAttribute('id'));
$this->assertInstanceOf('Aura\\Router\\Route', AuraRouter::getRoute($request));
$response->getBody()->write('Ok');
return $response;
});
//Test
$response = $this->execute(
[
Middleware::AuraRouter($router),
],
'http://domain.com/user/oscarotero/35'
);
$this->assertEquals('Ok', (string) $response->getBody());
}
}
tests/DigestAuthenticationTest.php 0000666 00000001003 13436747160 0013407 0 ustar 00 execute(
[
Middleware::DigestAuthentication([])->realm('My realm')->nonce('xxx'),
]
);
$this->assertSame(401, $response->getStatusCode());
$this->assertSame('Digest realm="My realm",qop="auth",nonce="xxx",opaque="'.md5('My realm').'"', $response->getHeaderLine('WWW-Authenticate'));
}
}
tests/BasePathTest.php 0000666 00000002125 13436747160 0010765 0 ustar 00 execute(
[
Middleware::BasePath($basepath),
function ($request, $response, $next) {
$response->getBody()->write((string) $request->getUri());
return $response;
},
],
$url
);
$this->assertEquals($result, (string) $response->getBody());
}
}
tests/FastRouteTest.php 0000666 00000001635 13436747160 0011217 0 ustar 00 addRoute('GET', '/user/{name}/{id:[0-9]+}', function ($request, $response) {
$response->getBody()->write(json_encode([
'name' => $request->getAttribute('name'),
'id' => $request->getAttribute('id'),
]));
return $response;
});
});
$response = $this->execute(
[
Middleware::FastRoute($dispatcher),
],
'http://domain.com/user/oscarotero/35'
);
$body = json_decode((string) $response->getBody(), true);
$this->assertEquals($body['name'], 'oscarotero');
$this->assertEquals($body['id'], '35');
}
}
tests/bootstrap.php 0000666 00000000466 13436747160 0010461 0 ustar 00 execute(
[
Middleware::BasicAuthentication([])->realm('My realm'),
]
);
$this->assertSame(401, $response->getStatusCode());
$this->assertSame('Basic realm="My realm"', $response->getHeaderLine('WWW-Authenticate'));
}
}
tests/LanguageNegotiatorTest.php 0000666 00000002653 13436747160 0013063 0 ustar 00 execute(
[
Middleware::LanguageNegotiator($languages),
function ($request, $response, $next) use ($language) {
$response->getBody()->write(LanguageNegotiator::getLanguage($request));
return $response;
},
],
'',
['Accept-Language' => $acceptLanguage]
);
$this->assertEquals($language, (string) $response->getBody());
}
}
tests/MinifyTest.php 0000666 00000002155 13436747160 0010534 0 ustar 00
Title
Hello world!
EOT;
$body_minified = <<TitleHello world!
EOT;
$response = $this->response(['Content-Type' => 'text/html']);
$response->getBody()->write($body);
$middlewares = [
Middleware::Minify(),
];
$response = $this->dispatch($middlewares, $this->request(), $response);
$this->assertEquals($body_minified, (string) $response->getBody());
}
}
tests/FormatNegotiatorTest.php 0000666 00000002420 13436747160 0012560 0 ustar 00 execute(
[
Middleware::FormatNegotiator(),
function ($request, $response, $next) {
$response->getBody()->write(FormatNegotiator::getFormat($request));
return $response;
},
],
$url,
['Accept' => $accept]
);
$this->assertEquals($format, (string) $response->getBody());
}
}
.gitignore 0000666 00000000364 13436747160 0006556 0 ustar 00 # MACOSX
.DS_Store
.AppleDouble
.LSOverride
._*
.Spotlight-V100
.Trashes
# Icon must ends with two \r.
Icon
# WINDOWS
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
# COMPOSER
vendor/
composer.lock
composer.phar