Giter VIP home page Giter VIP logo

middleman's Introduction

mindplay/middleman

PHP Version CI Code Coverage

Dead simple PSR-15 / PSR-7 middleware dispatcher.

Provides (optional) integration with a variety of dependency injection containers compatible with PSR-11.

To upgrade between major releases, please see UPGRADING.md.

A growing catalog of PSR-15 middleware-components is available from github.com/middlewares.

Usage

The constructor expects an array of PSR-15 MiddlewareInterface instances:

use mindplay\middleman\Dispatcher;

$dispatcher = new Dispatcher([
    new ErrorHandlerMiddleware(...)
    new RouterMiddleware(...),
    new NotFoundMiddleware(...),
]);

The Dispatcher implements the PSR-15 RequestHandlerInterface. This package only provides the middleware stack - to run a PSR-15 handler, for example in your index.php file, you need a PSR-15 host or a similar facility.

Note that the middleware-stack in the Dispatcher is immutable - if you need a stack you can manipulate, array, ArrayObject, SplStack etc. are all fine choices.

Anonymous Functions as Middleware

You can implement simple middleware "in place" by using anonymous functions in a middleware-stack, using a PSR-7/17 implementation such as nyholm/psr7:

use Psr\Http\Message\ServerRequestInterface;
use mindplay\middleman\Dispatcher;
use Nyholm\Psr7\Factory\Psr17Factory;

$factory = new Psr17Factory();

$dispatcher = new Dispatcher([
    function (ServerRequestInterface $request, callable $next) {
        return $next($request); // delegate control to next middleware
    },
    function (ServerRequestInterface $request) use ($factory) {
        return $factory->createResponse(200)->withBody(...); // abort middleware stack and return the response
    },
    // ...
]);

$response = $dispatcher->handle($request);

Dependency Injection via the Resolver Function

If you want to integrate with an IOC container you can use the ContainerResolver - a "resolver" is a callable which gets applied to every element in your middleware stack, with a signature like:

function (string $name) : MiddlewareInterface

The following example obtains middleware components on-the-fly from a DI container:

$dispatcher = new Dispatcher(
    [
        RouterMiddleware::class,
        ErrorMiddleware::class,
    ],
    new ContainerResolver($container)
);

If you want the Dispatcher to integrate deeply with your framework of choice, you can implement this as a class implementing the magic __invoke() function (as ContainerResolver does) - or "in place", as an anonymous function with a matching signature.

If you want to understand precisely how this component works, the whole thing is just one class with a few lines of code - if you're going to base your next project on middleware, you can (and should) understand the whole mechanism.

Middleware?

Middleware is a powerful, yet simple control facility.

If you're new to the concept of middleware, the following section will provide a basic overview.

In a nutshell, a middleware component is a function (or MiddlewareInterface instance) that takes an incoming (PSR-7) RequestInterface object, and returns a ResponseInterface object.

It does this in one of three ways: by assuming, delegating, or sharing responsibility for the creation of a response object.

1. Assuming Responsibility

A middleware component assumes responsibility by creating and returning a response object, rather than delegating to the next middleware on the stack:

use Zend\Diactoros\Response;

function ($request, $next) {
    return (new Response())->withBody(...); // next middleware won't be run
}

Middleware near the top of the stack has the power to completely bypass middleware further down the stack.

2. Delegating Responsibility

By calling $next, middleware near the top of the stack may choose to fully delegate the responsibility for the creation of a response to other middleware components further down the stack:

function ($request, $next) {
    if ($request->getMethod() !== 'POST') {
        return $next($request); // run the next middleware
    } else {
        // ...
    }
}

Note that exhausting the middleware stack will result in an exception - it's assumed that the last middleware component on the stack always produces a response of some sort, typically a "404 not found" error page.

3. Sharing Responsibility

Middleware near the top of the stack may choose to delegate responsibility for the creation of the response to middleware further down the stack, and then make additional changes to the returned response before returning it:

function ($request, $next) {
    $result = $next($request); // run the next middleware

    return $result->withHeader(...); // then modify it's response
}

