Quick links: Content - sections - sub sections
EN FR

Jelix implements a simple inter-module communication system. It is based on events, and is called jEvent.

Anywhere in the code, it's possible to emit an event, to let the modules respond to, and to use the response.

The jEvent component is follow a broadcast scheme. Some code emits a request (an event). Some listeners are awaiting for this event, and when it is emitted, they process it, and return something (a response).

So basically we have:

  • an objet "event" which embed the message and some parameters
  • some listeners, that will receive this object
  • a dispatcher, that distributes the event to all listeners waiting after the message
  • the object "emitter", which gives the event to the dispatcher, and receives the responses.

Since Jelix 1.8, there two ways to create an event:

  • by indicating simply a name and some parameters if needed
  • by instanciating an objet to give to the dispatcher

Emitting an event with its name

This is the historic way to emit an event.

You have to use the jEvent's static method notify().

jEvent::notify accepts two arguments. The first one is the name of the event you want to send (alphanumeric characters only), and the second one (optional) is an array of event parameters.

jEvent::notify returns a jEvent instance, containing all the related responses. Responses are some data returned by the listeners (don't confuse with the jResponse objects returned by controllers). The structure and value of responses depend solely on each event, the number of modules that responded and how the data is structured in the listeners.

For example:


   //first way, with parameters
   $eventParams = array('user' => $userObject);
   $theEvent = jEvent::notify('authCanLogin', $authCLEventParams);

   //second way, without parameters
   $theEvent = jEvent::notify('authCanLogin2');

To get access to the responses returned by the modules, use the getResponse method of the jEvent object that was returned after the emission.


  $reponses = $theEvent->getResponse();

The result is an array containing all data returned by listeners.

You have other methods to query data from responses:

  • inResponse($responseKey, $value): says if a key with the given value exists in returned data.
  • getResponseByKey($responseKey): return list of data corresponding to the given key
  • allResponsesByKeyAreTrue($responseKey): says if all data of the given key are true.
  • allResponsesByKeyAreFalse($responseKey): says if all data of the given key are false.

Emitting an event with an event object

Since Jelix 1.8, it is possible to emit events with an object that you instancy yourself.

The advantage is that you can have specifique properties and methods on this object, that listeners can call.

The event objet must implement the interface Jelix\Event\EventInterface which provide a method getName(). This method should return the event name.

The event object can also implements the interface Psr\EventDispatcher\StoppableEventInterface, allowing to a listener to stop the propagation of the event to other listeners.

Once the event object is instanciated, you give it to the dispatcher. The dispatcher is given by Jelix\Core\Services::eventDispatcher(). It implements the interface Psr\EventDispatcher\EventDispatcherInterface, follwing the PSR-14 specificication.

Example:



class \MyProject\MyEvent implements \Jelix\Event\EventInterface {
    protected $isOk = false;

    public function getName() {
        return 'thisIsMyEvent';
    }

    public function itsOk()
    {
        $this->isOk = true;
    }

    public function isItOk()
    {
        return $this->isOk;
    }
}

// create the event
$event = new \MyProject\MyEvent();

// send the event to listeners, which can call for example the method @@itsOk()@@ on the event.
Jelix\Core\Services::eventDispatcher()->dispatch($event);

// then we can check the listeners response
if ($event->isItOk()) {
    //...
}

Responding to an event

Since there is no default listener in the modules, each module that wants to respond to events need to create a listener and declare it in the events.xml file of the same module. There is no limit in the number of listeners.

The creation of the listener has two steps: the creation of the class and the declaration of the listener.

Creating a listener

A listener is a class stored into the classes folder with a specific name.

Since Jelix 1.8.1, it could be any classes that is autoloadable, without specific name. It is more flexible.

To create a listener stored into the classes folder, first choose a "logical" name. We will use 'auth' as an example. Then create a listener class with the name that is a concatenation of the "logical" name with the name 'Listener', so in our case, authListener. Put that class in a file called {logicalname}.listener.php, so auth.listener.php for us, in the classes folder of your module.

A listener class must inherit from \Jelix\Event\EventListener (or from the deprecated class jEventListener) and it needs to implement one method for each event it is supposed to respond to. Those methods should follow this convention : on following by the event name. They need to accept as single argument a jEvent object.

Example:


class authListener extends jEventListener {
// or
// class \MyModule\Listeners\Auth extends \jEventListener {

   function onAuthCanLogin ($event) {
        //retrive user parameter
        $user = $event->getParam('user');

        //the actual processing
        $ok = true;
        if (isset($user->active)) {
            $ok = ($user->active == '1');
        }
        $ok = $ok && ($user->password != '');

        //the response
        $event->add(array('canlogin'=>$ok));
   }
}

This example demonstrates a listener responding to the AuthCanLogin event. First, the onAuthCanLogin method retrieves an 'user' parameter from the event object. Next, it does it's internal processing, and finally, it adds some data to the response by calling $event->add.

For events having a name that cannot be a method name, like name including characters like a dot, you can indicate the method to execute into a property eventMapping. Keys are event names, values are method names.


class authListener extends jEventListener {

    protected $eventMapping = array(
      'foo.bar' => 'execFooBar'
    );

    function execFooBar (\jEvent $event) {
        // here process the event.
    }
}

Note: the convention of the name of methods and the calling of methods corresponding to events can be changed by redefining the method performEvent of your listener.

Declaring a listener

The final step to setup a listener is to declare it. This is done through the events.xml file located in your module root folder. For example:


<events xmlns="http://jelix.org/ns/events/1.0">
   <listener name="auth">
       <event name="AuthCanLogin" />
   </listener>
   <-- or -->
   <listener name="\MyModule\Listeners\Auth">
       <event name="AuthCanLogin" />
       <event name="thisIsMyEvent" />
   </listener>
</events>

The name attribute of the listener element must indicate the "logical" name of the listener, if it is stored into classes, or the full qualified name of the class, if it is an autoloadable class.

A listener can listen to more than one event. Since it would take too much resources to load all the listeners one by one and check what events do they respond to, you also need to list all the events that you want a listener to listen to. Remember that two listener can also respond to the same event, and that there isn't a conventional structure for the responses.

Disabling listeners

When we use a vendor module, for any reason, we could want to deactivate one of its listener about a specific events.

It is possible by indicating them into the [disabledListeners] section in the main configuration. Keys are event name, values are selector of listeners or the full name of the class if it is autoloadable.


[disabledListeners]
authLogin="aModule~theListener"

In this example, the listener theListener of the module "aModule" won't be called when the event authLogin will be emited.

If you deactivate several listeners for a same event, just use [] to indicate an array:


[disabledListeners]
authLogin[]="aModule~theListener"
authLogin[]="\MyModule\Listeners\Auth"