A free and open-source book on ZF3 for beginners


14.3. Module.php File & Event Listening

The Module.php file located inside of module's source directory is some kind of module entry point. The Module class defined in this file is loaded by Zend\ModuleManager component when it loads all application modules.

One useful thing you can do with this class is registering to events. If you remember from the Website Operation chapter, the application has several life stages represented by events. You may write an event listener function (or class) and register it in your module entry point. When an event is triggered, your listener method (or class) will be called allowing you to do something useful.

Why would I want to register an event listener?

Here are several practical applications of event listening that you may find useful:

  • Listen to Route event to force the use of HTTPS secure connection.
  • When your website is in maintenance mode, listen to Route event to catch all requests and redirect user to the single page.
  • Listen to Dispatch event to redirect a user to a different page. For example, if user is not authenticated, redirect him to the login page.
  • Listen to Dispatch event to override the default layout template for all controllers belonging to the module.
  • Listen to Dispatch Error event to log and/or report any exception or error happening in your website.
  • Listen to Render event to modify the content of the resulting web page.

There are two ways to register an event listener within the Module class: either with the help of Module's init() method or with the help of its onBootstrap() method. The difference between init() method and onBootstrap() method is that the init() method is called earlier than onBootstrap(), before all other modules are initialized; while onBootstrap() is called once all modules are initialized. In the following examples, we use init() method.

14.3.1. Example 1. Switching Layout Template

To show you how to subscribe to an event, let's create an event listener that will react on Dispatch event and set a different layout template for all controllers of the module:

<?php
namespace YourCompanyModule;

use Zend\ModuleManager\ModuleManager;
use Zend\Mvc\MvcEvent;

class Module
{
    // The "init" method is called on application start-up and 
    // allows to register an event listener.
    public function init(ModuleManager $manager)
    {
        // Get event manager.
        $eventManager = $manager->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager();
        // Register the event listener method. 
        $sharedEventManager->attach(__NAMESPACE__, 'dispatch', 
                                    [$this, 'onDispatch'], 100);
    }
    
    // Event listener method.
    public function onDispatch(MvcEvent $event)
    {
        // Get controller to which the HTTP request was dispatched.
        $controller = $event->getTarget();
        // Get fully qualified class name of the controller.
        $controllerClass = get_class($controller);
        // Get module name of the controller.
        $moduleNamespace = substr($controllerClass, 0, strpos($controllerClass, '\\'));
           
        // Switch layout only for controllers belonging to our module.
        if ($moduleNamespace == __NAMESPACE__) {
            $viewModel = $event->getViewModel();
            $viewModel->setTemplate('layout/layout2');  
        }        
    }
    
    // ...
}

In the code above, we add the init() method to the Module class. In that method, we register an event listener (line 17) with the help of attach() method provided by the Zend\EventManager\SharedEventManager class. The attach() method takes four arguments: the ID of the emitting component, the event name ("dispatch"), the event listener method (the onDispatch() method of the current class), and the priority (100)).

The onDispatch() method is called on the Dispatch event. In this method, we check (line 32) if the HTTP request is dispatched to the controller belonging to our module, and if so, switch the layout template (line 34).

14.3.2. Example 2. Forcing the Use of HTTPS

In this example, we will show how to register an event listener that makes the website to always use HTTPS connection with all of your web pages:

<?php
namespace YourCompanyModule;

use Zend\ModuleManager\ModuleManager;
use Zend\Mvc\MvcEvent;

class Module
{
    // The "init" method is called on application start-up and 
    // allows to register an event listener.
    public function init(ModuleManager $manager)
    {
        // Get event manager.
        $eventManager = $manager->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager();
        // Register the event listener method. 
        $sharedEventManager->attach(__NAMESPACE__, 'route', 
                                    [$this, 'onRoute'], 100);
    }
    
    // Event listener method.
    public function onRoute(MvcEvent $event)
    {
        if (php_sapi_name() == "cli") {
            // Do not execute HTTPS redirect in console mode.
            return;
        }
        
        // Get request URI
        $uri = $event->getRequest()->getUri();
        $scheme = $uri->getScheme();
        // If scheme is not HTTPS, redirect to the same URI, but with
        // HTTPS scheme.
        if ($scheme != 'https'){
            $uri->setScheme('https');
            $response=$event->getResponse();
            $response->getHeaders()->addHeaderLine('Location', $uri);
            $response->setStatusCode(301);
            $response->sendHeaders();
            return $response;
        }
    }
    
    // ...
}

In the code above, we register an event listener method that is called on Route event.

Inside the listener, we first check if our website is working in console mode. We do not redirect HTTPS if in console mode.

Then, we extract the URI from the HTTP request and check if the current scheme is HTTPS or not. If the scheme is not HTTPS, we redirect the user to the same URL, but with HTTPS scheme.

14.3.3. Example 3. Reporting All Exceptions in Your Website

With this technique, you can easily track all exceptions happening in your website. Reporting exceptions and errors is an important thing, because it allows to make your website more stable, secure and improve user experience.

<?php
namespace YourCompanyModule;

use Zend\ModuleManager\ModuleManager;
use Zend\Mvc\MvcEvent;

class Module
{
    // The "init" method is called on application start-up and 
    // allows to register an event listener.
    public function init(ModuleManager $manager)
    {
        // Get event manager.
        $eventManager = $manager->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager();
        // Register the event listener method. 
        $sharedEventManager->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH_ERROR,
                                    [$this, 'onError'], 100);
        $sharedEventManager->attach(__NAMESPACE__, MvcEvent::EVENT_RENDER_ERROR, 
                                    [$this, 'onError'], 100);
    }
    
    // Event listener method.
    public function onError(MvcEvent $event)
    {
        // Get the exception information.
        $exception = $event->getParam('exception');
        if ($exception!=null) {
            $exceptionName = $exception->getMessage();
            $file = $exception->getFile();
            $line = $exception->getLine();
            $stackTrace = $exception->getTraceAsString();
        }
        $errorMessage = $event->getError();
        $controllerName = $event->getController();
        
        // Prepare email message.
        $to = 'admin@yourdomain.com';
        $subject = 'Your Website Exception';
        
        $body = '';
        if(isset($_SERVER['REQUEST_URI'])) {
            $body .= "Request URI: " . $_SERVER['REQUEST_URI'] . "\n\n";
        }
        $body .= "Controller: $controllerName\n";
        $body .= "Error message: $errorMessage\n";
        if ($exception!=null) {
            $body .= "Exception: $exceptionName\n";
            $body .= "File: $file\n";
            $body .= "Line: $line\n";
            $body .= "Stack trace:\n\n" . $stackTrace;
        }
        
        $body = str_replace("\n", "<br>", $body);
        
        // Send an email about the error.
        mail($to, $subject, $body);
    }
    
    // ...
}

In the code above, we register an event listener that will be called on every Dispatch Error (route mismatch or an exception) and Renderer Error. Inside the onError() listener method, we extract some information about the exception/error and send it as an email message to the address of your choice.


Top