Tutorial: Events and Listeners

Introduction

phpBB 3.1 introduced a system of events throughout the codebase and template files that allow extensions to use listeners to add features, inject code, and modify existing functionality and behaviour.

This tutorial explains how to use events and listeners:

Template Events & Listeners

Template events allow extensions to inject code into the template files. Events can be found throughout the template structure of phpBB’s styles, providing multiple useful injection points. A typical template event looks like:

<!-- EVENT event_name_and_position -->

See also

View the full list of Template events in our Wiki.

Listening for an event

To subscribe to a template event, we must create a listener, a template file with exactly the same name as the identifier of the template event we want to use, and place it in the event/ subdirectory of a style’s template folder.

For example, we will use the overall_header_navigation_prepend event to add a new link before the existing links in the board’s navigation bar. The listener for this event will be:

acme/demo/styles/prosilver/template/event/overall_header_navigation_prepend.html

Tip

If you want a template listener to be used by all styles, it should be placed in the all/ style directory, e.g. vendor/extname/styles/all/template/event/. This helps prevent code duplication and eases style file management.

The prosilver and subsilver2 directories should be used for template files that must be custom tailored to each style. Similarly, if a 3rd-party style requires additional customisation to maintain compatibility, a folder for that style should be included with those template files.

To add ACP template listeners, place those files in vendor/extname/adm/style/event/.

Inside the template listener, we will create a nav-bar link. This consists of a simple list element, with a link and a description:

<li class="small-icon icon-faq no-bulletin">
    <a href="{U_DEMO_PAGE}">
        {L_DEMO_PAGE}
    </a>
</li>

Once the template listener has been created, you should be able to see the new link in the board’s nav-bar, with the icon of the FAQ link and the text DEMO_PAGE. We will fix the link text in the next section.

Tip

If the link does not appear, you may need to purge the board cache from the main page of the ACP. You can also enable DEBUG_MODE in your config.php file, which will force the template engine to always look for template listeners when a page is being rendered.

It’s important to understand that when phpBB compiles the templates, there is no current system for determining the priority in which template listeners from different extensions subscribed to the same event are compiled. In rare cases some extensions could cause a conflict, in which case the recommendation is for the extension authors to work together on a solution for their conflicting template listeners.

PHP Core Events & Listeners

Events allow extensions to execute code in many locations within core phpBB code, without modifying any of the code. That way extensions can easily add features, remove functionality or modify behaviour, while maintaining compatibility and simple update procedures.

Terminology

Events
Events are dispatched at various places in the core phpBB code. Listeners in extensions subscribe to these events. They are able to execute code whenever the respective event has occurred. An alternative name for events is “hook locations”.
Listeners
Listeners are triggered by events. They are methods that can process incoming data and manipulate variables in the scope of the event. So they can change phpBB’s behaviour, add new functionality or if used in the context of templates, modify the output. Numerous listeners form part of a subscriber. An alternative name for listeners is “hooks”.

See also

View the full list of supported PHP events in our Wiki.

The event listener

In the previous section we created a template listener that adds a link for the Acme Demo extension to phpBB’s nav-bar. We will now use PHP events to load a language file that contains the DEMO_PAGE language key so that our nav-bar link will display with the correct text.

To do so, we need to create a PHP event listener class (a.k.a. subscriber class). This class includes a set of listener methods, each of which can subscribe to PHP events in phpBB’s codebase. The listener class must be created in the event/ subdirectory of the extension directory or it will not work. It must also conform to the following requirements:

  • Follow extension class name-spacing conventions: vendor\extname\event\subscribername.php.
  • Implement Symfony’s Symfony\Component\EventDispatcher\EventSubscriberInterface interface.
  • Use the static method getSubscribedEvents() to subscribe methods in the listener to specific events, the keys of which contain event names and the values of which contain listener function names.

In the Acme Demo extension, we want to load our language file everywhere. Therefore we will subscribe a listener function to phpBB’s core.user_setup event:

<?php

namespace acme\demo\event;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class main_listener implements EventSubscriberInterface
{
    /**
     * Assign functions defined in this class to event listeners in the core
     *
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return array(
            'core.user_setup' => 'load_language_on_setup',
        );
    }

    /**
     * Load the Acme Demo language file
     *     acme/demo/language/en/demo.php
     *
     * @param \phpbb\event\data $event The event object
     */
    public function load_language_on_setup($event)
    {
        $lang_set_ext = $event['lang_set_ext'];
        $lang_set_ext[] = array(
            'ext_name' => 'acme/demo',
            'lang_set' => 'demo',
        );
        $event['lang_set_ext'] = $lang_set_ext;
    }
}

So what is the main_listener.php class above actually doing?

The getSubscribedEvents() method is subscribing our listener function load_language_on_setup() to the event named core.user_setup. This means that when this event occurs, our function will execute.

Tip

You can assign multiple listener functions to a single event using an array:

'core.user_setup' => array(array('foo_method'), array('bar_method'))

The load_language_on_setup() listener method simply adds our language file to phpBB’s language data array. Generally speaking, a listener is simply a public function in the subscriber class, referred to in the array returned by getSubscribedEvents(). It takes one argument, $event. This parameter allows you to access and modify the variables that are given to the event from the core code. In this case we are modifying the lang_set_ext variable by adding Acme Demo’s language file to it.

Note

Note how the lang_set_ext event variable is first copied by assigning it to a local variable, then modified, and then copied back. This shortcut does not work: $event['foo']['bar'] = $baz; This is because the event variables are overloaded, which is a limitation in PHP.

Registering the listener

To have phpBB autoload and execute our event listener class, we need to create a service definition for it. This is done by creating a config/services.yml file in the extension:

services:
    acme.demo.listener:
        class: acme\demo\event\main_listener
        tags:
            - { name: event.listener }

Warning

YAML files are indentation sensitive. They require an indentation size of 4 spaces per indent, do not use tabs.

The first line tells phpBB that a list of services is being registered. On the next line we specify the name of the service, which is for our event listener in this case.

Important

Service names must be prefixed with your vendor and extension name.

The class attribute must contain the name-space and class name of the service being registered. The name-space depends on the file’s location, within the ext/ directory. Thus, the file ext/acme/demo/event/main_listener.php has the namespace acme\demo\event and class name main_listener. The full name of the class is therefore acme\demo\event\main_listener which is what we need to specify here.

The tags attribute tells phpBB that the service is an event listener.

Once the services YAML file has been created (or modified), phpBB’s cache needs to be purged. After purging the cache in the ACP, the description of the link in the navigation bar should now display Demo instead of DEMO_PAGE.

Note

phpBB’s core PHP and template files have been prepared with dozens of event locations. However, if there are no events where your extension may need one, the phpBB development team welcomes event requests at the area51.com Event Requests forum.

Prioritising event listeners (optional)

Sometimes different extensions can run into problems when competing for use of the same PHP core events. In trying to resolve these issues, the extension developer may want to prioritise their extension over others, so that their extension will be triggered before other extensions.

In such cases, the getSubscribedEvents() method provides an argument for setting a priority for event listener methods. For example:

static public function getSubscribedEvents()
{
    return array(
        'core.user_setup' => array('foo_method', $priority)
    );
}

In this example, $priority is an integer, the value of which defaults to 0. Setting this integer to higher values equals more importance and therefore that listener will be triggered earlier than others subscribed to this event.

We have now used events and listeners to modify phpBB and insert a nice link into the nav-bar. However, the link still does not work yet. Continue on to the next section to learn how to use controllers and routing to make our nav-bar link open up a custom user facing page.