[DEPRECATED] Collection of PSR-7 middlewares

Overview

This package is deprecated in favor of the new PSR-15 standard. Check it out here

psr7-middlewares

Build Status Scrutinizer Code Quality

SensioLabsInsight

Collection of PSR-7 middlewares.

Requirements

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

function (RequestInterface $request, ResponseInterface $response, callable $next) {
    // ...
}

So, you can use these midlewares with:

Installation

This package is installable and autoloadable via Composer as oscarotero/psr7-middlewares.

$ composer require oscarotero/psr7-middlewares

Usage example:

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(),

    //Minify the result
    Middleware::minify(),

    //Handle errors
    Middleware::errorHandler()->catchExceptions(true),

    //Override the method using X-Http-Method-Override header
    Middleware::methodOverride(),

    //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(),

    //Execute fast route
    Middleware::fastRoute($app->get('dispatcher')),
]);

$response = $dispatcher(ServerRequestFactory::fromGlobals(), new Response());

Available middlewares

AccessLog

To generate access logs for each request using the Apache's access log format. This middleware requires a Psr log implementation, for example monolog:

use Psr7Middlewares\Middleware;
use Monolog\Logger;
use Monolog\Handler\ErrorLogHandler;

//Create the logger
$logger = new Logger('access');
$logger->pushHandler(new ErrorLogHandler());

$middlewares = [

    //Required to get the Ip
    Middleware::ClientIp(),

    Middleware::AccessLog($logger) //Instance of Psr\Log\LoggerInterface
        ->combined(true)           //(optional) To use the Combined Log Format instead the Common Log Format
];

AttributeMapper

Maps middleware specific attribute to regular request attribute under desired name:

use Psr7Middlewares\Middleware;

$middlewares = [

    //Example with authentication
    Middleware::BasicAuthentication([
        'username1' => 'password1',
        'username2' => 'password2'
    ]),

    //Map the key used by this middleware
    Middleware::attributeMapper([
        Middleware\BasicAuthentication::KEY => 'auth:username'
    ]),

    function ($request, $response, $next) {
        //We can get the username as usual
        $username = BasicAuthentication::getUsername($request);

        //But also using the "auth:username" attribute name.
        assert($username === $request->getAttribute('auth:username'));

        return $next($request, $response);
    }
];

AuraRouter

To use Aura.Router (3.x) as a middleware:

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\AuraRouter;
use Aura\Router\RouterContainer;

//Create the router
$router = new RouterContainer();

$map = $router->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
$middlewares = [

    Middleware::AuraRouter($router) //Instance of Aura\Router\RouterContainer
        ->arguments($myApp)         //(optional) append more arguments to the controller
];

AuraSession

Creates a new Aura.Session instance with the request.

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\AuraSession;

$middlewares = [

    Middleware::AuraSession(),
        ->factory($sessionFactory) //(optional) Intance of Aura\Session\SessionFactory
        ->name('my-session-name'), //(optional) custom session name

    function ($request, $response, $next) {
        //Get the session instance
        $session = AuraSession::getSession($request);

        return $response;
    }
];

BasePath

Removes 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. You can provide the prefix to remove or let the middleware autodetect it. In the router you can retrieve the prefix removed or a callable to generate more urls with the base path.

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\BasePath;

$middlewares = [

    Middleware::BasePath('/web/public') // (optional) The path to remove...
        ->autodetect(true),             // (optional) ...or/and autodetect the base path

    function ($request, $response, $next) {
        //Get the removed prefix
        $basePath = BasePath::getBasePath($request);

        //Get a callable to generate full paths
        $generator = BasePath::getGenerator($request);

        $generator('/other/path'); // /web/public/other/path

        return $response;
    }
];

BasicAuthentication

Implements the basic http authentication. You have to provide an array with all users and password:

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::BasicAuthentication([
            'username1' => 'password1',
            'username2' => 'password2'
        ])
        ->realm('My realm'), //(optional) change the realm value

    function ($request, $response, $next) {
        $username = BasicAuthentication::getUsername($request);

        return $next($request, $response);
    }
];

BlockSpam

To block referral spam using the piwik/referrer-spam-blacklist list

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::BlockSpam('spammers.txt'), //(optional) to set a custom spammers list instead the piwik's list
];

Cache

Requires micheh/psr7-cache. Saves the responses' headers in cache and returns a 304 response (Not modified) if the request is cached. It also adds Cache-Control and Last-Modified headers to the response. You need a cache library compatible with psr-6.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::Cache(new Psr6CachePool()) //the PSR-6 cache implementation
        ->cacheControl('max-age=3600'),    //(optional) to add this Cache-Control header to all responses
];

ClientIp

Detects the client ip(s).

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\ClientIp;

$middlewares = [

    Middleware::ClientIp()
        ->remote()  // (optional) Hack to get the ip from localhost environment
        ->headers([ // (optional) to change the trusted headers
            'Client-Ip',
            'X-Forwarded-For',
            'X-Forwarded'
        ]),

    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 library:

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::Cors($settings)                 //(optional) instance of Neomerx\Cors\Contracts\Strategies\SettingsStrategyInterface
        ->origin('http://example.com:123')      //(optional) the server origin
        ->allowedOrigins([                      //(optional) Allowed origins
            'http://good.example.com:321' => true,
            'http://evil.example.com:123' => null,
        ])
        ->allowedMethods([                      //(optional) Allowed methods. The second argument forces to add the allowed methods to preflight response
            'GET' => true,
            'PATCH' => null,
            'POST' => true,
            'PUT' => null,
            'DELETE' => true,
        ], true)
        ->allowedHeaders([                      //(optional) Allowed headers. The second argument forces to add the allowed headers to preflight response
            'content-type' => true,
            'some-disabled-header' => null,
            'x-enabled-custom-header' => true,
        ], true)
        ->exposedHeaders([                      //(optional) Headers other than the simple ones that might be exposed to user agent
            'Content-Type' => true,
            'X-Custom-Header' => true,
            'X-Disabled-Header' => null,
        ])
        ->allowCredentials()                    //(optional) If access with credentials is supported by the resource.
        ->maxAge(0)                             //(optional) Set pre-flight cache max period in seconds.
        ->checkHost(true)                       //(optional) If request 'Host' header should be checked against server's origin.
];

Csp

To use the paragonie/csp-builder library to add the Content-Security-Policy header to the response.

$middlewares = [

    Middleware::csp($directives)                          //(optional) the array with the directives.
        ->addSource('img-src', 'https://ytimg.com')       //(optional) to add extra sources to whitelist
        ->addDirective('upgrade-insecure-requests', true) //(optional) to add new directives (if it doesn't already exist)
        ->supportOldBrowsers(false)                       //(optional) support old browsers (e.g. safari). True by default
];

Csrf

To add a protection layer agains CSRF (Cross Site Request Forgery). The middleware injects a hidden input with a token in all POST forms and them check whether the token is valid or not. Use ->autoInsert() to insert automatically the token or, if you prefer, use the generator callable:

