Code Monkey home page Code Monkey logo

zend-stratigility's Introduction

Logo

Welcome to the Zend Framework 3.0 Release!

RELEASE INFORMATION

Zend Framework 3.0.1dev

This is the first maintenance release for the Zend Framework 3 series.

DD MMM YYYY

UPDATES IN 3.0.1

Please see CHANGELOG.md.

SYSTEM REQUIREMENTS

Zend Framework 3 requires PHP 5.6 or later; we recommend using the latest PHP version whenever possible.

INSTALLATION

We no longer recommend installing this package directly. The package is a metapackage that aggregates all components (and/or integrations) originally shipped with Zend Framework; in most cases, you will want a subset, and these may be installed separately; see https://docs.zendframework.com/ for a list of available packages and installation instructions for each.

We recommend using either the zend-mvc skeleton application:

$ composer create-project zendframework/skeleton-application project

or the Expressive skeleton application:

$ composer create-project zendframework/zend-expressive-skeleton project

The primary use case for installing the entire framework is when upgrading from a version 2 release.

If you decide you still want to install the entire framework:

$ composer require zendframework/zendframework

GETTING STARTED

A great place to get up-to-speed quickly is the Zend Framework QuickStart.

The QuickStart covers some of the most commonly used components of ZF. Since Zend Framework is designed with a use-at-will architecture and components are loosely coupled, you can select and use only those components that are needed for your project.

MIGRATION

For detailed information on migration from v2 to v3, please read our Migration Guide.

COMPONENTS

This package is a metapackage aggregating the following components:

CONTRIBUTING

If you wish to contribute to Zend Framework, please read the CONTRIBUTING.md and CODE_OF_CONDUCT.md files.

QUESTIONS AND FEEDBACK

Online documentation can be found at https://docs.zendframework.com/. Questions that are not addressed in the manual should be directed to the relevant repository, as linked above.

If you find code in this release behaving in an unexpected manner or contrary to its documented behavior, please create an issue with the relevant repository, as linked above.

Reporting Potential Security Issues

If you have encountered a potential security vulnerability in Zend Framework, please report it to us at [email protected]. We will work with you to verify the vulnerability and patch it.

When reporting issues, please provide the following information:

  • Component(s) affected
  • A description indicating how to reproduce the issue
  • A summary of the security vulnerability and impact

We request that you contact us via the email address above and give the project contributors a chance to resolve the vulnerability and issue a new release prior to any public exposure; this helps protect Zend Framework users and provides them with a chance to upgrade and/or update in order to protect their applications.

For sensitive email communications, please use our PGP key.

LICENSE

The files in this archive are released under the Zend Framework license. You can find a copy of this license in LICENSE.md.

ACKNOWLEDGEMENTS

The Zend Framework team would like to thank all the contributors to the Zend Framework project; our corporate sponsor, Zend Technologies / Rogue Wave Software; and you, the Zend Framework user.

Please visit us sometime soon at http://framework.zend.com.

zend-stratigility's People

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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 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

zend-stratigility's Issues

Zend\Stratigility\MiddlewarePipe missing dependencies

Instantiating a MiddlewarePipe instance fails on missing interface dependencies, namely:
Webimpress\HttpMiddlewareCompatibility\HandlerInterface, and
Webimpress\HttpMiddlewareCompatibility\MiddlewareInterface

Code to reproduce the issue

use Interop\Http\Server\RequestHandlerInterface;
use Zend\Stratigility{MiddlewarePipe, NoopFinalHandler};
use Zend\Diactoros{Server, Response};
use Psr\Http\Message\ServerRequestInterface;

require DIR . '/../vendor/autoload.php';

$pipe = new MiddlewarePipe();

Expected results

Pipe instance.

Actual results

Fatal error on missing interfaces

Make `$next` argument required in `MiddlewareInterface`

With the current MiddlewareInterface definition, developers must first validate that $next is non-empty before calling it:

return ($next ? $next($req, $res) : $res);

This is confusing, and error-prone.

For version 2, the argument should be required.

[RFC] HTTP method overriding middleware

  • I was not able to find an open or closed issue matching what I'm seeing.

  • This is not a question. (Questions should be asked on slack (Signup for Slack here) or our forums.)

  • Case 1: In my previous company which was handling several millions of request each day on a Restful API-centric website based on Apigility, we once realised that one of our customers just didn't allow other HTTP verbs than GET and POST through his proxy.

  • Case 2: Some browser still don't allow other verbs than GET and POST in forms.

I propose to create a middleware overriding the HTTP method when _method parameter is provided in the GET or POST payload (ie ?_method=DELETE).

Some options I also consider:

  • Provide user with the ability to set the name of the parameter instead of the default name _method
  • Provide the user with the ability to enforce that the argument has been provided in the super global ($_GET or $_POST) corresponding to the HTTP method used for the request (GET or POST)
  • Provide user with the ability to decide whether argument should be removed from the query after usage (hence a cloned and modified query returned)

validateMethod has been derived from the request trait.

If interest shown, I will create the file and unit tests in a PR.
Should that middleware be provided as a "module"?

<?php

declare(strict_types=1);

namespace Zend\Stratigility\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class HttpMethodOverride implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
    {
        $method = $request->getAttribute('_method', null);

        if (!is_null($method)) {
            $this->validateMethod($method);

            $originalMethod = strtoupper($request->getMethod());

            switch ($originalMethod) {
                case 'GET':
                case 'POST':
                    $request = $request->withMethod(strtoupper($method));
                    break;
                default:
                    throw new InvalidArgumentException(sprintf(
                        'Only GET and POST http methods can be overrode, received %s',
                        $method
                    ));
            }
        }

        return $handler->handle($request);
    }

    /**
     * Validate the HTTP method
     *
     * @param null|string $method
     * @throws InvalidArgumentException on invalid HTTP method.
     */
    private function validateMethod($method)
    {
        if (! is_string($method)) {
            throw new InvalidArgumentException(sprintf(
                'Unsupported HTTP method; must be a string, received %s',
                (is_object($method) ? get_class($method) : gettype($method))
            ));
        }

        if (! preg_match('/^[!#$%&\'*+.^_`\|~0-9a-z-]+$/i', $method)) {
            throw new InvalidArgumentException(sprintf(
                'Unsupported HTTP method "%s" provided',
                $method
            ));
        }
    }
}

