Search code examples
phpzend-frameworkzend-framework2zend-framework-mvc

__invoke in listener class called after controller action in zend framework 2


I created an event listener and attached it to a specific controller in onBootstrap. The problem is that __invoke function is called after controller action called. Everything except this is working fine.

Factories

public function getServiceConfig() {
        return array(
            'factories' => array(
                'Api\Adapter\HeaderAuthentication' => 'Api\Factory\AuthenticationAdapterFactory',
                'Api\Listener\AuthenticationListener' => 'Api\Factory\AuthenticationListenerFactory',
            ),
        );
    }

Bootstrap

public function onBootstrap(MvcEvent $e) {
        $app = $e->getApplication();
        $sm = $app->getServiceManager();
        $em = $app->getEventManager();

        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($em);

        $listener = $sm->get('Api\Listener\AuthenticationListener');
        $em->getSharedManager()->attach('Api\Controller', 'dispatch', $listener);
    }

I have created a base controller Api\Controller of Zend\Mvc\Controller\AbstractRestfulController type and then I'll inherit this controller to all other controllers.

ListenerFactory

class AuthenticationListenerFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $sl)
    {
        $name    = 'Api\Adapter\HeaderAuthentication';
        $adapter = $sl->get($name);

        $listener = new AuthenticationListener($adapter);
        return $listener;
    }
}

AuthenticationListener

class AuthenticationListener {

    protected $adapter;

    public function __construct(HeaderAuthentication $adapter) {
        $this->adapter = $adapter;
        die('died in listener'); // this executes before controller execution.
    }

    public function __invoke(MvcEvent $event) { // this executes after controller action execution
        $result = $this->adapter->authenticate();
        die('died in listener'); 
        if(!$result->isValid()){
            $response = $event->getResponse();
            $response->setStatusCode(400);

            $responseMessages = '';
            foreach($result->getMessages() as $message) {
                $responseMessages .= $message . '. ';
            }
            $response->setContent($responseMessages);
            return $response;
        }

        $event->setParam('user', $result->getIdentity());

    }

}

Solution

  • Okay so what I found is interesting. In ZF2 postDispatch and preDispatch behavior can be achieved by prioritizing events. Simply add a priority to your event like this

    $em->getSharedManager()->attach('Api\Controller', 'dispatch', $listener, 100);
    

    By default events have a priority of 1. All events having priority greater than 1 called before action and act as preDispatch. While a number less than or equal to 1 makes it postDispatch.

    $em->getSharedManager()->attach('Api\Controller', 'dispatch', $listener); // postDispatch
    $em->getSharedManager()->attach('Api\Controller', 'dispatch', $listener, 100); // preDispatch
    $em->getSharedManager()->attach('Api\Controller', 'dispatch', $listener, -100); // postDispatch