$middlewares = [

    //required to save the tokens in the user session
    Middleware::AuraSession(),
    //or
    Middleware::PhpSession(),

    //required to get the format of the request (only executed in html requests)
    Middleware::FormatNegotiator(),

    //required to get the user ip
    Middleware::ClientIp(),

    Middleware::Csrf()
        ->autoInsert(), //(optional) To insert automatically the tokens in all POST forms

    function ($request, $response, $next) {
        //Get a callable to generate tokens (only if autoInsert() is disabled)
        $generator = Middleware\Csrf::getGenerator($request);

        //Use the generator (you must pass the action url)
        $response->getBody()->write(
            '<form action="/action.php" method="POST">'.
            $generator('/action.php').
            '<input type="submit">'.
            '</form>'
        );

        return $next($request, $response);
    }
];

DebugBar

Inserts the PHP debug bar 1.x in the html body. This middleware requires Middleware::formatNegotiator executed before, to insert the debug bar only in Html responses.

use Psr7Middlewares\Middleware;
use DebugBar\StandardDebugBar;

$debugBar = new StandardDebugBar();

$middlewares = [

    Middleware::FormatNegotiator(), //(recomended) to insert only in html responses

    Middleware::DebugBar($debugBar) //(optional) Instance of debugbar
        ->captureAjax(true)         //(optional) To send data in headers in ajax
];

Delay

Delays the response to simulate slow bandwidth in local environments. You can use a number or an array to generate random values in seconds.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::delay(3.5),      //delay the response 3.5 seconds

    Middleware::delay([1, 2.5]), //delay the response between 1 and 1.5 seconds
];

DetectDevice

Uses Mobile-Detect library to detect the client device.

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\DetectDevice;

$middlewares = [

    Middleware::DetectDevice(),

    function ($request, $response, $next) {
        //Get the device info
        $device = DetectDevice::getDevice($request);

        if ($device->isMobile()) {
            //mobile stuff
        }
        elseif ($device->isTablet()) {
            //tablet stuff
        }
        elseif ($device->is('bot')) {
            //bot stuff
        }

        return $next($request, $response);
    },
];

DigestAuthentication

Implements the digest http authentication. You have to provide an array with the users and password:

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::DigestAuthentication([
            'username1' => 'password1',
            'username2' => 'password2'
        ])
        ->realm('My realm') //(optional) custom realm value
        ->nonce(uniqid()),   //(optional) custom nonce value

    function ($request, $response, $next) {
        $username = DigestAuthentication::getUsername($request);

        return $next($request, $response);
    }
];

EncodingNegotiator

Uses willdurand/Negotiation (2.x) to detect and negotiate the encoding type of the document.

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\EncodingNegotiator;

$middlewares = [

    Middleware::EncodingNegotiator()
        ->encodings(['gzip', 'deflate']), //(optional) configure the supported encoding types

    function ($request, $response, $next) {
        //get the encoding (for example: gzip)
        $encoding = EncodingNegotiator::getEncoding($request);

        return $next($request, $response);
    }
];

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.

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\ErrorHandler;

function handler($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'
    }
}

$middlewares = [

    Middleware::ErrorHandler('handler') //(optional) The error handler
        ->arguments($myApp)             //(optional) extra arguments to the handler
        ->catchExceptions()             //(optional) to catch exceptions
        ->statusCode(function ($code) { //(optional) configure which status codes you want to handle with the errorHandler
            return $code >= 400 && $code < 600 && $code != 422; // you can bypass some status codes in case you want to handle it
        })
];

Expires

Adds Expires and max-age directive of the Cache-Control header in the response. It's similar to the apache module mod_expires. By default uses the same configuration than h5bp apache configuration. Useful for static files.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::formatNegotiator(), //(recomended) to detect the content-type header

    Middleware::expires()
        ->addExpire('text/css', '+1 week') //Add or edit the expire of some types
];

FastRoute

To use FastRoute as middleware.

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');
    });
});

$middlewares = [

    Middleware::FastRoute($router) //Instance of FastRoute\Dispatcher
        ->argument($myApp)         //(optional) arguments appended to the controller
];

Firewall

Uses M6Web/Firewall to provide an IP filtering. This middleware depends on ClientIp (to extract the ips from the headers).

See the ip formats allowed for trusted/untrusted options:

use Psr7Middlewares\Middleware;

$middlewares = [

    //required 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
];

FormatNegotiator

Uses willdurand/Negotiation (2.x) 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.

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\FormatNegotiator;

$middlewares = [

    Middleware::FormatNegotiator()
        ->defaultFormat('html') //(optional) default format if it's unable to detect. (by default is "html")
        ->addFormat('tiff', ['image/tiff', 'image/x-tiff']), //(optional) add a new format associated with mimetypes

    function ($request, $response, $next) {
        //get the format (for example: html)
        $format = FormatNegotiator::getFormat($request);

        return $next($request, $response);
    }
];

You can optionally specify the formats which your server supports, in priority order, with the first element being the default.

//This will only negotiate html, pdf and xml. html is the default.
Middleware::FormatNegotiator([
    'html' => [['html', 'htm', 'php'], ['text/html', 'application/xhtml+xml']],
    'pdf' => [['pdf'], ['application/pdf', 'application/x-download']],
    'xml' => [['xml'], ['text/xml', 'application/xml', 'application/x-xml']]
])

If the client requests a format which is not supported by the server, then the default format will be used. If you wish to generate a 406 Not Acceptable response instead, set the default format to null.

//This will generate a 406 Not Acceptable response if the client requests anything other than html.
Middleware::FormatNegotiator([
        'html' => [['html', 'htm', 'php'], ['text/html', 'application/xhtml+xml']]
    ])
    ->defaultFormat(null)

FormTimestamp

Simple spam protection based on injecting a hidden input in all post forms with the current timestamp. On submit the form, check the time value. If it's less than (for example) 3 seconds ago, assumes it's a bot, so returns a 403 response. You can also set a max number of seconds before the form expires.

use Psr7Middlewares\Middleware;

$middlewares = [

    //(recomended) to detect html responses
    Middleware::FormatNegotiator(),

    Middleware::FormTimestamp()
        ->key('my-secret-key'),   //Key used to encrypt/decrypt the input value.
        ->min(5)                  //(optional) Minimum seconds needed to validate the request (default: 3)
        ->max(3600)               //(optional) Life of the form in second. Default is 0 (no limit)
        ->inputName('time-token') //(optional) Name of the input (default: hpt_time)
        ->autoInsert(),           //(optional) To insert automatically the inputs in all POST forms

    function ($request, $response, $next) {
        //Get a callable to generate the inputs (only if autoInsert() is disabled)
        $generator = Middleware\FormTimestamp::getGenerator($request);

        //Use the generator (you must pass the action url)
        $response->getBody()->write(
            '<form action="/action.php" method="POST">'.
            $generator().
            '<input type="submit">'.
            '</form>'
        );

        return $next($request, $response);
    }
];

Geolocate

Uses Geocoder library to geolocate the client using the ip. This middleware depends on ClientIp (to extract the ips from the headers).

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\Geolocate;

