Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Commit

Permalink
Merge pull request #7240 from stefanotorresi/feature/http-method-list…
Browse files Browse the repository at this point in the history
…ener

Feature: Zend\Mvc\HttpMethodListener
  • Loading branch information
weierophinney committed Feb 25, 2015
2 parents ed1eeea + ec5f1a6 commit 710eb65
Show file tree
Hide file tree
Showing 7 changed files with 389 additions and 59 deletions.
1 change: 1 addition & 0 deletions library/Zend/Mvc/Application.php
Expand Up @@ -66,6 +66,7 @@ class Application implements
protected $defaultListeners = array(
'RouteListener',
'DispatchListener',
'HttpMethodListener',
'ViewManager',
'SendResponseListener',
);
Expand Down
127 changes: 127 additions & 0 deletions library/Zend/Mvc/HttpMethodListener.php
@@ -0,0 +1,127 @@
<?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 Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;
use Zend\Http\Request as HttpRequest;
use Zend\Http\Response as HttpResponse;

class HttpMethodListener extends AbstractListenerAggregate
{
/**
* @var array
*/
protected $allowedMethods = array(
HttpRequest::METHOD_CONNECT,
HttpRequest::METHOD_DELETE,
HttpRequest::METHOD_GET,
HttpRequest::METHOD_HEAD,
HttpRequest::METHOD_OPTIONS,
HttpRequest::METHOD_PATCH,
HttpRequest::METHOD_POST,
HttpRequest::METHOD_PUT,
HttpRequest::METHOD_PROPFIND,
HttpRequest::METHOD_TRACE,
);

/**
* @var bool
*/
protected $enabled = true;

/**
* @param bool $enabled
* @param array $allowedMethods
*/
public function __construct($enabled = true, $allowedMethods = array())
{
$this->setEnabled($enabled);

if (! empty($allowedMethods)) {
$this->setAllowedMethods($allowedMethods);
}
}

/**
* {@inheritdoc}
*/
public function attach(EventManagerInterface $events)
{
if (! $this->isEnabled()) {
return;
}

$this->listeners[] = $events->attach(
MvcEvent::EVENT_ROUTE,
array($this, 'onRoute'),
10000
);
}

/**
* @param MvcEvent $e
* @return void|HttpResponse
*/
public function onRoute(MvcEvent $e)
{
$request = $e->getRequest();
$response = $e->getResponse();

if (! $request instanceof HttpRequest || ! $response instanceof HttpResponse) {
return;
}

$method = $request->getMethod();

if (in_array($method, $this->getAllowedMethods())) {
return;
}

$response->setStatusCode(405);

return $response;
}

/**
* @return array
*/
public function getAllowedMethods()
{
return $this->allowedMethods;
}

/**
* @param array $allowedMethods
*/
public function setAllowedMethods(array $allowedMethods)
{
foreach ($allowedMethods as &$value) {
$value = strtoupper($value);
}
$this->allowedMethods = $allowedMethods;
}

/**
* @return bool
*/
public function isEnabled()
{
return $this->enabled;
}

/**
* @param bool $enabled
*/
public function setEnabled($enabled)
{
$this->enabled = (bool) $enabled;
}
}
40 changes: 40 additions & 0 deletions library/Zend/Mvc/Service/HttpMethodListenerFactory.php
@@ -0,0 +1,40 @@
<?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\Service;

