PSR-15 middleware for Symfony framework.

Overview

PSR-15 middleware now in Symfony

test Codecov Software License Total Downloads Quality Score

Contents

Installation

composer require kafkiansky/symfony-middleware

Configuration

Ensure you have added bundle in config/bundles.php:

Kafkiansky\SymfonyMiddleware\SymiddlewareBundle::class => ['all' => true],

Create the configuration file in packages/symiddleware.yaml:

symiddleware:
  global:
    ##

Usage

Each middleware must implement the Psr\Http\Server\MiddlewareInterface interface. Thanks for symfony autoconfiguration now the middleware registry knows your middleware.

So that middlewares can start execution, they must be defined on controller class and/or on controller method.

use Kafkiansky\SymfonyMiddleware\Attribute\Middleware;

#[Middleware([ValidatesQueryParams::class])]
final class SomeController
{
    #[Middleware([ConvertStringsToNull::class])]
    public function index(): void
    {
        
    }
}

If controller is invokable, middleware can be defined just on controller class:

use Kafkiansky\SymfonyMiddleware\Attribute\Middleware;

#[Middleware([ValidatesQueryParams::class, ConvertStringsToNull::class])]
final class SomeController
{
    public function __invoke(): void
    {
    }
}

groups

If you want to use the list of middlewares, you can define middleware group inside symfony_middleware.yaml configuration file:

symiddleware:
  groups:
    debug:
      if: '%env(RUN_DEBUG_MIDDLEWARE)%'
      middlewares:
        - 'App\Middleware\TrackRequestTime'
        - 'App\Middleware\EnableSqlLogger'

Now define this middleware on controller class or method:

use Kafkiansky\SymfonyMiddleware\Attribute\Middleware;

#[Middleware(['debug'])]
final class SomeController
{
    public function __invoke(): void
    {
    }
}

Pay attention to the if parameter in configuration file. This parameter tells the middleware runner when the middleware group can be run. If false, this middleware will not be executed.

global

If you want to run the list of middleware every request, you need the global middleware section. This keyword is reserved and if parameter is not supported.

symiddleware:
  global:
      - App\Controller\SetCorsHeaders
  groups:
    web:
      middlewares:
        - 'App\Middleware\ModifyRequestMiddleware'

Now the App\Controller\SetCorsHeaders middleware will execute on every request.

Examples

  1. Simple middleware that modifies request:
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;

final class ModifyRequestMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        return $handler->handle($request->withAttribute(__CLASS__, 'handled'))
    }
}
  1. Middleware that modifies response:
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;

final class ModifyResponseMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $response = $handler->handle($request)

        return $response->withHeader('x-developer', 'kafkiansky');
    }
}
  1. Middleware that stop execution:
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Nyholm\Psr7\Response;

final class StopExecution implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $response = new Response(200, [], json_encode(['success' => false]));

        return $response;
    }
}

In this example controller will not be executed.

  1. Stop execution with symfony response:
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Nyholm\Psr7\Response;
use Kafkiansky\SymfonyMiddleware\Psr\PsrResponseTransformer;
use Symfony\Component\HttpFoundation\JsonResponse;

final class StopExecution implements MiddlewareInterface
{
    private PsrResponseTransformer $psrResponseTransformer;

    public function __construct(PsrResponseTransformer $psrResponseTransformer)
    {
        $this->psrResponseTransformer = $psrResponseTransformer;        
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        return $this->psrResponseTransformer->toPsrResponse(new JsonResponse(['success' => false]));
    }
}

You can compose middleware group with single middleware, use list of Middleware attributes and so on. All the following examples will work:

use Kafkiansky\SymfonyMiddleware\Attribute\Middleware;

#[Middleware(['debug', 'api', SomeMiddleware::class])]
#[Middleware([SomeAnotherMiddleware::class])]
final class SomeController
{
    public function __invoke(): void
    {
    }
}
use Kafkiansky\SymfonyMiddleware\Attribute\Middleware;

#[Middleware(['debug', 'api', SomeMiddleware::class])]
final class SomeController
{
    #[Middleware([SomeAnotherMiddleware::class, 'web'])]
    #[Middleware(['tracking'])]
    public function index(): void
    {
    }
}

Duplicated middlewares will be removed.

Customization

PSR middlewares and Symfony has different incompatible Request objects. If your middleware going to change the request object, only attributes, query params, headers and parsed body will be copied from psr request to symfony request. If you wish to change this behaviour, you may change the Kafkiansky\SymfonyMiddleware\Psr\PsrRequestCloner interface binding it to your realization.

Caching

Package use caching on production environment to prevent reflection usage. First of all, package will search of the app.cache_middleware parameter. If package doesn't find it, it's going to use the kernel.environment definition and will cache attributes when it set to prod.

Package will cache all controllers even if it doesn't found the attributes for it. This approach will allow to remember all the controllers and not use reflection further.

Real World Example

Imagine that you have some endpoints which requires authorization access via basic. Write middleware:

# services.yaml

services:
    _defaults:
        autowire: true
        autoconfigure: true

        bind:
            $basicUser: 'root'
            $basicPassword: 'secret'
// Authorization Middleware

use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Nyholm\Psr7\Response;

final class AuthorizeRequests implements MiddlewareInterface
{
    private string $basicUser;
    private string $basicPassword;