$middlewares = [

    //(optional) only if you want to save the result in the user session
    Middleware::PhpSession(),
    //or
    Middleware::AuraSession(),


    //required to capture the user ips before
    Middleware::ClientIp(),

    Middleware::Geolocate($geocoder) //(optional) To provide a custom Geocoder instance
        ->saveInSession(),           //(optional) To save the result to reuse in the future requests (required a session middleware before)

    function ($request, $response, $next) {
        //get the location
        $addresses = Geolocate::getLocation($request);

        //get the country
        $country = $addresses->first()->getCountry();

        $response->getBody()->write('Hello to '.$country);

        return $next($request, $response);
    }
];

GoogleAnalytics

Inject the Google Analytics code in all html pages.

use Psr7Middlewares\Middleware;

$middlewares = [

    //(recomended) to detect html responses
    Middleware::formatNegotiator(),

    Middleware::GoogleAnalytics('UA-XXXXX-X') //The site id
];

Gzip

Use gzip functions to compress the response body, inserting also the Content-Encoding header.

use Psr7Middlewares\Middleware;

$middlewares = [

    //required to get the preferred encoding type
    Middleware::EncodingNegotiator(),

    Middleware::Gzip()
];

Honeypot

Implements a honeypot spam prevention. This technique is based on creating a input field that should be invisible and left empty by real users but filled by most spam bots. The middleware scans the html code and inserts this inputs in all post forms and check in the incoming requests whether this value exists and is empty (is a real user) or doesn't exist or has a value (is a bot) returning a 403 response.

use Psr7Middlewares\Middleware;

$middlewares = [

    //(recomended) to detect html responses
    Middleware::formatNegotiator(),

    Middleware::Honeypot()
        ->inputName('my_name') //(optional) The name of the input field (by default "hpt_name")
        ->inputClass('hidden') //(optional) The class of the input field (by default "hpt_input")
        ->autoInsert(),        //(optional) To insert automatically the inputs in all POST forms

    function ($request, $response, $next) {
        //Get a callable to generate the inputs (only if autoInsert() is disabled)
        $generator = Middleware\Honeypot::getGenerator($request);

        //Use the generator (you must pass the action url)
        $response->getBody()->write(
            '<form action="/action.php" method="POST">'.
            $generator().
            '<input type="submit">'.
            '</form>'
        );

        return $next($request, $response);
    }
];

Https

Returns a redirection to the https scheme if the request uri is http. It also adds the Strict Transport Security header to protect against protocol downgrade attacks and cookie hijacking.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::Https(true)   //(optional) True to force https, false to force http (true by default)
        ->maxAge(1000000)     //(optional) max-age directive for the Strict-Transport-Security header. By default is 31536000 (1 year)
        ->includeSubdomains() //(optional) To add the "includeSubDomains" attribute to the Strict-Transport-Security header.
];

ImageTransformer

Uses imagecow/imagecow 2.x to transform images on demand. You can resize, crop, rotate and convert to other formats. Use the the imagecow syntax to define the available sizes.

To define the available sizes, you have to asign a filename prefix representing the size, so any file requested with this prefix will be dinamically transformed.

There's also support for Client hints to avoid to serve images larger than needed (currently supported only in chrome and opera).

If you want to save the transformed images in the cache, provide a library compatible with psr-6 for that.

use Psr7Middlewares\Middleware;

$middlewares = [

    //(recomended) to detect responses' mimetype
    Middleware::formatNegotiator(),

    Middleware::imageTransformer([   // The available sizes of the images.
            'small.' => 'resizeCrop,50,50', //Creates a 50x50 thumb of any image prefixed with "small." (example: /images/small.avatar.jpg)
            'medium.' => 'resize,500|format,jpg', //Resize the image to 500px and convert to jpg
            'pictures/large.' => 'resize,1000|format,jpg', //Transform only images inside "pictures" directory (example: /images/pcitures/large.avatar.jpg)
        ])
        ->clientHints()              // (optional) To enable the client hints headers
        ->cache(new Psr6CachePool()) // (optional) To save the transformed images in the cache

    function ($request, $response, $next) {
        //Get the generator to generate urls
        $generator = Middleware\ImageTransformer::getGenerator($request);

        //Use the generator
        $response->getBody()->write('<img src="'.$generator('images/picture.jpg', 'small.').'">');

        return $next($request, $response);
    }
];

IncludeResponse

Useful to include old style applications, in which each page has it's own php file. For example, let's say we have an application with paths like /about-us.php or /about-us (resolved to /about-us/index.php), this middleware gets the php file, include it safely, capture the output and the headers send and create a response with the results. If the file does not exits, returns a 404 response (unless continueOnError is true).

use Psr7Middlewares\Middleware;

$middlewares = [
    Middleware::includeResponse('/doc/root'), //The path of the document root
        ->continueOnError(true)               // (optional) to continue with the next middleware on error or not
];

JsonValidator

Uses justinrainbow/json-schema to validate an application/json request body with a JSON schema:

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\JsonValidator;

// Validate using a file:
$middlewares = [
    Middleware::payload(['forceArray' => false]),
    JsonValidator::fromFile(new \SplFileObject(WEB_ROOT . '/json-schema/en.v1.users.json')),
];

// Validate using an array:
$middlewares = [
    Middleware::payload(['forceArray' => false]),
    JsonValidator::fromArray([
        '$schema' => 'http://json-schema.org/draft-04/schema#',
        'type' => 'object',
        'properties' => [
            'id' => [
                'type' => 'string'
            ],
        ],
        'required' => [
            'id',
        ]
    ]);
];

// Override the default error handler, which responds with a 422 status code and application/json Content-Type:
$middlewares = [
    Middleware::payload(['forceArray' => false]),
    JsonValidator::fromFile(new \SplFileObject('schema.json'))
        ->setErrorHandler(function ($request, $response, array $errors) {
            $response->getBody()->write('Failed JSON validation.');

            return $response->withStatus(400, 'Oops')
                ->withHeader('Content-Type', 'text/plain');
        }),
];

JsonSchema

Uses justinrainbow/json-schema to validate an application/json request body using route-matched JSON schemas:

use Psr7Middlewares\Middleware;

$middlewares = [

    // Transform `application/json` into an object, which is a requirement of `justinrainbow/json-schema`.
    Middleware::payload([
        'forceArray' => false,
    ]),

    // Provide a map of route-prefixes to JSON schema files.
    Middleware::jsonSchema([
        '/en/v1/users' => WEB_ROOT . '/json-schema/en.v1.users.json',
        '/en/v1/posts' => WEB_ROOT . '/json-schema/en.v1.posts.json',
        '/en/v2/posts' => WEB_ROOT . '/json-schema/en.v2.posts.json',
    ])
];

LanguageNegotiation

Uses willdurand/Negotiation to detect and negotiate the client language using the Accept-Language header and (optionally) the uri's path. You must provide an array with all available languages:

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\LanguageNegotiator;

$middlewares = [

    Middleware::LanguageNegotiator(['gl', 'en']) //Available languages
        ->usePath(true)                          //(optional) To search the language in the path: /gl/, /en/
        ->redirect()                             //(optional) To return a redirection if the language is not in the path

    function ($request, $response, $next) {
        //Get the preferred language
        $language = LanguageNegotiator::getLanguage($request);

        return $next($request, $response);
    }
];

LeagueRoute

To use league/route (2.x) as a middleware:

use Psr7Middlewares\Middleware;
use League\Route\RouteCollection;

$router = new RouteCollection();

