Skip to content

Commit

Permalink
Created a MiddlewareListener
Browse files Browse the repository at this point in the history
  • Loading branch information
ezimuel committed Oct 1, 2015
1 parent fe65cba commit 47ac19a
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 66 deletions.
4 changes: 3 additions & 1 deletion src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* - RouteListener
* - Router
* - DispatchListener
* - MiddlewareListener
* - ViewManager
*
* The most common workflow is:
Expand All @@ -53,7 +54,7 @@ class Application implements
const ERROR_EXCEPTION = 'error-exception';
const ERROR_ROUTER_NO_MATCH = 'error-router-no-match';
const ERROR_MIDDLEWARE_CANNOT_DISPATCH = 'error-middleware-cannot-dispatch';

/**
* @var array
*/
Expand All @@ -66,6 +67,7 @@ class Application implements
*/
protected $defaultListeners = [
'RouteListener',
'MiddlewareListener',
'DispatchListener',
'HttpMethodListener',
'ViewManager',
Expand Down
32 changes: 7 additions & 25 deletions src/DispatchListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\Exception\InvalidControllerException;
use Zend\Stdlib\ArrayUtils;
use Zend\Psr7Bridge\Psr7ServerRequest as Psr7Request;
use Zend\Psr7Bridge\Psr7Response;

/**
* Default dispatch listener
Expand Down Expand Up @@ -63,29 +61,10 @@ public function attach(EventManagerInterface $events)
*/
public function onDispatch(MvcEvent $e)
{
$routeMatch = $e->getRouteMatch();
$request = $e->getRequest();
$application = $e->getApplication();
$response = $application->getResponse();
$serviceManager = $application->getServiceManager();

// middleware?
$middleware = $routeMatch->getParam('middleware', false);
if (false !== $middleware) {
if (is_string($middleware) && $serviceManager->has($middleware)) {
$middleware = $serviceManager->get($middleware);
}
if (!is_callable($middleware)) {
$middleware = is_string($middleware) ? $middleware : get_class($middleware);
$return = $this->marshalControllerNotFoundEvent($application::ERROR_MIDDLEWARE_CANNOT_DISPATCH, $middleware, $e, $application);
return $this->complete($return, $e);
}
$return = $middleware(Psr7Request::fromZend($request), Psr7Response::fromZend($response));
return $this->complete(Psr7Response::toZend($return), $e);
}

$routeMatch = $e->getRouteMatch();
$application = $e->getApplication();
$serviceManager = $application->getServiceManager();
$controllerName = $routeMatch->getParam('controller', 'not-found');
$events = $application->getEventManager();
$controllerLoader = $serviceManager->get('ControllerManager');

if (!$controllerLoader->has($controllerName)) {
Expand All @@ -107,14 +86,17 @@ public function onDispatch(MvcEvent $e)
$controller->setEvent($e);
}

$request = $e->getRequest();
$response = $application->getResponse();

try {
$return = $controller->dispatch($request, $response);
} catch (\Exception $ex) {
$e->setError($application::ERROR_EXCEPTION)
->setController($controllerName)
->setControllerClass(get_class($controller))
->setParam('exception', $ex);
$results = $events->trigger(MvcEvent::EVENT_DISPATCH_ERROR, $e);
$results = $application->getEventManager()->trigger(MvcEvent::EVENT_DISPATCH_ERROR, $e);
$return = $results->last();
if (! $return) {
$return = $e->getResult();
Expand Down
116 changes: 116 additions & 0 deletions src/MiddlewareListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Mvc;

use ArrayObject;
use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\Exception\InvalidControllerException;
use Zend\Stdlib\ArrayUtils;
use Zend\Psr7Bridge\Psr7ServerRequest as Psr7Request;
use Zend\Psr7Bridge\Psr7Response;

class MiddlewareListener extends AbstractListenerAggregate
{
/**
* Attach listeners to an event manager
*
* @param EventManagerInterface $events
* @return void
*/
public function attach(EventManagerInterface $events)
{
$this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH, [$this, 'onDispatch'], 1000);
}

/**
* Listen to the "dispatch" event
*
* @param MvcEvent $e
* @return mixed
*/
public function onDispatch(MvcEvent $e)
{
$routeMatch = $e->getRouteMatch();
$middleware = $routeMatch->getParam('middleware', false);
if (false === $middleware) {
return;
}

$request = $e->getRequest();
$application = $e->getApplication();
$response = $application->getResponse();
$serviceManager = $application->getServiceManager();
$middlewareName = is_string($middleware) ? $middleware : get_class($middleware);

if (is_string($middleware) && $serviceManager->has($middleware)) {
$middleware = $serviceManager->get($middleware);
}
if (!is_callable($middleware)) {
$return = $this->marshalMiddlewareNotCallable($application::ERROR_MIDDLEWARE_CANNOT_DISPATCH, $middlewareName, $e, $application);
$e->setResult($return);
return $return;
}
try {
$return = $middleware(Psr7Request::fromZend($request), Psr7Response::fromZend($response));
} catch (\Exception $ex) {
$e->setError($application::ERROR_EXCEPTION)
->setController($middlewareName)
->setControllerClass(get_class($middleware))
->setParam('exception', $ex);
$results = $events->trigger(MvcEvent::EVENT_DISPATCH_ERROR, $e);
$return = $results->last();
if (! $return) {
$return = $e->getResult();
}
}

if (! $return instanceof \Psr\Http\Message\ResponseInterface) {
$e->setResult($return);
return $return;
}
$response = Psr7Response::toZend($return);
$e->setResult($response);
return $response;
}

/**
* Marshal a middleware not callable exception event
*
* @param string $type
* @param string $middlewareName
* @param MvcEvent $event
* @param Application $application
* @param \Exception $exception
* @return mixed
*/
protected function marshalMiddlewareNotCallable(
$type,
$middlewareName,
MvcEvent $event,
Application $application,
\Exception $exception = null
) {
$event->setError($type)
->setController($middlewareName)
->setControllerClass('Middleware not callable: ' . $middlewareName);
if ($exception !== null) {
$event->setParam('exception', $exception);
}

$events = $application->getEventManager();
$results = $events->trigger(MvcEvent::EVENT_DISPATCH_ERROR, $event);
$return = $results->last();
if (! $return) {
$return = $event->getResult();
}
return $return;
}
}
1 change: 1 addition & 0 deletions src/Service/ServiceListenerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class ServiceListenerFactory implements FactoryInterface
protected $defaultServiceConfig = [
'invokables' => [
'DispatchListener' => 'Zend\Mvc\DispatchListener',
'MiddlewareListener' => 'Zend\Mvc\MiddlewareListener',
'RouteListener' => 'Zend\Mvc\RouteListener',
'SendResponseListener' => 'Zend\Mvc\SendResponseListener',
'ViewJsonRenderer' => 'Zend\View\Renderer\JsonRenderer',
Expand Down
1 change: 1 addition & 0 deletions test/ApplicationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ public function bootstrapRegistersListenersProvider()
return [
['RouteListener', MvcEvent::EVENT_ROUTE, 'onRoute'],
['DispatchListener', MvcEvent::EVENT_DISPATCH, 'onDispatch'],
['MiddlewareListener', MvcEvent::EVENT_DISPATCH, 'onDispatch'],
['SendResponseListener', MvcEvent::EVENT_FINISH, 'sendResponse'],
['ViewManager', MvcEvent::EVENT_BOOTSTRAP, 'onBootstrap'],
['HttpMethodListener', MvcEvent::EVENT_ROUTE, 'onRoute'],
Expand Down
40 changes: 0 additions & 40 deletions test/DispatchListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,44 +122,4 @@ public function testUnlocatableControllerLoaderComposedOfAbstractFactory()
$this->assertArrayHasKey('error', $log);
$this->assertSame('error-controller-not-found', $log['error']);
}

public function testMiddlewareDispatch()
{
$request = $this->serviceManager->get('Request');
$request->setUri('http://example.local/path');

$router = $this->serviceManager->get('HttpRouter');
$route = Router\Http\Literal::factory([
'route' => '/path',
'defaults' => [
'middleware' => function($request, $response) {
$this->assertInstanceOf('Psr\Http\Message\ServerRequestInterface', $request);
$this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response);
$response->getBody()->write('Test!');
return $response;
}
],
]);
$router->addRoute('path', $route);
$this->application->bootstrap();

$controllerLoader = $this->serviceManager->get('ControllerLoader');
$controllerLoader->addAbstractFactory('ZendTest\Mvc\Controller\TestAsset\ControllerLoaderAbstractFactory');

$log = [];
$this->application->getEventManager()->attach(MvcEvent::EVENT_DISPATCH_ERROR, function ($e) use (&$log) {
$log['error'] = $e->getError();
});

$this->application->run();

$event = $this->application->getMvcEvent();
$dispatchListener = $this->serviceManager->get('DispatchListener');
$return = $dispatchListener->onDispatch($event);

$this->assertEmpty($log);
$this->assertInstanceOf('Zend\Http\Response', $return);
$this->assertSame(200, $return->getStatusCode());
$this->assertEquals('Test!', $return->getBody());
}
}
111 changes: 111 additions & 0 deletions test/MiddlewareListenerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\Mvc;

use PHPUnit_Framework_TestCase as TestCase;
use Zend\Mvc\Application;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\Mvc\Service\ServiceListenerFactory;
use Zend\ServiceManager\ServiceManager;
use Zend\Stdlib\ArrayUtils;

class MiddlewareListenerTest extends TestCase
{
/**
* @var ServiceManager
*/
protected $serviceManager;

/**
* @var Application
*/
protected $application;

public function setUp()
{
$serviceConfig = ArrayUtils::merge(
$this->readAttribute(new ServiceListenerFactory, 'defaultServiceConfig'),
[
'allow_override' => true,
'invokables' => [
'Request' => 'Zend\Http\PhpEnvironment\Request',
'Response' => 'Zend\Http\PhpEnvironment\Response',
'ViewManager' => 'ZendTest\Mvc\TestAsset\MockViewManager',
'SendResponseListener' => 'ZendTest\Mvc\TestAsset\MockSendResponseListener',
'BootstrapListener' => 'ZendTest\Mvc\TestAsset\StubBootstrapListener',
],
'aliases' => [
'Router' => 'HttpRouter',
],
'services' => [
'Config' => [],
'ApplicationConfig' => [
'modules' => [],
'module_listener_options' => [
'config_cache_enabled' => false,
'cache_dir' => 'data/cache',
'module_paths' => [],
],
],
],
]
);
$this->serviceManager = new ServiceManager(new ServiceManagerConfig($serviceConfig));
$this->application = $this->serviceManager->get('Application');
}

public function setupPathMiddleware()
{
$request = $this->serviceManager->get('Request');
$request->setUri('http://example.local/path');

$router = $this->serviceManager->get('HttpRouter');
$route = Router\Http\Literal::factory([
'route' => '/path',
'defaults' => [
'middleware' => function($request, $response) {
$this->assertInstanceOf('Psr\Http\Message\ServerRequestInterface', $request);
$this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response);
$response->getBody()->write('Test!');
return $response;
}
],
]);
$router->addRoute('path', $route);
$this->application->bootstrap();
}


public function testMiddlewareDispatch()
{
$this->setupPathMiddleware();

$controllerLoader = $this->serviceManager->get('ControllerLoader');
$controllerLoader->addAbstractFactory('ZendTest\Mvc\Controller\TestAsset\ControllerLoaderAbstractFactory');

$log = [];
$this->application->getEventManager()->attach(MvcEvent::EVENT_DISPATCH_ERROR, function ($e) use (&$log) {
$log['error'] = $e->getError();
});

$this->application->run();

$event = $this->application->getMvcEvent();
$dispatchListener = $this->serviceManager->get('DispatchListener');
$return = $dispatchListener->onDispatch($event);

$this->assertEmpty($log);
$this->assertInstanceOf('Zend\Http\Response', $return);
$this->assertSame(200, $return->getStatusCode());
$this->assertEquals('Test!', $return->getBody());
}
}

0 comments on commit 47ac19a

Please sign in to comment.