Document directory
- Defining events
- Triggering Events
- Attaching events
Welcome once again to this tour of the yii Framework'sCComponentClass. 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 wocould 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 coshould define "Registration complete" as an event. rather than hard-coding email logic or even setting different user properties in your code, the module cocould simply
Raise an event and forget about any specific implementation details. the details can be provided by application-specific les 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:
- Define the event
- Attach custom functionality to the event
- 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 wocould create a new event arbitrarily called
onUserRegisteredThat is raised after a user has been successfully registered. This wocould be defined within my your user module's code.
<?phppublic 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 throughYii::app()->user.
Triggering Events
The Controller class of the User Module wocould be responsible for triggering the event when it actually occurs.
<?phppublic 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 except CT one parameter which isCEventObject.
CEventObject 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 specified event (these parameters can be accessed later
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
array("user" => $user)Where$userIs an object representing our recently registered user. I then trigger the event and evoke yii's
raiseEvent()Function by calling the FunctiononUserRegistered()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.
<?phppublic 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 attachingsendMyEmail()Method toonUserRegisteredEvent so now when a new user registers
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 wocould 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
CComponentClass. 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 mechanic to store a list of valid PHP callbacks against an event.
The mechanic for defining events in yii is to simply create a function with the "on" prefix as you saw earlier. The mechanic 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 codeonMyEventName = callback, Which means the implementation for attaching functions to events must be handled in the components magic
__setMethod.
<?phppublic 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$nameStarts with the text "on" and that a method also exists with the same name as the value. If it does, yii assumes
$valueIs a representation of a callback which it needs to attach to the event defined
$name. Yii has a private member variable$_eWhich holds an array of callbacks keyed by event names, and it simply adds the callback to the list for the participates 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 cocould be as easy as looking up the event in
$_eVariable, 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 refactoredraiseEvent()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 how the yii Framework's
CComponentClass 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.