$router->get('/blog/{id:[0-9]+}', function ($request, $response, $vars) {
    return 'This is the post number'.$vars['id'];
});

$middlewares = [

    Middleware::LeagueRoute($router) //The RouteCollection instance
];

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:

use Psr7Middlewares\Middleware;

$middlewares = [

    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
        ->parameter('method-override') //(optional) to use a parsed body and uri query parameter in addition to the header
        ->parameter('method-override', false) //(optional) to use only the parsed body (but not the uri query)
];

Minify

Uses mrclay/minify to minify the html, css and js code from the responses.

use Psr7Middlewares\Middleware;

$middlewares = [

    //(recomended) to detect the mimetype of the response
    Middleware::formatNegotiator(),

    Middleware::Minify()
];

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.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::Payload([     // (optional) Array of parsing options:
        'forceArray' => false // Force to use arrays instead objects in json (true by default)
    ])
    ->override(),             // (optional) To override the existing parsed body if exists (false by default)

    function ($request, $response, $next) {
        //Get the parsed body
        $content = $request->getParsedBody();

        return $next($request, $response);
    }
];

PhpSession

Initializes a php session using the request data.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::PhpSession()
        ->name('SessionId') //(optional) Name of the session
        ->id('ABC123')      //(optional) Id of the session

    function ($request, $response, $next) {
        //Use the global $_SESSION variable to get/set data
        $_SESSION['name'] = 'John';

        return $next($request, $response);
    }
];

Piwik

To use the Piwik analytics platform. Injects the javascript code just before the </body> closing tag.

use Psr7Middlewares\Middleware;

$middlewares = [

    //(recomended) to detect html responses
    Middleware::formatNegotiator(),

    Middleware::Piwik()
        ->piwikUrl('//example.com/piwik')    // The url of the installed piwik
        ->siteId(1)                          // (optional) The site id (1 by default)
        ->addOption('setDoNotTrack', 'true') // (optional) Add more options to piwik API
];

ReadResponse

Read the response content from a file. It's the opposite of SaveResponse. The option continueOnError changes the behaviour of the middleware to continue with the next middleware if the response file is NOT found and returns directly the response if the file is found. This is useful to use the middleware as a file based cache and add a router middleware (or other readResponses) next in the queue.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::ReadResponse('path/to/files') // Path where the files are stored
        ->appendQuery(true)                   // (optional) to use the uri query in the filename
        ->continueOnError(true)               // (optional) to continue with the next middleware on error or not
];

Recaptcha

To use the google recaptcha library for spam prevention.

use Psr7Middlewares\Middleware;

$middlewares = [

    //required to get the user IP
    Middleware::ClientIp(),

    Middleware::Recaptcha('secret') //The secret key
];

Rename

Renames the request path. This is useful in some use cases:

  • To rename public paths with random suffixes for security reasons, for example the path /admin to a more unpredictible /admin-19640983
  • Create pretty urls without use any router. For example to access to the path /static-pages/about-me.php under the more friendly /about-me

Note that the original path wont be publicly accesible. On above examples, requests to /admin or /static-pages/about-me.php returns 404 responses.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::Rename([
        '/admin' => '/admin-19640983',
    ]),

    function ($request, $response, $next) {
        $path = $request->getUri()->getPath(); // /admin

        return $next($request, $response);
    }
];

ResponseTime

Calculates the response time (in miliseconds) and saves it into X-Response-Time header:

use Psr7Middlewares\Middleware;

$middlewares = [

    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.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::Robots(false) //(optional) Set true to allow search engines instead disallow
];

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

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::SaveResponse('path/to/files') //Path directory where save the responses
        ->appendQuery(true)                   // (optional) to append the uri query to the filename
];

Shutdown

Useful to display a 503 maintenance page. You need to specify a handler.

use Psr7Middlewares\Middleware;

function shutdownHandler ($request, $response, $app) {
    $response->getBody()->write('Service unavailable');
}

$middlewares = [

    Middleware::Shutdown('shutdownHandler') //(optional) 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.

use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::TrailingSlash(true) //(optional) set true to add the trailing slash instead remove
        ->redirect(301)             //(optional) to return a 301 (seo friendly) or 302 response to the new path
];

Uuid

Uses ramsey/uuid (3.x) to generate an Uuid (Universally Unique Identifiers) for each request (compatible with RFC 4122 versions 1, 3, 4 and 5). It's usefull for debugging purposes.

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\Uuid;

$middlewares = [

    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);
    }
];

Whoops

To use whoops 2.x as error handler.

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Middleware\Whoops;
use Whoops\Run;

$whoops = new Run();

$middlewares = [

    //(recomended) to allows to choose the best handler according with the response mimetype
    Middleware::formatNegotiator(),

    Middleware::Whoops($whoops) //(optional) provide a custom whoops instance
        ->catchErrors(false)    //(optional) to catch not only exceptions but also php errors (true by default)
];

Www

Adds or removes the www subdomain in the host uri and, optionally, returns a redirect response. The following types of host values wont be changed:

  • The one word hosts, for example: http://localhost.
  • The ip based hosts, for example: http://0.0.0.0.
  • The multi domain hosts, for example: http://subdomain.example.com.
use Psr7Middlewares\Middleware;

$middlewares = [

    Middleware::Www(true) //(optional) Add www instead remove it
        ->redirect(301)   //(optional) to return a 301 (seo friendly), 302 response to the new host or false to don't redirect. (301 by default)
];

Lazy/conditional middleware creation

You may want to create middleware in a lazy way under some circunstances:

  • The middleware is needed only in a specific context (for example in development environments)
  • The middleware creation is expensive and is not needed always (because a previous middleware returns a cached response)
  • The middleware is needed only in a specific path

To handle with this, you can use the Middleware::create() method that must return a callable or false. Example:

use Psr7Middlewares\Middleware;

$middlewares = [

    //This middleware can return a cached response
    //so the next middleware may not be executed
    Middleware::cache($myPsr6CachePool),

    //Let's say this middleware is expensive, so use a proxy for lazy creation
    Middleware::create(function () use ($app) {
        return Middleware::auraRouter($app->get('router'));
    }),

    //This middleware is needed only in production
    Middleware::create(function () {
        return (getenv('ENV') !== 'production') ? false : Middleware::minify();
    }),

    //This middleware is needed in some cases
    Middleware::create(function ($request, $response) {
        if ($request->hasHeader('Foo')) {
            return Middleware::www();
        }

        return false;
    }),

    //This middleware is needed only in a specific basePath
    Middleware::create('/admin', function () {
        return Middleware::DigestAuthentication(['user' => 'pass']);
    }),

    //This middleware is needed in some cases under a specific basePath
    Middleware::create('/actions', function ($request, $response) {
        if ($request->hasHeader('Foo')) {
            return Middleware::responseTime();
        }

        return false;
    }),
];

Extending middlewares

Some middleware pieces use different functions to change the http messages, depending of some circunstances. For example, Payload parses the raw body content, and the method used depends of the type of the content: it can be json, urlencoded, csv, etc. Other example is the Minify middleware that needs a different minifier for each format (html, css, js, etc), or the Gzip that depending of the Accept-Encoding header, use a different method to compress the response body.