use Zend\Mvc\HttpMethodListener;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class HttpMethodListenerFactory implements FactoryInterface
{
/**
* {@inheritdoc}
* @return HttpMethodListener
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('config');

if (! isset($config['http_methods_listener'])) {
return new HttpMethodListener();
}

$listenerConfig = $config['http_methods_listener'];
$enabled = array_key_exists('enabled', $listenerConfig)
? $listenerConfig['enabled']
: true;
$allowedMethods = (isset($listenerConfig['allowed_methods']) && is_array($listenerConfig['allowed_methods']))
? $listenerConfig['allowed_methods']
: null;

return new HttpMethodListener($enabled, $allowedMethods);
}
}
1 change: 1 addition & 0 deletions library/Zend/Mvc/Service/ServiceListenerFactory.php
Expand Up @@ -57,6 +57,7 @@ class ServiceListenerFactory implements FactoryInterface
'FormAnnotationBuilder' => 'Zend\Mvc\Service\FormAnnotationBuilderFactory',
'FormElementManager' => 'Zend\Mvc\Service\FormElementManagerFactory',
'HttpRouter' => 'Zend\Mvc\Service\RouterFactory',
'HttpMethodListener' => 'Zend\Mvc\Service\HttpMethodListenerFactory',
'HttpViewManager' => 'Zend\Mvc\Service\HttpViewManagerFactory',
'HydratorManager' => 'Zend\Mvc\Service\HydratorManagerFactory',
'InjectTemplateListener' => 'Zend\Mvc\Service\InjectTemplateListenerFactory',
Expand Down
112 changes: 53 additions & 59 deletions tests/ZendTest/Mvc/ApplicationTest.php
Expand Up @@ -137,52 +137,68 @@ public function testEventsAreEmptyAtFirst()
$this->assertAttributeEquals(array(), 'identifiers', $sharedEvents);
}

public function testBootstrapRegistersRouteListener()
/**
* @param string $listenerServiceName
* @param string $event
* @param string $method
*
* @dataProvider bootstrapRegistersListenersProvider
*/
public function testBootstrapRegistersListeners($listenerServiceName, $event, $method, $isCustom = false)
{
$routeListener = $this->serviceManager->get('RouteListener');
$this->application->bootstrap();
$listenerService = $this->serviceManager->get($listenerServiceName);
$this->application->bootstrap($isCustom ? (array) $listenerServiceName : array());
$events = $this->application->getEventManager();
$listeners = $events->getListeners(MvcEvent::EVENT_ROUTE);
$this->assertEquals(1, count($listeners));
$listener = $listeners->top();
$callback = $listener->getCallback();
$this->assertSame(array($routeListener, 'onRoute'), $callback);
}
$listeners = $events->getListeners($event);

public function testBootstrapRegistersDispatchListener()
{
$dispatchListener = $this->serviceManager->get('DispatchListener');
$this->application->bootstrap();
$events = $this->application->getEventManager();
$listeners = $events->getListeners(MvcEvent::EVENT_DISPATCH);
$this->assertEquals(1, count($listeners));
$listener = $listeners->top();
$callback = $listener->getCallback();
$this->assertSame(array($dispatchListener, 'onDispatch'), $callback);
$foundListener = false;
foreach ($listeners as $listener) {
$callback = $listener->getCallback();
$foundListener = $callback === array($listenerService, $method);
if ($foundListener) {
break;
}
}
$this->assertTrue($foundListener);
}

public function testBootstrapRegistersSendResponseListener()
public function bootstrapRegistersListenersProvider()
{
$sendResponseListener = $this->serviceManager->get('SendResponseListener');
$this->application->bootstrap();
$events = $this->application->getEventManager();
$listeners = $events->getListeners(MvcEvent::EVENT_FINISH);
$this->assertEquals(1, count($listeners));
$listener = $listeners->top();
$callback = $listener->getCallback();
$this->assertSame(array($sendResponseListener, 'sendResponse'), $callback);
return array(
array('RouteListener', MvcEvent::EVENT_ROUTE, 'onRoute'),
array('DispatchListener', MvcEvent::EVENT_DISPATCH, 'onDispatch'),
array('SendResponseListener', MvcEvent::EVENT_FINISH, 'sendResponse'),
array('ViewManager', MvcEvent::EVENT_BOOTSTRAP, 'onBootstrap'),
array('HttpMethodListener', MvcEvent::EVENT_ROUTE, 'onRoute'),
array('BootstrapListener', MvcEvent::EVENT_BOOTSTRAP, 'onBootstrap', true),
);
}

public function testBootstrapRegistersViewManagerAsBootstrapListener()
public function testBootstrapAlwaysRegistersDefaultListeners()
{
$viewManager = $this->serviceManager->get('ViewManager');
$this->application->bootstrap();
$events = $this->application->getEventManager();
$listeners = $events->getListeners(MvcEvent::EVENT_BOOTSTRAP);
$this->assertEquals(1, count($listeners));
$listener = $listeners->top();
$callback = $listener->getCallback();
$this->assertSame(array($viewManager, 'onBootstrap'), $callback);
$refl = new \ReflectionProperty($this->application, 'defaultListeners');
$refl->setAccessible(true);
$defaultListenersNames = $refl->getValue($this->application);
$defaultListeners = array();
foreach ($defaultListenersNames as $defaultListenerName) {
$defaultListeners[] = $this->serviceManager->get($defaultListenerName);
}

$this->application->bootstrap(array('BootstrapListener'));
$eventManager = $this->application->getEventManager();

$registeredListeners = array();
foreach ($eventManager->getEvents() as $event) {
$listeners = $eventManager->getListeners($event);
foreach ($listeners as $listener) {
$callback = $listener->getCallBack();
$registeredListeners[] = $callback[0];
}
}

foreach ($defaultListeners as $defaultListener) {
$this->assertContains($defaultListener, $registeredListeners);
}
}

public function testBootstrapRegistersConfiguredMvcEvent()
Expand Down Expand Up @@ -630,28 +646,6 @@ public function testCompleteRequestShouldReturnApplicationInstance()
$this->assertSame($this->application, $result);
}


public function testCustomListener()
{
$this->application->bootstrap(array('BootstrapListener'));

// must contain custom bootstrap listeners
$bootstrapListener = $this->serviceManager->get('BootstrapListener');
$listeners = $this->application->getEventManager()->getListeners(MvcEvent::EVENT_BOOTSTRAP);
$bootstrapListeners = $bootstrapListener->getListeners();
$this->assertTrue($listeners->contains($bootstrapListeners[0]));

// must contain default listeners
$listeners = $this->application->getEventManager()->getListeners(MvcEvent::EVENT_DISPATCH);
$this->assertEquals(1, count($listeners));

$listeners = $this->application->getEventManager()->getListeners(MvcEvent::EVENT_ROUTE);
$this->assertEquals(1, count($listeners));

$listeners = $this->application->getEventManager()->getListeners(MvcEvent::EVENT_FINISH);
$this->assertEquals(1, count($listeners));
}

public function testFailedRoutingShouldBePreventable()
{
$this->application->bootstrap();
Expand Down

0 comments on commit 710eb65

Please sign in to comment.