-
Notifications
You must be signed in to change notification settings - Fork 195
Implement programmatic pipeline features #396
Implement programmatic pipeline features #396
Conversation
Ready for review! Pinging:
Thanks in advance! |
"cs-fix": "phpcbf", | ||
"test": "phpunit", | ||
"cs-check": "phpcs --colors", | ||
"cs-fix": "phpcbf --colors", |
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.
--colors for phpcs and phpcbf is not needed since the new coding standard will have them enabled via the ruleset.xml
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.
Good to know; I'll push that change shortly, after verifying the other build changes pass.
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've read the documentation changes so far. Later tonight or tomorrow night I'll go through the code.
I like vfsStream. We should used use it for the skeleton as well. It will make testing a lot cleaner and safer.
- `delete($path, $middleware, $name = null)` | ||
- `route($path, $middleware, array $methods = null, $name = null)` | ||
|
||
Each returns a `Zend\Expressive\Router\Route` instance; this is useful if you |
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.
Thanx for mentioning this. No need anymore for routes via config.
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.
This has actually been true all along... just not well documented.
- Decorate the middleware in a `Zend\Stratigility\Middleware\CallableMiddlewareWrapper` | ||
instance (which also requires a `$responsePrototype`). | ||
|
||
We recommend that you begin writing middleware to follow the http-interop |
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.
Maybe add an example here to clarify it a bit. I think it's something like this? (Disclaimer: from memory and untested)
<?php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Interop\Http\Middleware\ServerMiddlewareInterface;
use Interop\Http\Middleware\DelegateInterface;
class MyMiddleware implements ServerMiddlewareInterface
{
public function __construct()
{
}
/**
* @param Request $request
* @param DelegateInterface $delegate
*
* @return Response
*/
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
{
// Do something (with the request) first
// Call the next middleware and wait for the response
$response = $delegate->process($request);
// Do something (with the response) before returning the response
// Return the response
return $response;
}
}
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.
Excellent feedback! I'll incorporate that shortly!
middleware_ using the caught exception as the `$err` argument. | ||
|
||
- The "Final Handler". This is a special middleware type with the signature | ||
`function ServerRequestInterface $request, ResponseInterface $response, $err = null)`, |
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.
Probably missed a (
|
||
```php | ||
include 'config/pipeline.php'; | ||
include 'config/routes.php'; |
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.
Shouldn't these use 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.
Yeah, probably. Thanks for the suggestion; will implement shortly.
namespace ZendTest\Expressive; | ||
|
||
use Closure; | ||
use InvalidArgumentException; |
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 is never used.
|
||
namespace Zend\Expressive; | ||
|
||
use ReflectionProperty; |
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 is never used.
use Zend\Diactoros\Response\EmitterInterface; | ||
use Zend\Expressive\Application; | ||
use Zend\Expressive\ApplicationUtils; | ||
use Zend\Expressive\Exception; |
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 is no longer used here, can be removed.
use Interop\Container\ContainerInterface; | ||
use PHPUnit_Framework_TestCase as TestCase; | ||
use Zend\Expressive\Container\ErrorResponseGeneratorFactory; | ||
use Zend\Expressive\Middleware\ErrorResponseGenerator; |
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 is never used.
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 is - but using the ::class
notation, for assertInstanceOf
checks.
use org\bovigo\vfs\vfsStream; | ||
use org\bovigo\vfs\vfsStreamDirectory; | ||
use PHPUnit_Framework_TestCase as TestCase; | ||
use Prophecy\Argument; |
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.
Unused, please remove.
use Zend\Diactoros\Response\EmitterInterface; | ||
use Zend\Expressive\Application; | ||
use Zend\Expressive\ApplicationUtils; |
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 can't see this class in the library. Where is it?
* - 'Zend\Diactoros\Response\EmitterInterface'. If missing, an EmitterStack is | ||
* created, adding a SapiEmitter to the bottom of the stack. | ||
* - 'config' (an array or ArrayAccess object). If present, and it contains route | ||
* definitions, these will be used to seed routes in the Application instance | ||
* before returning it. | ||
* | ||
* When introspecting the `config` service, the following structure can be used | ||
* to define routes: | ||
* Please see the `Zend\Expressive\ApplicationUtils` class for details on how |
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.
As above, I can't see this class in the library. Am I missing something?
*/ | ||
private function createOptionValue($value, $indentLevel = 1) | ||
{ | ||
if (is_array($value) || $value instanceof Traversable) { |
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.
Traversable
should be imported or \Traversable
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.
Done!
class WhoopsErrorResponseGenerator | ||
{ | ||
/** | ||
* @var Whoops |
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.
Seems to be wrong type here.
* @param int $indentLevel | ||
* @return string | ||
*/ | ||
private function formatOptions($options, $indentLevel = 1) |
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.
Is $options
arg always an array? Can we add type hint 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.
It could be an ArrayObject
.
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.
So then PHPDoc should be updated.
## http-interop | ||
|
||
Stratigility 1.3 provides the ability to work with [http-interop middleware | ||
0.2.0](https://github.com/http-interop/http-middleware/tree/ff545c87e97bf4d88f0cb7eb3e89f99aaa53d7a9). |
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.
Maybe better here link to tagged version:
https://github.com/http-interop/http-middleware/tree/0.2.0
In
and also:
Is it gonna be done in this PR, or PR #376 will be merged before release 1.1? |
<?php | ||
/** | ||
* Expressive middleware pipeline | ||
*/ |
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.
Maybe will be nice to add here also:
@var \Zend\Expressive\Application $app
because then, in this file $app
variable is gonna be used.
The same about TEMPLATE_ROUTES
.
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.
Done!
Migration script:
This is because migration script is not adding:
Into configuration, but:
is added into I think it should be done automatically (in migration script) or it should be noted in migration notes, that this action is required to be done manually. |
One more issue. This was my config based configuration - [...]
'middleware_pipeline' =>
'my-path' => [
'path' => '/my-path',
'middleware' => [
ApplicationFactory::ROUTING_MIDDLEWARE,
\App\Middleware\MyMiddleware::class,
\App\Middleware\OtherMiddleware::class,
],
], it was successfully converted to middleware pipeline (via migration script) - [...]
$app->pipe('/my-path', [
\Zend\Expressive\Application::ROUTING_MIDDLEWARE,
\App\Middleware\MyMiddleware::class,
\App\Middleware\OtherMiddleware::class,
]);
$app->pipeRoutingMiddleware();
[...] And then piping is failing, because
Am I doing something wrong or there are missing some scenarios, when we have some routing/dispatch middlewares only for some paths? Update: Ok, I think I know. So - [...]
$app->pipeRoutingMiddleware();
$app->pipe('/my-path', [
\App\Middleware\MyMiddleware::class,
\App\Middleware\OtherMiddleware::class,
]);
[...] The question is now: is my previous middleware-pipeline configuration wrong or the migration script is doing it wrongly? |
Another issue. "Method not allowed". I have routing, let say: $app->post('/foo', \App\Action\Foo::class, 'foo'); So only POST request are allowed. If I try to do I think the problem is in $result = $this->router->match($request);
if ($result->isFailure()) {
if ($result->isMethodFailure()) {
$response = $response->withStatus(405)
->withHeader('Allow', implode(',', $result->getAllowedMethods()));
return $next($request, $response, 405); // <-------
}
[...] and then, the 3rd argument ( |
Should be resolved now; thanks for finding the issue! |
I think this is an edge case we never tested. When you use configuration-based pipelines, as it loops through nested pipelines, if it encounters either of those special constants, it replaces the middleware with the appropriate callable ( However, in the I'm going to see if I can create a test case and resolve this; I think it should be relatively easy to accomplish. |
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.
Hey @weierophinney, thanks for this awesome work! This will definitely make the pipeline way more explicit and clear :)
? $container->get(ErrorResponseGenerator::class) | ||
: null; | ||
|
||
return new ErrorHandler(new Response(), $generator); |
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.
@weierophinney any chance to make it possible to configure ErrorHandler
listeners via config? Or should I override this factory to attach my custom listeners?
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.
Use a delegator factory. 😉
@weierophinney I only see the config setting for the new error handling in the migration guide: Did you forgot EDIT: Thinking about this, I think the config option would be preferred so an upgrade to v2.0 might go smoother since |
|
||
Stratigility 1.3 deprecates its internal request and response decorators, | ||
`Zend\Stratigility\Http\Request` and `Zend\Stratigility\Http\Response`, | ||
respsectively. The main utility of these instances was to provide access 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.
Typo: respectively
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.
Fixed, thanks!
Also phpunit can be upgraded to 5.x since PHP 5.5 support is removed. |
programmatic/declarative statements. Specifically: | ||
|
||
- We recommend putting the pipeline declarations into `config/pipeline.php`. | ||
- We recommend putting the pipeline declarations into `config/routes.php`. |
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 recommend putting the pipeline declarations into `config/pipeline.php`.
We recommend putting the routing declarations into `config/routes.php`.
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.
Fixed, thanks!
You can do either; I didn't document the method, as technically it's part of the Stratigility 1.3 API, and documented there. |
Done in latest commit; thanks! |
This patch adds `bin/expressive-tooling`, which will help you install and remove the zendframework/zend-expressive-tooling package.
- is_executable does not work correctly on Windows; use file_exists. - Grab $argv[1] instead of $argv[2]; default to an empty string.
…ckage Details how to install and/or uninstall the tooling, and the commands it exposes.
- Adds fig/http-message-util as a dependency - Updates `NotFoundHandler` to use `StatusCodeInterface::STATUS_NOT_FOUND` instead of `404`.
86f136e
to
af52e19
Compare
This patch implements the Programmatic Pipelines RFC requirements.
TODO
Zend\Expressive\Middleware\ErrorResponseGenerator
Zend\Expressive\Middleware\WhoopsErrorResponseGenerator
Zend\Expressive\Middleware\NotFoundHandler
Zend\Expressive\Container\ErrorResponseGeneratorFactory
Zend\Expressive\Container\WhoopsErrorResponseGeneratorFactory
Zend\Expressive\Container\NotFoundHandlerFactory
Zend\Expressive\Container\ErrorHandlerFactory
Zend\Expressive\ApplicationConfigInjectionTrait
, and compose it intoApplication
.Zend\Expressive\Container\ApplicationFactory
to vary creation and return of theApplication
instance based on thezend-expressive.programmatic_pipeline
configuration flag.Zend\Expressive\Container\ApplicationFactory
to use the methods defined inApplicationConfigInjectionTrait
to inject pipeline and routed middleware from configuration into theApplication
instance.Zend\Expressive\Application
to emit a deprecation notice frompipeErrorMiddleware()
.Zend\Expressive\MarshalMiddlewareTrait
to allow marshalling http-interop middleware.ApplicationFactory
.Tooling support
The following tools were written in zendframework/zend-expressive-tooling to support user migrations to work with the above changes:
getOriginal*()
calls togetAttribute(*)
calls on the request object.References