The interface Psr7Middlewares\Transformers\ResolverInterface provides a way to resolve and returns the apropiate "transformer" in each case. The transformer is just a callable with a specific signature. You can create custom resolvers or extend the included in this package to add your owns. Let's see an example:

use Psr7Middlewares\Middleware;
use Psr7Middlewares\Transformers\BodyParser;
use Psr\Http\Message\ServerRequestInterface;

class MyBodyParser extends BodyParser
{
    /**
     * New parser used in request with the format "php"
     */
    public function php(ServerRequestInterface $request)
    {
        $data = unserialize((string) $request->getBody());

        return $request->withParsedBody($data);
    }
}

//Use the resolver
$middlewares = [
    Middleware::Payload()->resolver(new MyBodyParser())
];

The following middlewares are using resolvers that you can customize:

  • Payload To parse the body according with the format (json, urlencoded, csv, ...)
  • Gzip To encode the body with the encoding method supported by the browser (gzip, deflate)
  • Minify To use different minifiers for each format (html, css, js, ...)

Contribution

New middlewares are appreciated. Just create a pull request.

Comments
  • Remove dependency on container-interop

    Remove dependency on container-interop

    I highly recommend adopting the Resolver pattern instead of depending container-interop. The interface looks like this:

    interface Resolver
    {
        /**
         * @param string
         * @return object
         */
        public function resolve($spec);
    }
    
    opened by shadowhand 37
  • add middleware to validate JSON request body with schema

    add middleware to validate JSON request body with schema

    add JsonSchema middleware to validate request body using route-matched JSON schema files.

    uses justinrainbow/json-schema:>=3.0. earlier versions resolve schema files differently and i chose not support it. i'd be happy to add support if requested.

    route-matching is performed by prefix, e.g.:

    Middleware::JsonSchema([
        '/en/v1/users' => WEB_ROOT . '/json-schema/en.v1.users.json',
        '/en/v1/posts' => WEB_ROOT . '/json-schema/en.v1.posts.json',
        '/en/v2/posts' => WEB_ROOT . '/json-schema/en.v2.posts.json',
    ]);
    

    failed validation and malformed JSON will result in a short-circuited 422 response.

    should the status code, message, and/or short-circuit behavior be configurable?

    opened by abacaphiliac 17
  • Make all middleware immutable

    Make all middleware immutable

    There are a number of configuration methods in these middleware that prevent them from being treated as immutable, for example Payload::associative which could be written as:

    public function withAssociative($setting)
    {
        $copy = clone $this;
        $copy->associative = (bool) $setting;
        return $copy;
    }
    
    opened by shadowhand 15
  • allow JSON-validator default error-handler to be overridden.

    allow JSON-validator default error-handler to be overridden.

    based on some feedback here: https://github.com/oscarotero/psr7-middlewares/pull/46

    the error handler receives the psr request, response, and an array of validation errors. the default error handler responds with a 422 status code, application/json content-type, and the json-encoded array of errors.

    the route-based multi-file wrapper also accepts an error handler override, which is passed through to the underlying validator.

    @oscarotero @sbol-coolblue please tell me what you think.

    opened by abacaphiliac 12
  • split a simpler json-schema validator from the route-based-validator

    split a simpler json-schema validator from the route-based-validator

    split a simpler json-schema validator from the multi-validator. the simple validator handles a single schema and there is no route-based logic. the file-per-route implementation consumes the simple validator.

    opened by abacaphiliac 12
  • https middleware

    https middleware

    Hello,

    Actually it's not possible to activate or deactivate https middleware by passing an argument like for www middleware does :

    Middleware::Www(true)

    This is really useful when switching from a development to production environment.

    Is it possible to add this function?

    Thank you so much.

    opened by jimblue 10
  • HTTPS terminated at load balancer

    HTTPS terminated at load balancer

    So I found the following issue when implementing my site. I had originally tested it on servers that were using local self-signed certs, so everything was great.

    $app->add(Middleware::TrailingSlash(false)->redirect(301));
    $app->add(Middleware::Https());
    

    HTTPS Because I moved to a system that was terminating the SSL on the load balancer the HTTPS middleware was causing infinite redirects. Its not really a bug but took me a hot second to figure out what the heck was going on.

    TrailingSlash Then because the SSL is terminated when TrailingSlash would fire, it would send the redirect back but with an http:// protocol because that is how the request came in. As far as I'm aware the following headers are somewhat of a standard for this type of situation. Do you think it would be prudent to look for these headers and if the they exist change the URI? You could maybe if just add it as a chained method that would checkHTTPSForward or something.

    HTTP_X_FORWARDED_PROTO: https
    HTTP_X_FORWARDED_PORT: 443
    
    opened by mdevine82 9
  • Would traits better replace the static class?

    Would traits better replace the static class?

    I am assuming that the Middleware class is basically a collection of functions, which are collected into a class instance?

    $request = Middleware::setAttribute($request, self::KEY, $this->basePath);
    
    // better as
    
    use RequestAttributesHelperTrait;
    
    $request = $this->setRequestAttribute($request, self::KEY, $this->basePath);
    

    Using this as an example, it would be (IMO) better to have a set of traits to allow this functionality to be implanted into middlewares, rather than relying on a static class. Obviously backwards compatibility is an issue, so the static class can use the traits and still implement it's own methods.

    When it comes to the factory method, I think an abstract factory class would be more apt, which can also have a helper trait to allow injecting itself into middleware instances that require it.

    What do you think about an architectural change like this? Being that it is such a large change, it could be argued into a version release, but having a good backwards compatibility, it could be a feature release. I don't mind working on it, as I really like this codebase and want to use it, but I just can't bring myself to allow statics into my codebase :D

    opened by designermonkey 9
  • Filter list of acceptable headers; use default if the client accepts everything

    Filter list of acceptable headers; use default if the client accepts everything

    Added a priorities member variable, which allows the dev to filter the list of acceptable headers.

    Usage:

    //Code
    Middleware::FormatNegotiator()
        ->setPriorities(['json', 'zip'])
        ->defaultFormat('json')
    
    //Request header
    Accept: application/pdf, application/zip
    
    //Result: 
    FormatNegotiator::getFormat($request) === "zip"
    

    getFromHeader will return null if the accept header is either missing, or set to accept everything. This will allow the default to be used instead.

    Usage:

    //Code
    Middleware::FormatNegotiator()
        ->defaultFormat('json')
    
    //Request header
    Accept: */*
    
    //Result: 
    FormatNegotiator::getFormat($request) === "json"
    
    opened by mlambley 8
  • Pass the stream you're replacing to the `createStream` method.

    Pass the stream you're replacing to the `createStream` method.

    This method will try to use the same stream resource uri and will fall back to php://temp. The stream that is being replaced is also passed to the factory as third argument. This will allow the factory to create a new stream with the same class (or affect it in some other way).

    opened by jasny 8
  • Bug: 502 bad gateway on 3.10

    Bug: 502 bad gateway on 3.10

    The new update got installed with composer update. But I got a strange 502 bad gateway error for my ajax requests.

    I couldn't figure out the problem at first, so I tried to rollback some composer updates and it looks like that the problem is gone when I rolled back to the previous 3.9.3 version.

    The strange thing is that normal (none-ajax) request are doing fine.

    This is my dispatcher with the used middleware. Please let me know if you need more information.

    $relay = new RelayBuilder();
    
    $dispatcher = $relay->newInstance([
    
        Middleware::FormatNegotiator(),
    
        //Adds the php debug bar
        Middleware::debugBar($debugbar),
    
        Middleware::LeagueRoute()
            ->router($router) //The RouteCollection instance
    ]);
    
    $response = $dispatcher(ServerRequestFactory::fromGlobals(), new Response());
    
    (new Zend\Diactoros\Response\SapiEmitter)->emit($response);
    
    
    opened by matthijsthoolen 8
  • JSON Schema incompatible with Slim Framework

    JSON Schema incompatible with Slim Framework

    The JSON Schema middleware does not work with Slim Framework. Reason is that it relies on the Payload middleware to have getParsedBody to yield a stdClass object, using the newly added forceArray=false option on Middleware\Payload.

    Slim's Request object has body parsing built-in. Unfortunately they force the JSON payload into an associative array (as documented in their manual) Our Payload MW currently does the following check to decide whether or not we should parse the body:

    if (*!$request->getParsedBody()* && in_array($request->getMethod(), [...]

    Since getParsedBody will hit Slim's version, we're basically stuck with whatever parsed body they provide. I've come up with three ways to deal with this:

    Option 1: "Not our problem", have the developer deal with it in their Framework. In effect this would mean registering a custom "body parser" in Slim using registerMediaTypeParser to overwrite the default parser for application/json media types.

    Option 2: Instead of bailing out in JSON Schema if the parsed body is not an object, do an if-array-then-cast-to-object, which will yield the stdClass we need.

    Option 3: Fix it in Middleware::Payload by (optionally?) having it overwrite previously parsed bodies. Slight loss of efficiency, but ultimately it does give us more control over the request.

    Would like your opinion on this before forging ahead with a PR. My preference would be option 3, under the following rationale: By adding the Payload middleware to your stack you're explicitly handing us responsibility to handle the request body, since apparently your current stack is not capable of dealing with the provided payload... it would make sense then not to rely on any previous parsed body contents.

    I'll also raise this issue over at Slim to get their opinion on this.

    opened by sbol-coolblue 5
  • Split into separate discrete packages?

    Split into separate discrete packages?

    I had some recollection of this being discussed, but I don't see it in the issue tracker, so I figure I'd raise it here. I assume there has been some discussion on this, so at minimum, I think it would be nice to have the record of it here.

    Firstly, I'm a fan of minimalism and the 'do one thing and do it right' ethos. In my opinion, it would be nice to have this collection split into separate, discrete packages. Things like the Authentication middleware might be grouped together, but things of disparate concerns with varying requirements could/should be separate.

    I think the most obvious reason is that a handful of these have real requirements (i.e. AuraRouter), which are only listed as suggestions. The ability to actually require the dependency (including version requirements) seems like an obvious benefit.

    Additionally, it just doesn't feel right to me to require the whole thing to use a single component. It would be nice to glance at componser.json and have at least some idea of what functionality is being leveraged. It's also strange to continually get updates which are possibly unrelated to the utilized functionality.

    I also assume (possibly without cause) that release versioning is/will-become cumbersome with this paradigm. Given two components of unrelated concerns, should not one be able to change it's public API and require a 1.0 release without affecting the other?

    I would register my vote for separate vendor prefix with discrete packages (eg oscarware/aura-router, oscarware/auth, etc.). If absolutely necessary, they could require a common utility package. For those desiring the "all-in-one" paradigm, an additional package could be created which simply requires the discrete ones.

    Thoughts?

    opened by jakejohns 21
  • Middleware request: Barebones SSO Client

    Middleware request: Barebones SSO Client

    Although I am not sure that this can be implemented.

    I use Barebones SSO for most of my projects. So it would be nice if I can implement this with middleware.

    Link: https://barebonescms.com/documentation/sso/

    Feature request 
    opened by matthijsthoolen 5
Releases(v3.21.1)
  • v3.21.1(Sep 28, 2017)

  • v3.21.0(Aug 27, 2017)

    • Added an option to prepend namespaces to override middlewares #78
    • Fixed phpunit tests #82
    • Improved FormatNegotiator #80 #81:
      • Fixed the behaviour of the default format in some circunstances
      • Allow to disable the default format returning a 406 response if no format has been found
      • Allow to customize the list of available formats
    Source code(tar.gz)
    Source code(zip)
  • v3.20.0(Mar 23, 2017)

    • Added support for proxies in ClientIp. This changes the default behaviour of this middleware. More info #65
    • Added an argument to Https to invert the behaviour of the middleware #73
    Source code(tar.gz)
    Source code(zip)
  • v3.19.0(Jan 4, 2017)

    • Robots: Use Content-Type: text/plain for robots.txt #47
    • Fixed a conflict with the version of fast-route and league/route #53
    • New JsonValidator middleware, to validate the json scheme of the request's body. It's like the JsonSchema middleware but without the router functionality. #46 #58
    • BasicAuthentication::checkUserPassword() is protected, allowing to override it. #60
    • New option Cors::logger() to provide a psr-3 logger instance to debugging #61
    • Fixed Csrf in php 5 #62
    • New option Payload::override() to override the previous parsed body if exists.
    Source code(tar.gz)
    Source code(zip)
  • v3.18.0(Nov 5, 2016)

    • New middleware JsonSchema by @abacaphiliac #44 to validate application/json request
    • Payload Added an argument to provide an array of parsing options (thanks @abacaphiliac )
    • Added the missing documentation for IncludeResponse
    Source code(tar.gz)
    Source code(zip)
  • v3.17.0(Oct 12, 2016)

    • Https: New option Https::checkHttpsForward() to check the headers X-Forwarded-Proto: https or X-Forwarded-Port: 443
    • Https: Adds automatically the https protocol to the Location header in redirect responses to avoid infinite redirections #41
    • The stream factory receives a third argument with the stream object to replace #42
    Source code(tar.gz)
    Source code(zip)
  • v3.16.2(Sep 2, 2016)

  • v3.16.1(Jun 10, 2016)

    ErrorHandler: added a statusCode option to filter the status code handled by the middleware. Example:

    Middleware::errorHandler('myHandler')
        ->statusCode(function ($code) {
            return $code === 500;
        });
    
    Source code(tar.gz)
    Source code(zip)
  • v3.16.0(May 29, 2016)

    New middlewares

    • IncludeResponse

    Fixes

    • SaveResponse: do not save responses with the header location
    • FormatNegotiator: removed duplicated code that inserts the Content-Type header twice
    • DebugBar: removed unnecesary FormatNegotiator dependency
    • Fixed some bugs reported by @ircmaxell in this post
    • ErrorHandler: created an empty body before execute the callback #35
    Source code(tar.gz)
    Source code(zip)
  • v3.15.1(May 6, 2016)

  • v3.15.0(Apr 21, 2016)

    Added

    Changed

    • The FormatNegotiator middleware is no longer required with other middlewares, since they use the Content-Type header instead the format name.
    • ReadResponse opens the stream in read-only mode, to prevent changes in the file content
    • FormatNegotiator adds the Content-Type header before the next middleware (and after, if it's missing again)
    • The default handler of ErrorHandler supports many formats (images, json, xml, svg, etc)
    • Fixed MethodOverride when the two methods (original and new) are the same.
    Source code(tar.gz)
    Source code(zip)
  • v3.14.3(Mar 31, 2016)

    FormatNegotiator:

    • Added more formats by default
    • Allow to define multiple extensions associated with one format (for example: jpg, jpeg and jpe).

    Whoops

    • Improved Handler detection with txt, css and js formats
    Source code(tar.gz)
    Source code(zip)
  • v3.14.2(Mar 11, 2016)

  • v3.14.1(Mar 1, 2016)

    Bugs and minor improvements:

    • Whoops: Ensure all buffers are discarded before outputting whoops (thanks @brad-jones). #32
    • Whoops: Fixed php7 support (catch Throwable objects)
    • ErrorHandler: Fixed php7 support (catch Throwable objects). Improved the style of the default handler.
    • Shutdown: Use a sans-serif font by default.
    • Uuid: Included the X-Uuid header in the response.
    Source code(tar.gz)
    Source code(zip)
  • v3.14.0(Feb 26, 2016)

    BasePath

    Previous versions provide the basePath option in some middleware pieces (saveResponse, readResponse, languageNegotiator, etc). For simplicity and to avoid repeat code, this option was removed. Now you can use the basePath middleware that provides a path generator to create full paths:

    use Psr7Middlewares\Middleware;
    use Psr7Middlewares\Middleware\BasePath;
    
    $middlewares = [
        Middleware::basePath('/my-site/public'),
    
        Middleware::trailinSlash(),
    
        function ($request, $response, $next) {
            $basePath = BasePath::getBasePath($request); // returns /my-site/public
    
            $builder = BasePath::getPathBuilder($request);
            $link = $builder('fancy/path'); // generates /my-site/public/fancy/path
    
            return $next($request, $response);
        }
    ];
    

    The middleware pieces affected by this change (dropped the basepath option) are:

    • LanguageNegotiator
    • TrailinSlash
    • ReadResponse
    • SaveResponse
    • ImageTransformer

    Storage

    Added the ability to use the session data of PhpSession and AuraSession in other middlewares, creating and sharing a subarray in the request attributes. This change affects to Csrf that no longer need to pass $_SESSION array in the constructor.

    Automatic injection

    Some middleware pieces like Csrf, Honeypot, etc inject automatically the inputs in post forms. This feature is disabled by default, in order to improve the performance. Now you can get a callable in your router to generate automatically these inputs:

    $middlewares = [
        Middleware::Honeypot(),
        function ($request, $response, $next) {
            $generator = Middleware\Honeypot::getGenerator($request);
    
            $inputs = $generator();
    
           $response->getBody()->write('<form action="/action.php" method="POST">'.$inputs.'<input type="submit"></form>');
    
            return $next($request, $response);
        }
    ];
    

    This change affects to the following middlewares:

    • Csrf
    • Honeypot
    • FormTimestamp

    You can enable the automatic injection again using the option ->autoInsert(true)

    New additions and fixes

    • MethodOverride Added ->parameter() option #28
    • ReadResponse Added ->continueOnError() option.
    • Geolocate New option ->saveInSession() to reuse the same geolocation result in all requests.
    • ImageTransformer Added a generator callable to generate the images paths.
    • Allow to register new middleware namespaces using Psr7Middlewares\Middleware::registerNamespace($namespace)
    • Robots Do not execute the next middleware if the path is "/robots.txt"
    • Middleware::create() now accepts a base path as first argument

    Internal changes (not should affects)

    • Moved some static methods of Psr7Middlewares\Middleware to traits (getAttribute, setAttribute, hasAttribute, createStream)
    • Changed some transformers to be more independent
    Source code(tar.gz)
    Source code(zip)
  • v3.13.2(Feb 6, 2016)

  • v3.13.1(Feb 6, 2016)

  • v3.13.0(Jan 31, 2016)

    New middlewares

    Improvements

    • New static methods BasicAuthentication::getUsername() and DigestAuthentication::getUsername() to get the name of the logged user (#21, thanks @wolfy-j)
    • Improved whoops in cli and xml requests, and upgraded to 2.x

    Fixes

    • Fixed psr-6 implementation in Cache
    • Fixed ImageTransformer bug that added Accept-CH header to all responses
    Source code(tar.gz)
    Source code(zip)
  • v3.12.4(Jan 30, 2016)

    Fixed execution order to be compatible with Zend Expressive and Slim. More info #20 This change affects to:

    • Gzip
    • Minify
    • FormatNegotiator
    • AccessLog
    • Csp
    • GoogleAnalytics
    • ImageTransformer
    • Piwik
    Source code(tar.gz)
    Source code(zip)
  • v3.12.3(Jan 24, 2016)

  • v3.12.2(Jan 24, 2016)

  • v3.12.1(Jan 24, 2016)

  • v3.12.0(Jan 22, 2016)

    New middlewares

    Cache

    There's some breaking changes in this middleware:

    • The response body is not cached, just the headers, returning a 304 response if the response is not modified.
    • It uses internally micheh/psr7-cache so there's now support for Tags.
    • There's an option to add automatically a Cache-Control header to all responses not having this header.

    ImageTransformer

    • Added ImageTransformer::clientHints() option to detect the Client Hints headers
    • Added ImageTransformer::cache() option to save the transformed images in the cache
    • Removed ImageTransformer::basePath() option
    • Allow to limit the sizes to specific directories

    LanguageNegotiator

    • New option LanguageNegotiator::usePath() to allow to detect the language in the uri's path (for example: /en/about).
    • New option LanguageNegotiator::redirect() to return redirect response if the language is not in the path (for example: /about => /en/about).
    • New option LanguageNegotiator::basePath() to define a basePath after which is the language.

    Other changes

    • Removed Minify::forCache() option
    • Added ReadResponse::appendQuery() option to read filenames with the uri's query
    • Added SaveResponse::appendQuery() option to save files with the uri's query
    • Www::redirect() is set by default to 302
    Source code(tar.gz)
    Source code(zip)
  • v3.11.2(Jan 17, 2016)

  • v3.11.1(Jan 13, 2016)

    Csrf

    • Allow to set a custom storage in the constructor, instead use $_SESSION
    • Fixed the use of text inputs instead hidden inputs in the html forms.
    Source code(tar.gz)
    Source code(zip)
  • v3.11.0(Jan 12, 2016)

    New middlewares

    Api changes

    Some middlewares allow set options in two places: using the constructor and methods. For example, the AuraRouter middleware can be configured like this:

    //using constructor
    Middleware::auraRouter($routerContainer);
    
    //using the method
    Middleware::auraRouter()->router($routerContainer);
    

    To simplify the api and remove duplications, a lot of these methods have been removed, so these options must be set only in the constructor. Each constructor does not accept more than one argument and it is:

    • The required option (in middlewares like auraRouter, fastRouter, etc)
    • The optional (but most important option) (DebugBar instance, Whoops instance, "addSlash" option in trainlingSlash, etc)

    The rest of optional options are defined with methods.

    Other changes

    • Improved BasePath to allow to combine the user defined basepath and the autodetected
    • Improved the output buffer handling to prevent unexpect outputs with throwed exceptions
    • The handler in ErrorHandler is optional
    • The handler in Shutdown is optional
    • Simplified ImageTransformer and remove unsecure features
    • FormTimestamp::key() option is required now.
    • DebugBar changed the way to insert the debugbar. Instead dumping the entire css/js code, now serves the assets files. This avoid 404 error to other assets like fonts.
    • FormatNegotiator Added support to fonts mime types
    Source code(tar.gz)
    Source code(zip)
  • v3.10.2(Dec 30, 2015)

  • v3.10.1(Dec 29, 2015)

    Debugbar:

    • The option DebugBar::debugBar() is optional. If it's not passed, creates automatically an instance of DebugBar\StandardDebugBar
    • Fixed the html injection.
    • Make it work with ajax and redirections.

    ReadResponse:

    • Use a copy of the stream to not modify the original file accidentally.

    GoogleAnalytics:

    • Changed the execution order to be more intuitive. This middleware must be added after the router
    • Fixed the html injection.

    Piwik:

    • Changed the execution order to be more intuitive. This middleware must be added after the router
    • Fixed the html injection.
    Source code(tar.gz)
    Source code(zip)
  • v3.10.0(Dec 29, 2015)

    New Middlewares:

    Improvements:

    • New BasePath::autodetect() option to detect automatically the base path of the request
    • Allow to retrieve the base path removed using BasePath::getBasePath($request). This is specially useful when the base path is autodetected.
    • New option ClientIp::remote() to get the ip from ipecho. This is usefull for testing in localhost environments.
    • ReadResponse and SaveResponse can read and save gzip encoded responses.
    • ReadResponse can handle ranges.

    Other changes

    • Removed ErrorHandler::whoops() option. Use the new Middleware::whoops() instead.
    • The middleware Payload no longer remove the raw content of the body.
    • Changed DebugBar middleware to use http headers in ajax requests
    • Implemented Resolvers and Transformers. This allows to extend easily the library with more transformers (for example, more body parsers, more encoders, minifiers, etc).
    • Removed some options that have be handled by the tranformers:
      • Minify::inlineJs()
      • Minify::inlineCss()
      • Payload::associative()
    Source code(tar.gz)
    Source code(zip)
  • v3.9.3(Dec 21, 2015)

Owner
Oscar Otero
Web designer and developer 🦄
Oscar Otero
Middleware for PHP built on top of PSR-7 and PSR-15

zend-stratigility Repository abandoned 2019-12-31 This repository has moved to laminas/laminas-stratigility. From "Strata", Latin for "layer", and "ag

Zend Framework 236 Sep 9, 2022
PSR-15 middleware in minutes!

zend-expressive Repository abandoned 2019-12-31 This repository has moved to mezzio/mezzio. Develop PSR-7 middleware applications in minutes! zend-exp

Zend Framework 718 Dec 9, 2022
A PSR-15 server request handler.

Relay A PSR-15 request handler. This package is installable and PSR-4 autoloadable via Composer as "relay/relay": "~2.0". Alternatively, download a re

Relay 304 Dec 30, 2022
PSR-15 middleware for Symfony framework.

PSR-15 middleware now in Symfony Contents Installation Configuration Usage Examples Customization Caching Real World Example Middlewares Testing Licen

kafkiansky 60 Dec 14, 2022
Common utils used by PSR-15 middlewares

middlewares/utils Common utilities used by the middlewares' packages: Factory Dispatcher CallableHandler HttpErrorException Installation This package

Middlewares 47 Jan 2, 2023
A simple and flexible PHP middleware dispatcher based on PSR-7, PSR-11, and PSR-15

Woohoo Labs. Harmony Woohoo Labs. Harmony is a PSR-15 compatible middleware dispatcher. Harmony was born to be a totally flexible and almost invisible

Woohoo Labs. 153 Sep 5, 2022
A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package.

Net A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package. Features: No hard dependencies; Favours

Minibase 16 Jun 7, 2022
A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package.

Net A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package. Features: No hard dependencies; Favours

Minibase 16 Jun 7, 2022
Builder for stack middlewares based on HttpKernelInterface.

Stack/Builder Builder for stack middlewares based on HttpKernelInterface. Stack/Builder is a small library that helps you construct a nested HttpKerne

null 289 Oct 19, 2022
Sistema de Rotas com Controllers e Middlewares

Sistema de Rotas com Controllers e Middlewares

adev 0 May 30, 2022
🦭 Kirby, but headless only – KQL with bearer token, Express-esque middlewares & more

Kirby Headless Starter ℹ️ Send a Bearer test authorization header with a request to the live playground to test this headless starter. This starter ki

Johann Schopplich 36 Dec 28, 2022
A set of shady Slim Framework middlewares that can solve some annoyances...

Shady A set of shady Slim Framework middlewares that can solve some annoyances... What does it contain? Available middlewares: ApacheVirtualHostFix Ur

Jan-Age Laroo 7 Jun 22, 2021
A collection of common algorithms implemented in PHP. The collection is based on "Cracking the Coding Interview" by Gayle Laakmann McDowell

PHPAlgorithms A collection of common algorithms implemented in PHP. The collection is based on "Cracking the Coding Interview" by Gayle Laakmann McDow

Doğan Can Uçar 921 Dec 18, 2022
Middleware for PHP built on top of PSR-7 and PSR-15

zend-stratigility Repository abandoned 2019-12-31 This repository has moved to laminas/laminas-stratigility. From "Strata", Latin for "layer", and "ag

Zend Framework 236 Sep 9, 2022
Fast PSR-7 based routing and dispatch component including PSR-15 middleware, built on top of FastRoute.

Route This package is compliant with PSR-1, PSR-2, PSR-4, PSR-7, PSR-11 and PSR-15. If you notice compliance oversights, please send a patch via pull

The League of Extraordinary Packages 608 Dec 30, 2022
:tada: Release 2.0 is released! Very fast HTTP router for PHP 7.1+ (incl. PHP8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger)

HTTP router for PHP 7.1+ (incl. PHP 8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger) Installation compo

Sunrise // PHP 151 Jan 5, 2023
PSR-6 cache implementation adapting a given PSR-16 instance

PSR-6 cache implementation adapting PSR-16 This package provides a PSR-6 cache instance when you only have a PSR-16 cache at hand. As PSR-6 is more fe

null 1 Oct 15, 2021
PSR Log - This repository holds all interfaces/classes/traits related to PSR-3.

PSR Log This repository holds all interfaces/classes/traits related to PSR-3. Note that this is not a logger of its own. It is merely an interface tha

PHP-FIG 10.1k Jan 3, 2023
PSR-7 and PSR-15 JWT Authentication Middleware

PSR-7 and PSR-15 JWT Authentication Middleware This middleware implements JSON Web Token Authentication. It was originally developed for Slim but can

Mika Tuupola 782 Dec 18, 2022
PSR-7 and PSR-15 HTTP Basic Authentication Middleware

PSR-7 and PSR-15 Basic Auth Middleware This middleware implements HTTP Basic Authentication. It was originally developed for Slim but can be used with

Mika Tuupola 430 Dec 30, 2022