Under the Hood of Yii’s Component Architecture, Part 2

Share this article

Welcome once again to this tour of the Yii Framework’s CComponent class. This three-part series demonstrates how Yii uses a component-based architecture and how the class implements properties, configuration, events, and behaviors. In Part 1 you saw how Yii uses PHP’s magic methods to implement component properties. In this article, which is Part 2 of the series, I’ll show you how you can do event-based programming in PHP. I’ll also show you how the component class makes it possible.

Events

An application event is something that occurs which might be of interest to other bits of code. A standard event in most GUI applications would be a “click” event, but the sky’s the limit and what events you define is really up to you. For example, when a user registers you’ll often want to send her an email welcoming her to your application. You could define “Registration complete” as an event. Rather than hard-coding email logic or even setting different user properties in your code, the module could simply raise an event and forget about any specific implementation details. The details can be provided by application-specific modules allowing you to keep individual requirements separate from your reusable code. Events allow you to attach a potentially unlimited amount of functionality without changing your core modules and components. There are generally three steps to implementing an event:
  1. Define the event
  2. Attach custom functionality to the event
  3. Trigger the event when it happens

Defining Events First you need to define the event allowing Yii to attach functions to it. Continuing with the user registration example, you would create a new event arbitrarily called onUserRegistered that is raised after a user has been successfully registered. This would be defined within my your user module’s code.
<?php
public function onUserRegistered($event) {
    $this->raiseEvent("onUserRegistered", $event);
}
Events in Yii are defined by simply adding a function with “on” prefixed to the method name. In order to attach a function to an object you need to be able to access the object from other areas of the application. The event is added to the user component so that it can be accessed throughout the application through Yii::app()->user.

Triggering Events

The controller class of the user module would be responsible for triggering the event when it actually occurs.
<?php
public function actionUserRegister() {
    // code to register a user and add to the database
    // ...
    // user has been successfully registered so lets raise an event to tell the world
    $e = new CEvent($this, array("user" => $user));
    Yii::app()->user->onUserRegistered($e);
}
Once a user has been successfully registered and added to your database, you’ll want to tell the world. This code raises the event. Events do not know the specific details of the function they are calling so all handling functions must have the same interface. In Yii, all handling functions expect one parameter which is a CEvent object. The CEvent object expects two parameters: a reference to the object that raised the event, and an optional array of parameters that can be used to store specific information relating to the particular event (these parameters can be accessed later by your handling functions). In this example I want all handling functions to be able to access the user object that was just registered. To do this I pass an array of array("user" => $user) where $user is an object representing our recently registered user. I then trigger the event and evoke Yii’s raiseEvent() function by calling the function onUserRegistered() and pass in the event object. That is all the code that needs to be added to the user module. Now any additional code anywhere in your application can attach additional functionality to be executed when a new user registers.

Attaching Events

Continuing with the example, let’s see how attach an event’s callback.
<?php
public function init() {
    Yii::app()->user->onUserRegistered = array($this, "sendMyEmail");

}

public function sendMyEmail($event) {
    $user = $event->params["user"];
    mail($user->email, "Welcome to this amazing event app", "Hello...");
}
I’ve attaching the sendMyEmail() method to the onUserRegistered event so now when a new user registers the sendMyEmail() function will be called. Alternatively, in PHP 5.3 or greater you can specify an anonymous function. Yii supports assigning any value to an event that would also return true if passed into the PHP is_callable() function.

Yii’s Magic

Now let’s look at how Yii implements events; managing events is all handled by cleverness in the CComponent class. In order to implement events the component class has to implement the three main concepts:
  • Defining events – Yii need to store or be able to lookup defined events.
  • Triggering events – When an event occurs, Yii need to call all of our PHP functions attached to the event.
  • Attaching events – Yii need a mechanism to store a list of valid PHP callbacks against an event.
The mechanism for defining events in Yii is to simply create a function with the “on” prefix as you saw earlier. The mechanism for triggering an event is to add $this->raiseEvent("onMethodName"); within the defined event and simply call the method when the event occurs in your code. This leaves us with two implementation details left:
  • Attaching functions to the event.
  • Calling all functions attached to the event when it is triggered.
