A free and open-source book on ZF3 for beginners


14.3. Файл Module.php и обработка событий

Файл Module.php, расположенный внутри корневого каталога модуля, является своего рода точкой входа модуля. Класс Module, который определяется в этом файле, загружается компонентом Zend\ModuleManager при загрузке всех модулей приложения.

С этим классом можно сделать одну полезную вещь - зарегистрировать обработчики событий. Если помните из главы Как работает веб-сайт, у приложения есть жизненный цикл, представленный событиями. Вы можете написать функцию-обработчик событий (или класс) и зарегистрировать ее в точке входа модуля. Когда вызывается событие, будет вызван ваш метод-обработчик (или же класс), позволяющий вам сделать что-то полезное.

Зачем мне регистрировать обработчик событий?

Вот несколько практических примеров обработки событий, которые могут вам пригодиться:

  • Обрабатывать событие Route для того, чтобы использовать безопасное соединение HTTPS.
  • Когда ваш сайт находится в режиме обслуживания, обрабатывать событие Route для перехвата всех запросов и перенаправления пользователя.
  • Обрабатывать событие Dispatch, чтобы перенаправить пользователя на другую страницу. Например, если пользователь не аутентифицирован, перенаправить его на страницу входа.
  • Обрабатывать событие Dispatch для переопределения шаблона лэйаута по умолчанию для всех контроллеров, принадлежащих модулю.
  • Обрабатывать событие Dispatch Error для сообщения об исключении или ошибке и/или их логирования.
  • Обрабатывать событие Render для изменения содержимого получившейся веб-страницы..

Существует два способа зарегистрировать обработчик событий внутри класса Module: либо с помощью метода init() класса Module, либо с помощью его другого метода, onBootstrap(). Разница между ними в том, что init() вызывается раньше, чем onBootstrap(), до инициализации всех модулей, в то время как onBootstrap() вызывается тогда, когда все модули инициализированы. В последующих примерах мы используем метод init().

14.3.1. Пример 1. Переключение шаблона лэйаута

Чтобы показать, как подписаться на событие, создадим обработчик, который будет реагировать на событие Dispatch и устанавливать другой шаблон лэйаута для всех контроллеров модуля:

<?php
namespace YourCompanyModule;

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

class Module
{
    // Метод "init" вызывается при запуске приложения и  
    // позволяет зарегистрировать обработчик событий.
    public function init(ModuleManager $manager)
    {
        // Получаем менеджер событий.
        $eventManager = $manager->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager();
        // Регистрируем метод-обработчик. 
        $sharedEventManager->attach(__NAMESPACE__, 'dispatch', 
                                    [$this, 'onDispatch'], 100);
    }
    
    // Обработчик события.
    public function onDispatch(MvcEvent $event)
    {
        // Получаем контроллер, к которому был отправлен HTTP-запрос.
        $controller = $event->getTarget();
        // Получаем полностью определенное имя класса контроллера.
        $controllerClass = get_class($controller);
        // Получаем имя модуля контроллера.
        $moduleNamespace = substr($controllerClass, 0, strpos($controllerClass, '\\'));
           
        // Переключаем лэйаут только для контроллеров, принадлежащих нашему модулю.
        if ($moduleNamespace == __NAMESPACE__) {
            $viewModel = $event->getViewModel();
            $viewModel->setTemplate('layout/layout2');  
        }        
    }
    
    // ...
}

Во фрагменте кода выше мы добавляем в класс Module метод init(). В этом методе мы регистрируем обработчик события (строка 17) с помощью метода attach(), предоставляемого классом Zend\EventManager\SharedEventManager. Метод attach() принимает четыре аргумента: ID компонента, являющегося источником события, имя события ("dispatch"), обработчик события (метод текущего класса onDispatch()) и приоритет (100)).

Метод onDispatch() вызывается при событии Dispatch. В этом методе мы проверяем (строка 32), отправлен ли HTTP-запрос контроллеру, принадлежащему нашему модулю, и, если это так, переключаем шаблон лэйаута (строка 34).

14.3.2. Пример 2. Использование HTTPS

В этом примере мы покажем, как зарегистрировать обработчик событий, благодаря которому ваш сайт всегда будет использовать HTTPS-соединение для всех веб-страниц:

<?php
namespace YourCompanyModule;

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

class Module
{
    // Метод "init" вызывается при запуске приложения и  
    // позволяет зарегистрировать обработчик событий.
    public function init(ModuleManager $manager)
    {
        // Получаем менеджер событий.
        $eventManager = $manager->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager();
        // Регистрируем метод-обработчик. 
        $sharedEventManager->attach(__NAMESPACE__, 'route', 
                                    [$this, 'onRoute'], 100);
    }
    
    // Обработчик события.
    public function onRoute(MvcEvent $event)
    {
        if (php_sapi_name() == "cli") {
            // Не выполняем перенаправление на HTTPS в консольном режиме.
            return;
        }
        
        // Получаем URI запроса
        $uri = $event->getRequest()->getUri();
        $scheme = $uri->getScheme();
        // Если схема - не HTTPS, перенаправляем на тот же URI, но
        // со схемой HTTPS.
        if ($scheme != 'https'){
            $uri->setScheme('https');
            $response=$event->getResponse();
            $response->getHeaders()->addHeaderLine('Location', $uri);
            $response->setStatusCode(301);
            $response->sendHeaders();
            return $response;
        }
    }
    
    // ...
}

В этом коде мы регистрируем метод обработчика события, который вызывается при событии Route.

Внутри обработчика мы сначала проверяем, работает ли наш сайт в консольном режиме. В этом режиме мы не выполняем перенаправление на HTTPS.

После этого мы извлекаем из HTTP-запроса URI и проверяем, является ли текущая схема HTTPS или нет. Если нет, мы перенаправляем пользователя на тот же URL, но со схемой HTTPS.

14.3.3. Пример 3. Сообщения об исключениях на веб-сайте

С помощью данной процедуры можно легко отслеживать все происходящие на сайте исключения. Сообщать об исключениях или ошибках довольно важно, так как это позволяет сделать сайт более стабильным и безопасным, а также улучшить взаимодействие с пользователем.

<?php
namespace YourCompanyModule;

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

class Module
{
    // Метод "init" вызывается при запуске приложения и 
    // позволяет зарегистрировать обработчик событий.
    public function init(ModuleManager $manager)
    {
        // Получаем менеджер событий.
        $eventManager = $manager->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager();
        // Регистрируем метод-обработчик.
        $sharedEventManager->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH_ERROR,
                                    [$this, 'onError'], 100);
        $sharedEventManager->attach(__NAMESPACE__, MvcEvent::EVENT_RENDER_ERROR, 
                                    [$this, 'onError'], 100);
    }
    
    // Обработчик события.
    public function onError(MvcEvent $event)
    {
        // Получаем информацию об исключении.
        $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();
        
        // Подготавливаем сообщение эл. почты.
        $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);
        
        // Посылаем эл. сообщение об ошибке.
        mail($to, $subject, $body);
    }
    
    // ...
}

В данном фрагменте мы регистрируем обработчик событий, который будет вызываться при каждой ошибке отправки запроса (при несоответствии маршрута или исключении) и ошибке визуализации. Внутри метода обработчика onError() мы извлекаем информацию об исключении/ошибке и посылаем ее в виде электронного сообщения на адрес по вашему выбору.


Top