> Info: While the approach of calling [[Yii::createObject()]] looks more complicated, it is more powerful due to
the fact that it is implemented on top of a [dependency injection container](concept-di-container.md).
> Info: While the approach of calling [[Yii::createObject()]] looks more complicated, it is more powerful because it is implemented on top of a [dependency injection container](concept-di-container.md).
The [[yii\base\Object]] class enforces the following object lifecycle:
...
...
@@ -90,5 +89,5 @@ The [[yii\base\Object]] class enforces the following object lifecycle:
3. Post-initialization within [[yii\base\Object::init()|init()]]. You may override this method to perform sanity checks and normalization of the properties.
4. Object method calls.
The first three steps all happen within the object's constructor. This means that once you get an object instance,
it has already been initialized to a proper state that you can reliably work with.
The first three steps all happen within the object's constructor. This means that once you get a class instance (i.e., an object),
that object has already been initialized to a proper, reliable state.
@@ -130,20 +64,24 @@ refer to the [Configurations](concept-configurations.md#configuration-format) se
When attaching an event handler, you may provide additional data as the third parameter to [[yii\base\Component::on()]].
The data will be made available to the handler when the event is triggered and the handler is called. For example,
The data will be made available to the handler when the event is triggered and the handler is called. For example:
```php
// The following code will display "abc" when the event is triggered
// because $event->data contains the data passed to "on"
$foo->on(Foo::EVENT_HELLO,function($event){
// because $event->data contains the data passed as the 3rd argument to "on"
$foo->on(Foo::EVENT_HELLO,'function_name','abc');
functionfunction_name($event){
echo$event->data;
},'abc');
}
```
You may attach one or multiple handlers to a single event. When an event is triggered, the attached handlers
will be called in the order they are attached to the event. If a handler needs to stop the invocation of the
handlers behind it, it may set the [[yii\base\Event::handled]] property of the `$event` parameter to be true,
like the following,
Event Handler Order
-------------------
You may attach one or more handlers to a single event. When an event is triggered, the attached handlers
will be called in the order that they were attached to the event. If a handler needs to stop the invocation of the
handlers that follow it, it may set the [[yii\base\Event::handled]] property of the `$event` parameter to be true:
```php
$foo->on(Foo::EVENT_HELLO,function($event){
...
...
@@ -153,8 +91,7 @@ $foo->on(Foo::EVENT_HELLO, function ($event) {
By default, a newly attached handler is appended to the existing handler queue for the event.
As a result, the handler will be called in the last place when the event is triggered.
To insert the new handler at the start of the handler queue so that the handler gets called first, y
ou may call [[yii\base\Component::on()]] by passing the fourth parameter `$append` as false:
To insert the new handler at the start of the handler queue so that the handler gets called first, you may call [[yii\base\Component::on()]], passing false for the fourth parameter `$append`:
```php
$foo->on(Foo::EVENT_HELLO,function($event){
...
...
@@ -162,23 +99,75 @@ $foo->on(Foo::EVENT_HELLO, function ($event) {
},$data,false);
```
Besides calling the `on()` method, you may also attach event handlers in [configurations](concept-configurations.md)
like the following. For more details, please refer to the [Configurations](concept-configurations.md#configuration-format)
Events are triggered by calling the [[yii\base\Component::trigger()]] method. The method requires an *event name*,
and optionally an event object that describes the parameters to be passed to the event handlers. For example:
```php
[
'on hello'=>function($event){
echo'hello event is triggered';
namespaceapp\components;
useyii\base\Component;
useyii\base\Event;
classFooextendsComponent
{
constEVENT_HELLO='hello';
publicfunctionbar()
{
$this->trigger(self::EVENT_HELLO);
}
]
}
```
With the above code, any calls to `bar()` will trigger an event named `hello`.
> Tip: It is recommended to use class constants to represent event names. In the above example, the constant
`EVENT_HELLO` represents the `hello` event. This approach has three benefits. First, it prevents typos. Second, it can make events recognizable for IDE
auto-completion support. Third, you can tell what events are supported in a class by simply checking its constant declarations.
Sometimes when triggering an event you may want to pass along additional information to the event handlers.
For example, a mailer may want pass the message information to the handlers of the `messageSent` event so that the handlers
can know the particulars of the sent messages. To do so, you can provide an event object as the second parameter to
the [[yii\base\Component::trigger()]] method. The event object must be an instance of the [[yii\base\Event]] class,
or of a child class. For example:
```php
namespaceapp\components;
useyii\base\Component;
useyii\base\Event;
classMessageEventextendsEvent
{
public$message;
}
classMailerextendsComponent
{
constEVENT_MESSAGE_SENT='messageSent';
publicfunctionsend($message)
{
// ...sending $message...
$event=newMessageEvent;
$event->message=$message;
$this->trigger(self::EVENT_MESSAGE_SENT,$event);
}
}
```
When the [[yii\base\Component::trigger()]] method is called, it will call all handlers attached to
The so-called *global event* is actually a trick based on the event mechanism described above.
It requires a globally accessible singleton, such as the [application](structure-applications.md) instance.
Yii supports a so-called *global event*, which is actually a trick based on the event mechanism described above.
The global event requires a globally accessible Singleton, such as the [application](structure-applications.md) instance itself.
An event sender, instead of calling its own `trigger()` method, will call the singleton's `trigger()` method
to trigger the event. Similarly, the event handlers are attached to the event of the singleton. For example,
To create the global evant, an event sender calls the Singleton's `trigger()` method
to trigger the event, instead of calling the sender's own `trigger()` method. Similarly, the event handlers are attached to the event on the Singleton. For example:
```php
useYii;
...
...
@@ -284,9 +273,9 @@ Yii::$app->on('bar', function ($event) {
In PHP, class member variables are also called *properties*. These variables are part of the class definition, and are used
to represent the state of a class instance (i.e., to differentiate one instance of the class from another). In practice, you may often want to handle the reading or writing of properties in special ways. For example, you may want to trim a string when it is being assigned
to a `label` property. You could use the following code to achieve this task:
to represent the state of a class instance (i.e., to differentiate one instance of the class from another). In practice, you may often want to handle the reading or writing of properties in special ways. For example, you may want to always trim a string when it is being assigned
to a `label` property. You *could* use the following code to achieve this task:
```php
$object->label=trim($label);
```
The drawback of the above code is that you have to call `trim()` everywhere in your code where you might set the `label`
property. If in the future, the `label` property gets a new requirement, such as the first letter must be captialized, you would again have to modify every bit of code that assigns a value to `label`. The repetition of code leads to bugs and is a practice you want to avoid as much as possible.
The drawback of the above code is that you would have to call `trim()` everywhere in your code where you might set the `label`
property. If, in the future, the `label` property gets a new requirement, such as the first letter must be captialized, you would again have to modify every bit of code that assigns a value to `label`. The repetition of code leads to bugs, and is a practice you want to avoid as much as possible.
To solve this problem, Yii introduces a base class called [[yii\base\Object]] that supports defining properties
based on *getter* and *setter* class methods. If a class needs such support, it should extend from
[[yii\base\Object]] or a child class.
based on *getter* and *setter* class methods. If a class needs that functionality, it should extend from
[[yii\base\Object]], or from a child class.
> Info: Nearly every core class in the Yii framework extends from [[yii\base\Object]] or a child class.
This means that whenever you see a getter or setter in a core class, you can use it like a property.
...
...
@@ -69,9 +69,9 @@ There are several special rules for, and limitations on, the properties defined
This is because method names in PHP are case-insensitive.
* If the name of such a property is the same as a class member variable, the latter will take precedence.
For example, if the above `Foo` class has a member variable `label`, then the assignment `$object->label = 'abc'`
will affect the member variable 'label', that line would not call the `setLabel()` setter method.
will affect the *member variable* 'label'; that line would not call the `setLabel()` setter method.
* These properties do not support visibility. It makes no difference for the visibility of a property
if the defining getter or setter method is public, protected or private.
* The properties can only be defined by *non-static* getters and/or setters. Static methods will not be treated in this same manner.
Returning back to the problem described at the beginning of this guide, instead of calling `trim()` everywhere a `label` value is assigned, `trim()` only needs to be invoked within the setter `setLabel()`. And if a new requirement comes that requires the label be initially capitalized, the `setLabel()` method can quickly be modified without touching any other code. The one change will universally affect every assignment to `label`.
Returning back to the problem described at the beginning of this guide, instead of calling `trim()` everywhere a `label` value is assigned, `trim()`now only needs to be invoked within the setter `setLabel()`. And if a new requirement comes that requires the label be initially capitalized, the `setLabel()` method can quickly be modified without touching any other code. The one change will universally affect every assignment to `label`.