To attach the event you use the code onMyEventName = callback, which means the implementation for attaching functions to events must be handled in the components magic __set method.
<?php
public function __set($name, $value){
    if (strncasecmp($name, "on", 2) === 0 && method_exists($this, $name)) {
        $name = strtolower($name);
        if (!isset($this->_e[$name])) {
            $this->_e[$name] = new CList();
        }
        return $this->_e[$name]->add($value);
    }
}
The implementation first checks if the value of $name
starts with the text “on” and that a method also exists with the same name as the value. If it does, Yii assumes $value is a representation of a callback which it needs to attach to the event defined by $name. Yii has a private member variable $_e which holds an array of callbacks keyed by event names, and it simply adds the callback to the list for the particular event key.
$_e => array(
    'onUserRegistered' => array(
        0 => array(object, 'sendMyEmail')
    ),
    'onSomeOtherEvent'=>array(
        0 => function(){}
        1 => ...
    )
)
All that’s left now is to call the attached functions when the event is triggered. From looking at the array of stored events, this could be as easy as looking up the event in the $_e variable, and iterating through each callback in the array and running it.
public function raiseEvent($name, $event) {
    foreach ($this->_e[$name] as $handler) {
        if (is_array($handler)) {
            // an array: 0 - object, 1 - method name
            list($object, $method) = $handler;
            $object->$method($event);
        }
        else {
            // PHP 5.3+ anonymous function
            call_user_func($handler, $event);
        }
    }
}
I’ve refactored the raiseEvent() method so it’s easier to read for our demo, but the Yii implementation has more error checking and handles additional more callback types, such as static methods.

Summary

Events are a great concept to implement for robust, flexible code. In this article you’ve learned how to create reusable components that can have their functionality extended through the use of events, and have seen seen how the Yii framework’s CComponent class implements and manages events in PHP. Of course, the usage and implementation details may be specific to Yii but the concept itself is universal. This wraps up our second article in this three part series. The final article walks through creating and implementing behaviors, another great way to extend a component’s functionality while keeping it easy to reuse. Image via Filipchuk Oleg Vasiliovich / Shutterstock

Frequently Asked Questions about Yii Under the Hood

What is the purpose of Yii events?

Yii events are a crucial part of the Yii framework. They provide a way for you to customize the behavior of your application without having to modify the core code. When an event is triggered, it can execute a piece of code, known as an event handler. This allows you to add, modify, or even replace the default behavior of the application. Events are a powerful tool for making your application more flexible and maintainable.

How do I use events in Yii2?

To use events in Yii2, you first need to define an event in your class. This is done by declaring a constant with the name of the event. Once the event is defined, you can attach an event handler to it using the on method. The event handler is a callback function that will be executed when the event is triggered. You can trigger an event using the trigger method.

Can I use multiple event handlers for a single event?

Yes, you can attach multiple event handlers to a single event. When the event is triggered, all attached event handlers will be executed in the order they were attached. This allows you to modularize your code and separate different aspects of an event’s behavior into different handlers.

How can I stop the execution of further event handlers?

If you want to stop the execution of further event handlers after a certain handler has been executed, you can set the handled property of the event object to true in your event handler. This will prevent any subsequent handlers from being executed.

Can I modify the data associated with an event?

Yes, you can modify the data associated with an event in an event handler. The event object passed to the event handler contains a data property that you can modify. This can be useful if you want to pass information from one event handler to another.

How can I detach an event handler?

You can detach an event handler using the off method. This will remove the event handler from the event and prevent it from being executed when the event is triggered.

Can I trigger an event without any event handlers?

Yes, you can trigger an event even if it doesn’t have any event handlers attached. However, since there are no handlers to execute, triggering the event will have no effect.

Can I use events in Yii2 to communicate between different parts of my application?

Yes, events in Yii2 can be used to communicate between different parts of your application. By triggering an event in one part of your application, you can execute code in another part of your application that is listening for that event.

What is the difference between global and class-level events?

Global events are events that are triggered on the global event manager, while class-level events are triggered on a specific instance of a class. Global events can be used to communicate between different parts of your application, while class-level events are typically used to customize the behavior of a specific instance.

Can I use events to modify the behavior of a Yii2 component?

Yes, you can use events to modify the behavior of a Yii2 component. By attaching an event handler to a component’s event, you can execute code when the event is triggered, allowing you to add, modify, or replace the component’s default behavior.

Steven O'BrienSteven O'Brien
View Author

Steve O'Brien is the founder of Newicon a UK based web application and software development company. He is a passionate coder and technology enthusiast with keen interest in machine learning and computational neuroscience. When he is not coding for Newicon, you might find him singing and riffing out with with his band In Extremis. You can connect with Steve on Facebook, Linked In, Twitter, or his blog www.steve-obrien.com.

Advanced
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week