    public function __construct(string $basicUser, string $basicPassword)
    {
        $this->basicUser = $basicUser;
        $this->basicPassword = $basicPassword;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $user = $request->getServerParams()['PHP_AUTH_USER'] ?? null;
        $passwd = $request->getServerParams()['PHP_AUTH_PW'] ?? null;

        if ($user === $this->basicUser && $passwd === $this->basicPassword) {
            return $handler->handle($request);
        }

        return new Response(401, [
            'WWW-Authenticate' => 'Basic realm="Backend"'
        ]);
    }
}
# example configuration
symiddleware:
  groups:
    basic:
      middlewares:
        - App\Middleware\AuthorizeRequests
// Some controller

use Symfony\Component\HttpFoundation\JsonResponse;
use Kafkiansky\SymfonyMiddleware\Attribute\Middleware;

final class SomeController
{
    #[Middleware(['basic'])] // via middleware group
    public function writeArticle(): JsonResponse
    {
    }

    #[Middleware([App\Middleware\AuthorizeRequests::class])] // via concrete class
    public function deleteArticle(): JsonResponse
    {
    }
}

Middlewares

Handle HTTP Basic Auth PSR-15 middleware for Symfony

Testing

$ composer test

License

The MIT License (MIT). See License File for more information.

You might also like...
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

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

OpenAPI(v3) Validators for Symfony http-foundation, using `league/openapi-psr7-validator` and `symfony/psr-http-message-bridge`.

openapi-http-foundation-validator OpenAPI(v3) Validators for Symfony http-foundation, using league/openapi-psr7-validator and symfony/psr-http-message

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

Disable Google's FLoC with help of PSR-15 middleware

Disable Google's FLoC with PSR-15 middleware This package will help you disable Google's FLoC. Installation You can install the package via composer:

PSR-15 middleware with various cache utilities

middlewares/cache Middleware components with the following cache utilities: CachePrevention Expires Cache Requirements PHP = 7.2 A PSR-7 http library

PSR-15 middleware to geolocate the client using the ip address

middlewares/geolocation ![SensioLabs Insight][ico-sensiolabs] Middleware to geolocate the client using the ip address and Geocoder and save the result

A PSR-15 middleware adapter for react/http

A PSR-15 middleware adapter for react/http Wraps PSR-15 middleware into coroutines using RecoilPHP making them usable within react/http as middleware.

PSR-15 middleware to use Whoops as error handler

middlewares/whoops Middleware to use Whoops as error handler. Requirements PHP = 7.2 A PSR-7 http library A PSR-15 middleware dispatcher Installation

A PSR-15 middleware to handle content negotiation

Content negotiation middleware Motivation Packages like middlewares/negotiation do a very good job to detect the correct content type based on the Acc

PSR-15 compatible middleware for Whoops, the pretty error handler

PSR-15 middleware for Whoops A PSR-15 compatible middleware for Whoops, the fantastic pretty error handler for PHP. Installation You can install the l

An internal redirect mechanism for PSR-15 middleware stacks

HTTP Request Forwarder The aim of this library is to make it possible to pass the HTTP request to another handler, creating a so-called internal redir

: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

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

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

Testing utilities for the psr/log package that backs the PSR-3 specification.

FIG - Log Test Testing utilities for the psr/log package that backs the PSR-3 specification. Psr\Log\Test\LoggerInterfaceTest provides a base test cla

This is a Symfony bundle that lets you you integrate your PSR-6 compliant cache service with the framework

PSR-6 Cache bundle This is a Symfony bundle that lets you you integrate your PSR-6 compliant cache service with the framework. It lets you cache your

Symfony React Blank is a blank symfony and react project, use this template to start your app using Symfony as an backend api and React as a frontend library.

Symfony React Blank Symfony React Blank is a blank symfony and react project, use this template to start your app using Symfony as an backend api and

Provides a Middleware to integration Tideways into Symfony Messenger Processing

Tideways Middleware for Symfony Messenger This package is currently under development and might be moved into the Tideways PHP Extension or stay indep

Comments
  • Adding support for middleware arguments.

    Adding support for middleware arguments.

    Now there is no possibility to use arguments with middleware as, for example, in Laravel.

    As I imagine it, the arguments can be specified as an array within the attribute:

    #[Middleware([MiddlewareA::class => ['x' => 1, 'y' => 2]])]
    final class SomeController
    {
    }
    

    Inside the middleware MiddlewareA arguments can be accessed using ServerRequestInterface::getAttribute() with middleware fqcn as attribute name:

    final class MiddlewareA implements MiddlewareInterface
    {
        public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
        {
            var_dump($request->getAttribute(MiddlewareA::class)); // ['x' => 1, 'y' => 2];
        }
    }
    

    I'm also thinking of adding the ability to refer to symfony parameters with %parameter_name%.

    #[Middleware([MiddlewareA::class => ['env' => '%app.env%']])]
    final class SomeController
    {
    }
    

    Then the arguments get to the MiddlewareA, they will be resolved.

    opened by kafkiansky 0
Releases(0.3.0)
Owner
kafkiansky
kafkiansky
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
Slim Framework 2 middleware

Slim Framework Middleware This repository contains a library of optional middleware for your Slim Framework application. How to Install Update your co

Slim Framework 47 Nov 7, 2022
[DEPRECATED] Collection of PSR-7 middlewares

This package is deprecated in favor of the new PSR-15 standard. Check it out here psr7-middlewares Collection of PSR-7 middlewares. Requirements PHP >

Oscar Otero 669 Dec 27, 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-7 middleware foundation for building and dispatching middleware pipelines

laminas-stratigility From "Strata", Latin for "layer", and "agility". This package supersedes and replaces phly/conduit. Stratigility is a port of Sen

Laminas Project 47 Dec 22, 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
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
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