The middleware component at the top of the stack ultimately has the most control, as it may override any properties of the response object before returning.

middleman's People

Contributors

brammm avatar fetchandadd avatar hannesvdvreken avatar mindplay-dk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

middleman's Issues

Replace resolver concept with middleware provider

Replace (or add) the resolver concept with a new concept: middleware providers.

interface MiddlewareProvider {
    /** @return MiddlewareInterface[] */
    public function getMiddleware();
}

Using a Generator would make it possible to resolve these on the fly.

This may simplify recursive dispatch as well.

The InteropResolver should likely change into a middleware provider for container-interop, e.g. ContainerMiddlewareProvider implements MiddlewareProvider.

Dispatcher

Shouldn't Dispatcher implement "ServerMiddlewareInterface" from Interop\Http\Middleware\ServerMiddlewareInterface and not "MiddlewareInterface" ?

MiddlewareInterface.php doesn't exist anymore in 0.2.1

Dispatcher as Middleware

Enable use of Dispatcher itself as middleware, by implementing MiddlewareInterface.

This would enable e.g. router middleware to delegate to a controller-specific middleware stack - which would remove the need to invent a different interface for controllers, a different concept for "filters", etc. - controllers and filters would simply be middleware components.

Immutable stack

Contrary to the documentation, the $stack constructor argument is currently type-hinted as array, which means you can't use an ArrayObject or SplStack, etc.

Note that fixing this will break recursive dispatch in the lambda-style Dispatcher slated for release 2.0 - it may therefore be necessary to fix that code, perhaps by employing a Generator, or simply by removing this note from the documentation and using e.g. iterator_to_array() in the constructor; modifying the internal state of the stack post-creation of the Dispatcher isn't good practice anyhow.

Switch to official PSR-15 psr/* packages

Switching to the official PSR-15 packages psr/http-server-middleware and psr/http-server-handler, sadly, is a breaking change.

You will need to live with the deprecation notice for the foreseeable future, likely for a couple of years, because a breaking change to this package means incompatibility with the current, existing body of available middleware.

Worse, the breaking change in middleman will propagate to breaking changes in every project and library that depends on it, which in my case means cascading breaking changes to around four dozen packages, again, with cascading breaking changes to every project depending on those.

There is simply no way I can justify spending this amount of time on a meaningless circus of dependency juggling again, since the container PSR also recently force everyone to go this route.

If you find this frustrating as all hell, you can go and thank the maintainers who refuse to listen when I argue that a proposed PSR should start with the Psr namespace - so that, come approval, all we need to do is switch to a new package that replaces the unofficial package.

This entire fucking circus could be avoided so easily with a little planning ahead.

Meanwhile, you can enjoy your deprecation notices for the next 2-3 years, until there's an actual reason for a major release of this package.

Yeah, FUCK.

Constrain Middlewares to only be executed for certain routes.

An often seen feature in frameworks that implement middlewares is the option to constrain middlewares from only being executed for certain routes (e.g. an AuthenticationMiddleware that's only executed for /admin routes).

Is this something that you'd wish to support with this library?

Use lambda-style middleware

Version 2.0 will use a lambda-style middleware interface similiar to that of Symfony and Stack.

Version 2.0 will use PSR-15^0.2 middleware interfaces.

We're awaiting the standardization of PSR-7 factory interface to make this feature more useful and more convenient to work with, as presently, a middleware component that creates a response needs to depend on a specific PSR-7 implementation.

We're not going to wait for the PSR-7 factory interfaces to make this change - middleware that creates a response will need to either depend directly on an implementation of PSR-7 for now, or will need to depend on an unstable http-interop/http-factory. Either way, this issue is somewhat external to this package.

Drop support for anonymous functions

As of PHP 7.0, support for anonymous functions is no longer required - we now have support for anonymous classes that actually implement the middleware-interface, which is much cleaner and safer.

As middleman already requires 7.0, we can drop support for function in the next major release.

Documentation should be updated to use anonymous classes instead of functions.

When support for legacy PSR-15 interfaces is removed as well, we should be able to get rid of all conditionals in the dispatcher and go completely strict on type-safety.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.