zendframework / zend-expressive

PSR-15 middleware in minutes!
BSD 3-Clause "New" or "Revised" License
711 stars 197 forks source link

Config-driven vs. programmatic approach #494

Closed wesperinteractive closed 6 years ago

wesperinteractive commented 7 years ago

Adding routed middleware via config-driven approach:

In my configuration i have one route:

namespace Cms;

return [
   [
       'name'            => 'cms::index',
       'path'            => '/cms',
       'middleware'      => Action\IndexAction::class,
       'allowed_methods' => ['GET'],
   ]
];

The _programmaticpipeline directive is set to true, so i use the $app->injectRoutesFromConfig() method to add my route to the application (in public/index.php):

$app = $container->get(\Zend\Expressive\Application::class);

$app->injectRoutesFromConfig();

$app->run();

Adding routed middleware via programmatic approach:

Doing the same via programmatic approach (in public/index.php):

$app = $container->get(\Zend\Expressive\Application::class);

$app->get('/cms', 'Cms\Action\IndexAction', 'cms::index');

$app->run();

The problem:

In config-driven approach the routed middleware is not prepared in the route method of the Application object.

https://github.com/zendframework/zend-expressive/blob/master/src/Application.php#L294

The $path is an instance of Router/Route, the $route variable will be defined (line 304), the routed middleware won't be prepared (line 312) and won't be stored in the route.

The routed middleware will be prepared only later in the DispatchMiddleware (https://github.com/zendframework/zend-expressive/blob/master/src/Middleware/DispatchMiddleware.php#L72). The prepared routed middleware won't be stored here either.

In programmatic approach the routed middleware is always prepared and stored in the route (in the route method of the Application).

The result of the different behavior:

Calling the getMatchedMiddleware method on the RouteResult object will return a string (Cms\Action\IndexAction) or an object (LazyLoadingMiddleware) depending on what approach was used.


Dependency used in the example:

namespace Cms;

use Zend\ServiceManager\Factory\InvokableFactory;

return [
   'factories'  => [
       Action\IndexAction::class => InvokableFactory::class
   ]
];
geerteltink commented 7 years ago

Some more details:

config-driven calls $app->route(Route); with a Route object. https://github.com/zendframework/zend-expressive/blob/master/src/ApplicationConfigInjectionTrait.php#L167

programmatic calls $app->route(...$routeArgs); with some strings and arrays. https://github.com/zendframework/zend-expressive/blob/master/src/Application.php#L160

That's why it's skipped for config-driven approach: https://github.com/zendframework/zend-expressive/blob/master/src/Application.php#L303

weierophinney commented 6 years ago

This is fixed in versions 2.2+ and 3.0+.