Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #15438 PSR middleware #15459

Closed
wants to merge 25 commits into from

Conversation

klimov-paul
Copy link
Member

Q A
Is bugfix? no
New feature? yes
Breaks BC? yes
Tests pass? yes
Fixed issues #15438, #10659

@klimov-paul klimov-paul added severity:BC breaking Either breaks backwards compatibility or fixed previously made breakage type:enhancement labels Jan 5, 2018
@klimov-paul klimov-paul added this to the 2.1.0 milestone Jan 5, 2018
@klimov-paul klimov-paul mentioned this pull request Jan 5, 2018
@@ -186,12 +213,12 @@ public function run($route, $params = [])
{
$pos = strpos($route, '/');
if ($pos === false) {
return $this->runAction($route, $params);
return $this->runAction(Yii::$app->getRequest(), $route, $params);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had a discussion about injecting Request instance in controller. Seems to be useful in this case

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way to do this in the middleware scope is having a setter for the request instance allowing request to be re-set per each action execution in the same way Controller::$action behaves.
Such approach was rejected at #14645.
This matter should be solved afterwards.

if (strpos($route, '/') !== false) {
[$id, $route] = explode('/', $route, 2);
list($id, $route) = explode('/', $route, 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure.
Should be fixed.

@SilverFire
Copy link
Member

Why do we have (nearly) the same Middleware-related code in yii\base and yii\http\server?

Copy link
Member

@SilverFire SilverFire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work. To be polished

@klimov-paul
Copy link
Member Author

Why do we have (nearly) the same Middleware-related code in yii\base and yii\http\server?

Middleware at 'yii\http\server' is bound to the PSR 15 interfaces and use them for type-restrictions. Attempt of usage for the Interop\Http\Server\MiddlewareInterface instance at yii\base\MiddlewareDispatcher will end with PHP error.
Still if anyone has a good idea - be my guest.

Copy link
Member

@samdark samdark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dispatcher looks good. I think that defining middleware stack should not be tied to modules/controllers but instead be done at router level.

* @param string $id the ID of the action to be executed.
* @param array $params the parameters (name-value pairs) to be passed to the action.
* @return mixed the result of the action.
* @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
* @see createAction()
*/
public function runAction($id, $params = [])
public function runAction($request, $id, $params = [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be type-hinted with Request.

* @see MiddlewareDispatcherInterface::dispatch()
* @since 2.1.0
*/
public $middleware = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Middleware is meant to be set on the router/url manager config level so it could be applied to a number of routes some of which could be non-controllers. It's not meant to be part of the controller itself although previously filters were controller-only thing.

@@ -22,6 +22,8 @@ Yii Framework 2 Change Log
- Enh #13702: Added support for PSR-3 'Logger' (klimov-paul)
- Enh #13706: 'Profiler' layer extracted (klimov-paul)
- Enh #15410: Added serialization abstraction layer under `yii\serialize\*` namespace (klimov-paul)
- Chg #10659: Method `runAction()` now is invoked for every module instance in the request handling chain, allowing custom implementation in particular module (klimov-paul)
- Enh #15438: Added middleware support including PSR-15 'HTTP Server Request Handlers' compatibility (klimov-paul)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are upcoming changes to be merged into PSR-15 spec soon.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes are only clarifications, and will not touch the actual interfaces and their relations to one another.

} else {
$runAction = false;
break;
$result = Yii::$app->getMiddlewareDispatcher()->dispatch($request, $this->middleware, function () use ($action, $params) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be moved out of controller to upper level (router which is Application in our case).

* @see MiddlewareDispatcherInterface::dispatch()
* @since 2.1.0
*/
public $middleware = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. I think it would be great to try moving it to routing level.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Providing middleware at the routing level should be handled by separated changeset in the scope of #15363

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

* @param string $route the route that specifies the action.
* @param array $params the parameters to be passed to the action
* @return mixed the result of the action.
* @throws InvalidRouteException if the requested route cannot be resolved into an action successfully.
*/
public function runAction($route, $params = [])
public function runAction($request, $route, $params = [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be type-hinted with Request.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlikely.
PSR middleware does not provide any garantee the original request instance to be preserved. It passes ServerRequestInterface instance over the tiers, but can modify it in process or even recreate at will.

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler)
{
    $newRequst = new ExternalRequest();
    return $handler->handle($newRequst);
}

Thus for the web application the correct typehinting will be ServerRequestInterface, while for the console application it will be yii\console\Request (or yii\base\Request).

yii\base\Module is suitable both for web and console application, thus such bounds can not be applied without introduction of the separated yii\web\Module and yii\console\Module. Such change will cause a lot of trouble. In particular: yii2-gii will have to implement 2 separated modules for the web and console interfaces, which then will require separated configuration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It passes ServerRequestInterface instance over the tiers, but can modify it in process or even recreate at will.

Yes but what's returned is still ServerRequestInterface, isn't it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. I've got what you mean. Console. Alright then.

@@ -77,6 +77,9 @@
"bower-asset/punycode": "1.3.*",
"bower-asset/yii2-pjax": "~2.0.1"
},
"suggest": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can require it safely. It's light dependency containing interface only.

Copy link
Member Author

@klimov-paul klimov-paul Jan 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for this package to be mandatory.
In case particular project does not use PSR middleware - this package is a simple garbage, which is never used. In case some project does use some PSR middleware - it is more likely to coming from another composer package, which will have 'http-server-middleware' among its dependencies for sure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're potentially converting action filters to middlewares, aren't we?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I insist on having it in require.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're potentially converting action filters to middlewares, aren't we?

At the present state, we are not. If such changeset will appear, it will include composer.json changeset inside it.

I insist on having it in require.

Unless the official PSR-15 composer package appear this is risk prone. I can agree having mandatory dependency on psr/http-server-middleware but not on http-interop/http-server-middleware

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Makes sense. I hope PHP-FIG will finish PSR-15 soon and there will be official interface package available.

* @throws Exception if the route is invalid
*/
public function runAction($route, $params = [])
public function runAction($request, $route, $params = [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be type-hinted with Request.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using yii\console\Request as a type restriction is a risk prone. Unless there is some yii\console\RequestInterface interface type-restriction introduction reduces flexibility here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

* @param string $id the ID of the action to be executed.
* @param array $params the parameters (name-value pairs) to be passed to the action.
* @return int the status of the action execution. 0 means normal, other values mean abnormal.
* @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
* @throws Exception if there are unknown options or missing arguments
* @see createAction
*/
public function runAction($id, $params = [])
public function runAction($request, $id, $params = [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be type-hinted with Request.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

@klimov-paul
Copy link
Member Author

I think that defining middleware stack should not be tied to modules/controllers but instead be done at router level.

Middleware can be inserted at any stage of the request processing. This PR provides 3 tiers:

  • controller level
  • particualr module level
  • whole application level

If I withdraw module-level middleware it will end up in complains of developers like @SamMousa, saying they are lacking flexibility at the module level.
Allowing middleware application for the module automatically allows it for the whole application.
Controller layer middleware can be useful as well, allowing passing request of articular module section to the external middleware.

As for defining middleware via URL rules - it should be applied as a separated changeset. It could be easily integrated at yii\web\Application::handleRequest().
This matter relates to #15363.

@SamMousa
Copy link
Contributor

SamMousa commented Jan 8, 2018

I see I'm getting a reputation :-p

Copy link
Member

@samdark samdark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good except:

  1. package dependency should be in require in composer.json.
  2. Please take a look at: [PSR-15] Provide middleware dispatch examples php-fig/fig-standards#996

@rob006
Copy link
Contributor

rob006 commented Jan 27, 2018

psr/http-server-handler was released and http-interop/http-server-handler is deprecated.

https://packagist.org/packages/http-interop/http-server-handler
https://packagist.org/packages/psr/http-server-handler

@samdark samdark closed this Jun 22, 2018
@rob006 rob006 changed the base branch from 2.1 to 3.0 July 7, 2018 15:48
@rob006 rob006 reopened this Jul 7, 2018
@olleharstedt
Copy link

Any progress on PSR-7 and PSR-15 support on Yii? Is it decided to be supported?

@samdark
Copy link
Member

samdark commented Jul 30, 2018

It is decided to be supported yes but no further progress was made yet.

@samdark
Copy link
Member

samdark commented Mar 12, 2019

Closing since it won't be applied to 2.x.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
severity:BC breaking Either breaks backwards compatibility or fixed previously made breakage type:enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants