-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
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
Fix #15438 PSR middleware #15459
Conversation
klimov-paul
commented
Jan 5, 2018
Q | A |
---|---|
Is bugfix? | no |
New feature? | yes |
Breaks BC? | yes |
Tests pass? | yes |
Fixed issues | #15438, #10659 |
@@ -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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
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.
Why do we have (nearly) the same Middleware-related code in |
There was a problem hiding this 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
Middleware at 'yii\http\server' is bound to the PSR 15 interfaces and use them for type-restrictions. Attempt of usage for the |
There was a problem hiding this 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 = []) |
There was a problem hiding this comment.
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 = []; |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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 = []; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 = []) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
framework/composer.json
Outdated
@@ -77,6 +77,9 @@ | |||
"bower-asset/punycode": "1.3.*", | |||
"bower-asset/yii2-pjax": "~2.0.1" | |||
}, | |||
"suggest": { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 = []) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 = []) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as #15459 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.
Middleware can be inserted at any stage of the request processing. This PR provides 3 tiers:
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. As for defining middleware via URL rules - it should be applied as a separated changeset. It could be easily integrated at |
I see I'm getting a reputation :-p |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good except:
- package dependency should be in require in composer.json.
- Please take a look at: [PSR-15] Provide middleware dispatch examples php-fig/fig-standards#996
https://packagist.org/packages/http-interop/http-server-handler |
Conflicts: composer.json framework/UPGRADE.md framework/composer.json tests/framework/console/ControllerTest.php
Any progress on PSR-7 and PSR-15 support on Yii? Is it decided to be supported? |
It is decided to be supported yes but no further progress was made yet. |
Closing since it won't be applied to 2.x. |