zendframework / zend-stratigility

Middleware for PHP built on top of PSR-7 and PSR-15
BSD 3-Clause "New" or "Revised" License
235 stars 57 forks source link

Throw EmptyPipelineException if passed in continuation is invoked more than once. #186

Closed alihammad-gist closed 5 years ago

alihammad-gist commented 5 years ago
Xerkus commented 5 years ago

This PR can not be accepted as it breaks intended behavior of the Next handler while not addressing the actual issue of allowing multiple invocations.

The problem you experience is due to your usage. Is there a reason why you need to invoke it multiple times? Normally middleware acts on request then potentially delegates to next handler then acts on the response returned by next handler.

Do you use this middleware outside of its intended usage as a part of middleware pipe? https://github.com/zendframework/zend-stratigility/blob/e62d1c5006ad4359b44d7700c78ca00939143fbc/src/MiddlewarePipe.php#L81-L84

I will keep this PR open for your feedback.

alihammad-gist commented 5 years ago

All test were passing, is there an implicit Next behavior I am ignoring? I added the test, If the test captures a valid Use case instead of an exception please regect the PR. What is the standard behavior when middleware delegates to the next handler twice? This captures the edge case when a continuation is called twice, you are right about normally middle ware delegates to the next handler (once).

https://github.com/zendframework/zend-stratigility/blob/90da95e4b18569b9dd4fa3f193c3209ca807d6df/test/NextTest.php#L219-L239

Xerkus commented 5 years ago

Hm. Next does not provide a way to push to queue once it has been passed to constructor, so one use case I had in mind is out. That also means that only questionable edge case behavior is affected by pushing final handler to queue. I was too fast to reject it.

Going back to original issue, we have two options here: go ahead and explicitly forbid second invocation or allow multiple invocations.

Second option is easy to achieve by passing cloned Next handler to queued middlewares. Next handler is a hot path though and it will have a performance impact as it will have to be cloned for each piped middleware.

$next = clone $this;
$middleware = $next->queue->dequeue();
return $middleware->process($request, $next);

public function __clone()
{
    $this->queue = clone $this->queue;
}

So, my question stands: Is there a reason why you need to invoke it multiple times?

@weierophinney I would also like your input on the two options outlined above.

alihammad-gist commented 5 years ago

So, my question stands: Is there a reason why you need to invoke it multiple times?

I do not want to invoke it multiple times, I rather have it throw an exception if it is invoked more than once so I am told explicitly that I have done something wrong.

Xerkus commented 5 years ago

After discussion in slack we came to conclusion that Next handler should not be allowed to be invoked multiple times from the same context as it constitutes bad design and leads to unpredictable and/or hard to test and debug results.

weierophinney commented 5 years ago

Thanks, @alihammad-gist!