```

FinalHandler and env option.

env, the application environment. If set to "production", no stack traces will be provided.

In reality, same will happen also when env is not set at all or falsy.

It would be nice to change FinalHandler behaviour to mute stack traces when env is set to production (const for that would be also nice), but this would break BC.

Another solution would be to drop env completely in favour of callable creating response message from passed error - effectively replacing ::createDevelopmentErrorMessage.
It would also allow xdebug users to create their own formatters.
But this is more in future.

So for now, probably docs update would do.

Calling Final Handler

Hi all,

Working with zend-expressive with no error handlers installed, I noticed something that seems the problem at stratigility or the way I am thinking.

In Zend\Stratigility\MiddlewarePipe , we are passing Zend\Stratigility\FinalHandler to the Zend\Stratigility\Next.

So on a middlewares ( expect we have many middlewares handling on the same route M1, M2, M3 and most of them are similar as below )

<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class Middle1
{
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
    {
         // do something
         if($next) {
             $response = $next($request, $response);
         }
         // modify response if needed
        return $response;
    }
}

And you can see at Zend\Stratigility\Next once the queue got empty it calls the final handler and return a response which is 404. https://github.com/zendframework/zend-stratigility/blob/1.1.2/src/FinalHandler.php#L137 .

So always the 404 is returned.

I feel it should not be like that and it should still wait to get the response back and should only call the Final Handler if there is no response got.

So my belief is that it should not pass the FinalHandler to the Next class.

Thoughts ?

Thank you.

raiseThrowables() - Issue here or in Expressive?

I might just have really bad timing with this, but I'm having issues with 1.3.0 and Expressive master branch which I think is related to raiseThrowables(). I'm using configuration-driven pipelining, and have converted to the new style of handling errors, but thrown exceptions are not bubbling their way out to the error middleware - they are hitting the error depreciation notice in Next.

Do I just need to wait till Expressive is updated, or is this a Stratigility issue?

FWIW, here is my pipeline config (I factory ErrorHandler for a custom generator):

return [
    'dependencies'        => [
        'factories'  => [
            Helper\ServerUrlMiddleware::class          => Helper\ServerUrlMiddlewareFactory::class,
            Helper\UrlHelperMiddleware::class          => Helper\UrlHelperMiddlewareFactory::class,
            MyApp\Action\AuthenticationAction::class   => MyApp\Action\AuthenticationFactory::class,
            Zend\Stratigility\Middleware\ErrorHandler::class => MyApp\ErrorHandling\ErrorHandlerFactory::class
        ],
        'invokables' => [
            Zend\Stratigility\Middleware\OriginalMessages::class => Zend\Stratigility\Middleware\OriginalMessages::class,
            Zend\Stratigility\NoopFinalHandler::class => Zend\Stratigility\NoopFinalHandler::class,
            Helper\BodyParams\BodyParamsMiddleware::class => Helper\BodyParams\BodyParamsMiddleware::class,
            MyApp\Action\AuthorizationAction::class => MyApp\Action\AuthorizationAction::class,
            MyApp\ErrorHandling\ErrorResponseGenerator::class => MyApp\ErrorHandling\ErrorResponseGenerator::class,
        ],
    ],
    'middleware_pipeline' => [
        'always' => [
            'middleware' => [
                Zend\Stratigility\Middleware\OriginalMessages::class,
                Zend\Stratigility\Middleware\ErrorHandler::class,
                Helper\ServerUrlMiddleware::class,
                Helper\BodyParams\BodyParamsMiddleware::class,
            ],
            'priority'   => 10000,
        ],

        'routing' => [
            'middleware' => [
                ApplicationFactory::ROUTING_MIDDLEWARE,
                Helper\UrlHelperMiddleware::class,
                MyApp\Action\AuthenticationAction::class,
                MyApp\Action\AuthorizationAction::class,
                ApplicationFactory::DISPATCH_MIDDLEWARE,
            ],
            'priority'   => 1,
        ],

        'final' => [
            'middleware' => [
                Zend\Stratigility\NoopFinalHandler::class
            ],
            'priority'   => -10000,
        ],
    ],
];

Edit - I removed the error => true from the bottom pipeline group and it didn't help. Config changed to reflect that.

MiddlewareRegexPipe class.

What about regex support ? It can be more functional optionally if its support regex.

$app = MiddlewarePipe('/api', $callable);

or i can use MiddlewareRegexPipe class.

$app = new MiddlewareRegexPipe('/admin/.*,  $callable)

Introducing a common Error class?

Hi,

I've thought of something: currently, you can throw an exception with a message, and a code. This code is used to match a status code and return the appropriate error.

I like the idea of having the FinalHandler handle this and format the error response. If you want a different way to render your errors, just override the final handler and format them the way you want.

However, the issue is that it middlewares built around Stratigility don't have a common way to format errors. For instance, how a middleware that would need to return more than just a message would work? Some developers may decide to maybe encode using json_encode, and do custom logic in the FinalHandler, but it does not encourage a common design.

I'm suggesting that we would add a common interface:

interface ResponseErrorExceptionInterface
{
    public function getMessage();

    public function getCode();

    /**
     * @return array
     */
    public function getExtra();
}

FinalHandler would be able to read this and create a response. Each middleware could use this exception. Then it's up to your ifnal handler to decide if you want to return the extra as "additional_details", "details", "error_extra" or whatever.

Possible bug on Middleware Error Handler

hi, may be it bug on stratigily or expressive. I dont know where the right place to report it. So, i will just try here.

I registered my error handler on expressive, and i try to hit the application that will cause the error thown. It execute my error handler but i dunno why it still reaches the final handler.

My error handler:

public function __invoke($error, Request $request, Response $response, callable $next = null)
    {
        if ($error instanceof TokenMismatchException) {
            $html = $this->template->render('error::csrf403', [
                'title' => 'Forbidden',
                'main'  => 'CSRF verification failed. Request aborted.'
            ]);
            $stream = new Stream('php://memory', 'wb+');
            $stream->write($html);
            var_dump('reached?');
            return $response
                ->withBody($stream)
                ->withStatus(403, 'Forbidden')
                ->withHeader('Content-Type', 'text/html');
        }

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

It print reached? to browser, but not the content i wanted, i think it because Stratigility (may be?) continue to execute the error middleware stack and reached my FinalHandler? Even though it already handled?

Note: If you wonder why it display 404 page, it was the Expressive FinalHandler default behavior.

error

Middleware of type array($dispatcher, 'methodName') cannot be passed

It results in following error

Warning: ReflectionFunction::__construct() expects parameter 1 to be string, array given in /private/var/www/html/redirector/vendor/zendframework/zend-stratigility/src/Utils.php on line 44

Fatal error: ReflectionFunctionAbstract::getNumberOfRequiredParameters(): Internal error: Failed to retrieve the reflection object in /private/var/www/html/redirector/vendor/zendframework/zend-stratigility/src/Utils.php on line 45

Documented examples fail

Here is the documented code:

use Zend\Stratigility\MiddlewarePipe;
use Zend\Diactoros\Server;

require __DIR__ . '/../vendor/autoload.php';

$app    = new MiddlewarePipe();
$server = Server::createServer($app,
  $_SERVER,
  $_GET,
  $_POST,
  $_COOKIE,
  $_FILES
);

$server->listen(function ($req, $res) {
  return $res;
});

This fails as $app is not callable.

Stratigility v3 and Diactoros v1.7.1

ErrorHandler marked as final

ErrorHandler middleware was marked as final but introduces a new public method (attachListener) that was not declared in MiddlewareInterface. This makes ErrorHandler not mockable and hard to use in tests.

This is similar to #144 and requires removing final keyword or creating an interface for ErrorHandler.

Why ErrorMiddlewareInterface?

I am trying to understand the benefits of having a separate interface and concept for error handling. I read about half of #16 (very long discussion) and still not see it.

Here is how I implemented it in a pet project not based on stratigility (the list below represents a middleware stack/pipe):

  • ErrorHandlingMiddleware
  • Middleware1
  • Middleware2
  • ...

The ErrorHandlingMiddleware is basically this:

class ErrorHandlingMiddleware
{
    public function __invoke(RequestInterface $request, ResponseInterface $response, $next)
    {
        try {
            return $next($request, $response);
        } catch (\Exception $e) {
            // Show a nice error page
            // ...
            return $response;
        }
    }
}

So why not go with this (simpler) solution for stratigility?

MissingResponsePrototypeException thrown when using http-middleware 0.5.0 callable middleware

The changelog says

Essentially, this means you can drop this package into an application targeting either the 0.4.1 or 0.5.0 versions of http-middleware, and it will "just work".

so I updated my app to use http-middleware to 0.5.0 and get this exception with zend-stratigility 2.1.0:

Fatal error: Uncaught Zend\Stratigility\Exception\MissingResponsePrototypeException: Cannot wrap callable middleware; no Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory or Psr\Http\Message\ResponseInterface instances composed in middleware pipeline; use setCallableMiddlewareDecorator() or setResponsePrototype() on your Zend\Stratigility\MiddlewarePipe instance to provide one or the other, or decorate callable middleware manually before piping. in zend-stratigility/src/MiddlewarePipe.php on line 250

Here is a small example to reproduce the error:

<?php declare(strict_types=1);

use Interop\Http\Server\MiddlewareInterface;
use Interop\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\TextResponse;
use Zend\Diactoros\Server;
use Zend\Stratigility\MiddlewarePipe;
use Zend\Stratigility\NoopFinalHandler;

require __DIR__ . "/vendor/autoload.php";

$app = new MiddlewarePipe();

// Does NOT work, triggers an exception
$app->pipe(function (ServerRequestInterface $request, RequestHandlerInterface $handler) {
    return new TextResponse("Hello, world!");
});

// It works fine with classes though
$app->pipe(new class implements MiddlewareInterface {
    function process(ServerRequestInterface $request, RequestHandlerInterface $handler) {
        return new TextResponse("Hello, world!");
    }
});

Server::createServer($app, $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES)
      ->listen(new NoopFinalHandler());

composer.json

{
    "require": {
        "http-interop/http-middleware": "0.5.0",
        "php": "^7.1",
        "psr/http-message": "^1.0",
        "zendframework/zend-diactoros": "1.6.0",
        "zendframework/zend-stratigility": "2.1.0"
    }
}

FinalHandler marks response as complete before emitted

The FinalHandler marks a response as complete when it handles an error. This is a problem because the emitter, such as SapiEmitter adds in the Content-Length header, which then fails because the response is complete.

Piping and routes

I was having issues using my router as middleware off of Stratigility. Given the following code:

$router = ...
$router->get('/', $handler1);

$router2 = ...
$router2->get('/', $handler2);
$router2->get('', $handler3);

$app->pipe($router);
$app->pipe('/articles', $router2);

You would expect / to match the first handler1 and /articles to match handler2. This is not the case, however, as /articles will match handler3 instead. I think there's a leading slash being removed somewhere but I haven't had time to debug it yet.

This is a fairly critical bug since you won't be able to take a set of routes and move them from one pipe() to another and have them work identical unless you have an optional leading slash in your route or duplicate the route entirely.

Compatibility with zend-httphandlerrunner and zend-diactoros

I'm trying to understand the larger picture with the new layouts for the aforementioned packages and this one, and I can't get my head fully around it.

I may be missing something, but there seems to be a component missing to allow the MiddlewarePipe to be useable as the ServerRequestHandlerInterface requirement of RequestHandlerRunner?

In the older versions, we had the ability to simply pass a pipeline to stratigility and it run, but since these packages have rightly been abstracted, I can't figure out after reading them, how we are to run a pipeline without writing an intermediary handler.

I imagine that expressive has such a thing, but for the consumers who don't use expressive, would it be possible to have something created in the stratigility package?

Example in documentation throws a fatal error

Using 2.0.1
https://zendframework.github.io/zend-stratigility/middleware/

Will throw

Fatal error: Uncaught Zend\Stratigility\Exception\MissingResponsePrototypeException:
Cannot wrap callable middleware; no Zend\Stratigility\Middleware\CallableMiddlewareWrapperFactory
or Psr\Http\Message\ResponseInterface instances composed in middleware pipeline; use
setCallableMiddlewareDecorator() or setResponsePrototype() on your Zend\Stratigility\MiddlewarePipe
instance to provide one or the other, or decorate callable middleware manually before piping.

PSR-7 violations

I just spent two hours trying to find out why a middleware was unable to set a header, and it turns out this line of code violates this clause of the spec:

MUST return an instance that has the new and/or updated header and value.

The idea of a response being "complete" isn't even a thing in the spec.

Simply ignoring function calls in this manner makes it very hard to debug anything, and is extremely risky. If I called setHeader() (or any number of other methods) clearly I'm calling them for one reason, and one reason only - quietly ignoring this call is a clear violation of the spec.

Debugging this type of silent error is an absolute nightmare. Components and middleware worked perfectly in unit and integration tests, but then quietly failed when implemented in production code.

I'm baffled as to why you would think this feature is necessary in the first place? Request and Response models are already immutable - what possible reason could you have to disable an an already-immutable model instance from generating a new model?

If a given piece of middleware decides that a response is "complete", quite simply, it won't delegate control to the next middleware on the stack. Instead, this feature allows you to delegate control to the next middleware, but disabling it from actually doing anything - it will run, but can't actually do anything.

Am I crazy for thinking this is crazy??

RuntimeException Layer and request path have gone out of sync for capitalised piped route

Getting a RuntimeException
"Layer and request path have gone out of sync"
From zendframework/zend-stratigility/src/Next.php line 271

Code to reproduce the issue

Add piped route like this for example
$app->pipe('/myadmin', \App\Middleware\Auth\AdminAuth::class);

Then via browser go to /MYADMIN
(Note the capital letters)

Expected results

Either 404 or it matches that route

Actual results

RuntimeException
"Layer and request path have gone out of sync"

Stratigility - `$next` type hint and project naming question

Hello there,

I have many questions regarding Stratigility
Why choosing a name that will confuse everyone knowing Apigility ?

Why getting rid of object implementation and only use Callable ? It will cause unused middlewares to be instantiated and serialization issues.

Finally, how to deal with middleware having more than one children (not just one "next" callable), why having not implemented real workflow around it ?

It is not just about complaining, I am ready to code what necessary. I just wanna know if this architecture have a purpose I cannot see and is already validated.

FinalHandler always uses `OK` as reasonPhrase if non previously set

If an error makes it to handleError, without a reasonPhrase, it will always be OK. This is because when calling withStatus, it is passing getReasonPhrase which checks if there is one set, and if not uses it from the current statusCode. Of course statusCode is 200 at that time because it's the default in the constructor.

Custom Request/Response Objects

If I have application specific Request and Response objects that are extensions of the Diactoros PSR-7 implementations, all customization added in my extensions become unusable within my application due to the Stratigility HTTP decorators.

A solution would be to have my own custom HTTP objects be decorators themselves that decorate the Stratigility HTTP objects, but that architecture feels less than ideal. Then because of the type hints within the default FinalHandler, best practice would be to undecorate before passing the objects on.

Would it be better to have the Stratigility HTTP decorators be extensions of the Diactoros PSR-7 implementations? That way any custom Request or Response objects the programmer wanted could then extend those. I haven't looked too deeply into this yet, but after quickly browsing the code, I believe such a change would be backwards compatible.

Also, as far as I can tell, none of the added functionality of the decorated HTTP objects is used until the FinalHandler. Maybe a solution is to wait until that handler is invoked to decorate? Or perhaps the FinalHandler should be responsible for the functionality that is currently provided by the decorator?

Anyway, I'm gonna play with some ideas here, but let me know your thoughts because I feel like this problem is very limiting.

Thanks for this lib!!

Regression on 1.2.0 with Response end

Starting from version 1.2, our app stopped working when an error is thrown in the code.

In our code, the FinalHandler is map to a custom handler:

class JsonErrorHandler
{
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $err = null)
    {
        if ($err) {
            return $this->handleError($err, $response);
        }

        if (null === $request->getAttribute(RouteResult::class)) {
            return new JsonResponse(['message' => 'Not Found'], 404);
        }

        return $response;
    }

    private function handleError($err, ResponseInterface $response)
    {
        // To avoid security issue, when the error is an exception, we simply display a 500
        if ($err instanceof Exception) {
            return new JsonResponse(['message' => 'An error occurred'], 500);
        }

        return new JsonResponse(['message' => $response->getReasonPhrase()], $response->getStatusCode());
    }
}

It used to return a JsonResponse when there was an error, but now it throws the RuntimeException error:

capture d ecran 2016-03-21 a 16 18 53

Getting paths from pipeline

I would like to be able to get a list of the paths that have been added to my MiddlewarePipe. My use case is this:

I am migrating an application to zend-expressive that includes several distinct sub-applications at different paths (say '/admin' and '/soap'), some of which require routing, some which don't. There is also a white label sub-application that administrators can brand and expose to their customers via a top-level path they define (ie '/mycustomer'). I need to ensure that the path they enter is not part of the core pipeline.

While I could just hard-code a list of paths it would be better if I could access something like $app->getPipedPaths() right after the programatic pipeline has been built. Not 100% sure if this belongs in Stratigility or Expressive - would this be of interest here?

When creating Zend application from scratch

127.0.0.1:39126 [500]: / - Uncaught Zend\Stratigility\Exception\MissingResponseException: Decorated callable middleware of type Closure failed to produce a response. in /home/peter/Code/Zend/expressive/vendor/zendframework/zend-stratigility/src/Exception/MissingResponseException.php:23
Stack trace:
#0 /home/peter/Code/Zend/expressive/vendor/zendframework/zend-stratigility/src/Middleware/CallableMiddlewareDecorator.php(55): Zend\Stratigility\Exception\MissingResponseException::forCallableMiddleware(Object(Closure))
#1 /home/peter/Code/Zend/expressive/vendor/zendframework/zend-expressive-router/src/Middleware/DispatchMiddleware.php(56): Zend\Stratigility\Middleware\CallableMiddlewareDecorator->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\Next))
#2 /home/peter/Code/Zend/expressive/vendor/zendframework/zend-stratigility/src/Next.php(127): Zend\Expressive\Router\Middleware\DispatchMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\Next))
#3 /home/peter/Code/Zend/expressive/vendor/zen in /home/peter/Code/Zend/expressive/vendor/zendframework/zend-stratigility/src/Exception/MissingResponseException.php on line 23

Stripping matched path in middleware pipeline

Middleware pipped to a path stripped in the original request matched path.
So if we attach middleware to path /api:
$app->pipe('/api', Middleware::class);
and we call /api/foo our $request in Middleware will contain path /foo not /api/foo.

Still, it is possible to get original request uri:
$originalUri = $request->getAttribute('originalUri');

but it seems to be odd for me that original request is modified.
There is short explanation in the documentation:

The handlers in each middleware attached this way will see a URI with that path segment stripped, allowing them to be developed separately and re-used under any path you wish.
https://docs.zendframework.com/zend-stratigility/middleware/

I've discussed this issue with @weierophinney on IRC and it's expected behaviour.

It is expected behavior. The idea behind using a path when piping is to allow segregating applications. It's not intended for routing.
So, the idea is that you can develop an application in isolation, and do the routing as if it were the application root. You can then nest it into another application under a sub-path, and your routing and whatnot continue to work.
As an example, I might develop an API in isolation from the rest of the application, but then compose it into the application under the /api sub-path. I can do that without worrying about whether or not my routing now breaks because of the addition of the sub-path.

It would be nice to update documentation with a bit more explanations, because IMHO changing original request is not something what you can expect.

Issues with error handling in upcoming 1.3

I'm preparing an RFC for error handling in Expressive 1.1, and discovered some potential problems with how error handling works for the upcoming 1.3 release of Stratigility.

While the ErrorHandler class is fantastic, it provides one fundamental problem: the try/catch block will have no effect out-of-the-box currently.

The reason is because Dispatch already has a try/catch block, and, when a Throwable or Exception is caught, it calls on the next error middleware, passing the Throwable or Exception:

return $next($request, $response, $exception);

In turn, this will now raise a deprecation notice, as the third argument to $next() is deprecated.

To make this work, I see the following potential paths.

Flag

First, we could introduce a flag for MiddlewarePipe, raiseExceptions. When enabled, the flag will be passed to the constructor of Next, which will in turn pass it on to Dispatch. Internally, if that flag is enabled in Dispatch, it will either not wrap middleware dispatch in a try/catch block, or re-raise the caught exception. Applications can then opt-in to this behavior in the 1.3 series; it would only technically be needed for the outermost middleware, though composed middleware pipelines should likely opt-in as well.

This flag would be marked deprecated from the outset, and removed for version 2.0.

Problems with the approach:

  • Requires intervention in order to enable the flag.
  • Depending on how the flag is enabled, there may be breakage when moving to 2.0. For example, if using a setter method, removing that method in 2.0 would lead to a BC break for users. If using a constructor argument, no issue would be present, but detecting the constructor argument and notifying users later to update and remove it would pose problems.

Error middleware

Alternately, we could introduce a special error middleware. This could compose an ErrorGenerator and potentially error handler listeners, and then be injected into the middleware pipeline, as the last error middleware. When invoked, it would delegate to the error generator and, if incorporating listeners, trigger them.

The problems with the approach are:

  • It requires changing existing pipelines to add the middleware.
  • Any triggering of the middleware will de facto result in a deprecation notice, as it is error middleware.
  • Users will need to remove the middleware before upgrading to 2.0, as that version does not allow error middleware.

Special "Final Handler"

When prototyping the error handler ideas for my own website originally, I created a special "Final Handler" that wrapped my error handler, and injected this into the application (instead of the no-op final handler), while simultaneously injecting the error handler as middleware. This approach meant that I was able to update from 1.3 to 2.0 with no issues. During 1.3 usage, the final handler was triggered for errors (as it received the $error argument due to Dispatch triggering an error middleware, but no error middleware being present), while in 2.0, the final handler would only receive the request/response pair, and return the response verbatim.

The problems with this approach:

  • It requires changing existing pipelines to add the ErrorHandler middleware in the outer layer. (This is true anyways.)
  • It requires swapping FinalHandler implementations for 1.3. (Potentially also true anyways.)
  • When upgrading to 2.0, even though things continue to "just work", users would need to remember to switch final handlers again, and/or remember to remove the error handler argument when creating the final handler. (In other words, changes are necessary over multiple versions.).

Alternate pipeline implementation

Another approach is to introduce alternate versions of:

  • Dispatch; implementation would be identical except that it would not wrap middleware dispatch in a try/catch block.
  • Next; implementation would be essentially identical except for the version of Dispatch used. Additionally, if a $err argument is provided:
    • if it is a Throwable/Exception, it would re-raise it.
    • if a string, it would raise an exception with the string as a message
    • for other non-null values, it would raise an exception indicating an unknown error occurred.
  • MiddlewarePipe; implementation would be identical, except for the version of Next used.

In version 2.0, the original versions would be used (where appropriate; Dispatch goes away in version 2 currently), and these alternate implementations would simply extend those with the original names. The alternate Next implementation would remove the special $err handling, as it would no longer be relevant.

This would provide an opt-in approach. Users would update their code to use the new MiddlewarePipe variant, and the code would continue to work with version 2.0. After updating to 2.0, they could update to use the original MiddlewarePipe, but could continue to use the alternate class as well, though we would likely deprecate it with the 2.0 release.

Those that do not update would need to make changes to their pipeline on upgrading to version 2.

Problems associated with this path:

  • We'd be introducing new classes for the 1.X series that we'd be deprecating with the 2.X series. We would recommend updating code twice, once to adopt the new pipeline features, and once on upgrade to 2.0 to use the originals again.
  • It requires changing existing pipelines to add the ErrorHandler middleware in the outer layer. (This is true anyways.)
  • It recommends swapping FinalHandler implementations (to NoopFinalHandler) for 1.3. (True anyways.)

Combine flag with alternate implementation strategy

Alternately, we could combine the ideas of the flag with the alternate implementations: users would opt-in by providing a flag to the MiddlewarePipe, and this would then vary behavior in the Next and Dispatch classes:

  • When enabled, Next, on receiving an $err argument, would raise an exception (as noted above).
  • When enabled, Dispatch would either re-raise exceptions or omit the try/catch block entirely.

We could do this similarly to the response prototype, by providing a setter; in 2.0, it would be a no-op, and potentially raise a deprecation notice. Users would then opt-in to the new error handling, and, if done in the outermost layer, the new error handling would apply for the entire application.

Problems with this course of action:

  • It requires changing existing pipelines to add the ErrorHandler middleware in the outer layer. (This is true anyways.)
  • It requires swapping the FinalHandler implementation for the NoopFinalHandler for 1.3. (Potentially also true anyways.)
  • When upgrading to 2.0, even though things continue to "just work", users would need to remember to remove the call to set the flag; this could possibly be prompted by a deprecation notice, however.

Suggested course

My suggestion is to go with the last option, using a flag to alter the behavior of the existing Dispatch and Next implementations. This introduces no backwards compatibility breaks, and can be adopted at the outer-most application layer to introduce the new error handling functionality. This would make implementation in Expressive far easier as well, as that project could inject the flag and add the error middleware automatically in an immediate minor release with no BC breaks; a later major release could remove the flag invocation.

Usage would then become:

$pipeline = new MiddlewarePipe();
$pipeline->raiseExceptionsByDefault();
$pipeline->setResponsePrototype(new Response());

$pipeline->pipe(ErrorHandler::class());
$pipeline->pipe(/* ... */);
// etc. ...
$pipeline->pipe(NotFoundHandler::class());

$pipeline($request, $response, new NoopFinalHandler());

Documentation on creating middleware

The documentation about creating middleware is still using the legacy middleware signature

function (
    Psr\Http\Message\ServerRequestInterface $request,
    Psr\Http\Message\ResponseInterface $response,
    callable $next
) : Psr\Http\Message\ResponseInterface

Would it make sense to update the documentation to describe how now a middleware should be a class implementing Interop\Http\ServerMiddleware\MiddlwareInterface, as also described in the middleware.md section?

Path prefix stripped in Next

In an Expressive app, I've been trying to do some logic based on the route path in a middleware where the path is set to a specific value ('/rest').

return [

    'middleware_pipeline' => [
        'pre-routing' => [
            'path' => '/rest',
            'middleware' => [
                Middleware\PathVersionMiddleware::class,
            ],
            'priority' => 11,
        ],
    ],

    // [...]

];

I've seen that Zend\Stratigility\Next strips the specified part from the beginning of the path for that middleware only, so if the dispatched route is '/rest/v1/foo/bar', doing $request->getUri()->getPath() inside that middleware results in /v1/foo/bar, while doing it on any other middleware where the path has not been set, results in /rest/v1/foo/bar

Is that the intended behavior? I find it a little bit counterintuitive.

Router?

I was wondering why there is a router integrated in this package. Are there use cases where you would use it instead of a dedicated router(-middleware)? Or is it just for prototyping?

Question regarding performance

Hi @weierophinney ,

As stratigility can just route with static routes, I am thinking, it may be good if it can provide a router or a default router interface so that it can do dynamic routing and no one need to create another application / middleware to do the same.

I think your idea is to call

$app    = new MiddlewarePipe();
$app->pipe($zf2mvc);
$app->pipe($symfony2app);
$server = Server::createServer($app,
  $_SERVER,
  $_GET,
  $_POST,
  $_COOKIE,
  $_FILES
);
$server->listen();

etc. Though it looks nice, I wish if it can have dynamic routing built by default so the overhead of looking the routes and dispatching can be reduced.

Thank you.

Dispatch break PSR-7

I have just started play with middlewares and psr-7. Current results are visible here https://github.com/snapshotpl/MiddlewareSandbox.

However is one thing what I don't understand (or probably I have found issue).

Dispatch class allow only Zend\Stratigility\Http\Request and Zend\Stratigility\Http\Response implementation of PSR-7 https://github.com/zendframework/zend-stratigility/blob/master/src/Dispatch.php#L53

In my middleware I change response to Diactoros implementation https://github.com/snapshotpl/MiddlewareSandbox/blob/master/src/Middlewares/HtmlMiddleware.php#L18 but I get exception:

Argument 4 passed to Zend\Stratigility\Dispatch::__invoke() must be an instance of Zend\Stratigility\Http\Response, instance of Zend\Diactoros\Response\JsonResponse given

Which is expected behavior in OOP paradigm.

It's a expected behavior of middlewares and PSR-7?

Performance - benchmarks?

I'd like to encourage you to publish some benchmarks.

I have a middleware stack currently with just one route to a single piece of thin middleware doing nothing fancy, and it's already too slow to be of any use in production. I haven't gotten around to looking for bottlenecks yet, but commenting out the middleware (and just dispatching my router manually) eliminated the performance issue completely, e.g. seconds to run 1000 requests with ab -n 1000 -c 20 vs minutes with the middleware stack in between.

Have you benchmarked this middleware stack for performance at all?

FinalHandler should not display stack traces by default

This seems like a dangerous default to me, and it contradicts the Expressive documentation here.

Specifically:

If the FinalHandler was initialized with an option indicating that it is in development mode, it writes the exception stack trace to the response body.

However, the default initialization of the FinalHandler (which is what Expressive does in the default error pipeline) leaves the env option unset, which causes ! isset($this->options['env']) to evaluate to true and creates the Development error message. You'd actually need to specify ['env' => 'production'] in the constructor in order to not display the stack trace.

I think the reverse would be a safer default - you must explicitly state that you're in development in order for the stack trace to appear (which is what the Expressive documentation indicates).

Specifically: if (isset($options['env']) && $options['env'] === 'development')

Either that, or correct the Expressive documentation.

I like the former better, but I realize it's technically a BC break (if your application needs to display stack traces in Production by default, that is...), so it should be discussed (assuming I'm not the only one who thinks this is an issue).

Thoughts?

Error handling

Me and a colleague have been working hard trying to bypass the error handler - we want to use an alternative error handler (booboo) but the middleware stack catches and handles exceptions before they can get to the error handler.

We noticed the onerror callback in $options of the FinalHandler constructor, but this gets constructed internally in MiddlewarePipe, where everything is declared private, so we had no luck attempting to extend the class in order to provide the option.

How do we turn off built-in error handling?

Move Interfaces into New Package

As far as I can see, Stratigility declares 3 interfaces:

  • Zend\Stratigility\Http\ResponseInterface
  • Zend\Stratigility\Http\ErrorMiddlewareInterface
  • Zend\Stratigility\Http\MiddlewareInterface

The first two will be removed with 2.0.0, the last one will introduce a BC if #15 get accepted.

Interfaces are nice, as long as they allow interoperability, but as long as they are bundled with a concrete implementation consumers cannot reuse their work in other projects.

It would be really nice if you may consider to move those interfaces into a new package, e.g. zendframework/zend-stratigility-contracts and release a 1.3 version. If time comes to release 2.0, just release a contracts 2.0 version as well.

Thus, middlewares can require "zendframework/zend-stratigility-contracts" : "^1.3 || ^2.0" in their composer.json files…

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.