ZF2 EventManager
Slightly free translation of article about the
The article in the examples, says that this
The original and the translation was written with the release of zf2.dev4 before .beta1, significant changes have occurred. But still the article should be used for reference, nothing more.
the
The event is an object that contains the data, when and how it was initiated: which object it was called, passed parameters, etc. the Event also has a name that allows you to bind handlers to a specific event referring to the event name.
The minimum things needed to run all this:
the
the
The output will get:
the
Nothing complicated!
But what is the second argument of 'null' in method
As a rule, the object
the
This example essentially does the same thing as the first. The main difference is that the second argument of the method
You may have 2 questions:
the
The response further.
One of the principles that I try to follow ZF2's the Liskov substitution Principle. The interpretation of this principle can be as follows: For any class, which in the future may need to override another class must be defined “basic” interface. And it allows developers to use a different implementation of a class by defining the methods of this interface.
Therefore developed interface
One aspect that the EventManager implementation provides is the ability to interact with the
How does it work?
the
This example is almost identical to the previous one. The only difference is that the first argument to the method
This is the place where the constructor parameters
Combine the definition of the Example class and a static event handler of 2 recent examples, and complete the following:
the
The output will get:
the
Now extend the Example class:
the
Note the parameters that we pass to the constructor
The names used as contexts or targets need not be class names; you can use arbitrary names. For example, if you have a set of classes that are responsible for Caching or logging, you can name contexts as "log" and "cache" and use these names rather than class names.
If you do not want the event Manager processed the static events, you can pass the parameter
the
To connect back the processing of static events:
the
You may need to sign a class on handling multiple events, and in this “class handler” to define methods to handle some events. To do this, you can implement in your “class handler” interface
(I do not understand what is translated, the example below is more intuitive).
the
To add this handler in the event Manager use:
the
and any event that needs processing our handler (
Pay attention to the method
You may need to get the result of event handlers. You need to keep in mind that one event can be signed by multiple handlers. And the result of each processor must not conflict with the results of other handlers.
the
the
Typically, when you trigger the event handling, you should not be heavily dependent on the result handlers. Moreover, when the initiation of the event, you can't always be sure which event handler will be subscribed to this event (maybe even there will be no handler, and no result, you will not get). However, you have the ability to abort handlers if the required result has already been obtained in one of the handlers.
If one of the handlers got the result expected by the initiator of the event; or the processor decides that something goes wrong; or one of the handlers, for whatever reason, decide that you do not need to fulfill the following handlers — you have a mechanism to break the ‘stack’ event handlers.
An example where this may be necessary can serve as a caching mechanism based on the
the
Example:
the
the arguments to the method
Following this way, we know that the probable cause of the interrupt event processing is that the result of the last executed handler meets our criteria.
Another way to interrupt the processing of an event is the use of method
the
With this approach, you can be sure that event processing is interrupted because of compliance of the work product handler our criteria.
You may want to specify the order of execution of handlers. For example, you want to handler, host Log, guaranteed to be executed despite the fact that other handlers can abort processing of this event at any time. Or if you implement Caching: a handler that searches the cache was executed before the other; and the processor, the leading entry in the cache, on the contrary, was carried out later.
the
the
Matthew Weier O Phinney recommends use priorities only when absolutely necessary. And I agree with him.
In the previous sections it was written that using events and interrupts the processing thereof — an interesting way to implement a caching mechanism in the application. Let's create a full example.
First define a method that could use caching.
Matthew Weier O Phinney in their examples often as the name of the event uses the
In addition,
Our method might look something like this:
the
Now define the event handlers working with cache. We need to attach handlers to the events 'someExpensiveCall.pre' and 'someExpensiveCall.post'. In the first case, if the data is found in cache, we return them. In the past, we keep data in the cache.
We also assume that the variable
the
Of course, we could implement a caching mechanism on the object itself, and not to make to the event handler. But this approach gives us the ability to plug in handlers for caching to other events (to implement a caching mechanism for other classes, keeping the logic of fetching from the cache and saving it to the cache in one place), or to attach other handlers to these events (which would be done for example by logging or validation). The fact is that if you design your class using the event — you make it more flexible and extensible.
the
Of course, there is some roughness, on removing which the people work.
I'd add — nothing really new there, it's nice that such a thing will appear in Zende — I will definitely use.
I think the text is saturated with terms and hard to read (partly because of the small experience in translation of articles).
I have nothing against criticism.
Original: http://weierophinney.net/matthew.
Article based on information from habrahabr.ru
EventManager
in the Zend Framework 2 blog Matthew Weier O Phinney.The article in the examples, says that this
Zend\EventManager
, how to use it, what are the benefits of event-driven way of solving programming tasks in PHP. About what new things we can expect in ZF2.The original and the translation was written with the release of zf2.dev4 before .beta1, significant changes have occurred. But still the article should be used for reference, nothing more.
Terminology
the
Event Manager (event Manager)
an object that aggregates event handlers (Listener) for one or more named events (Event) and also initiates the processing of these events.
Listener (event Handler)
a function/method callback.
Event (Event)
the action of initiation which starts the execution of certain event handlers
The event is an object that contains the data, when and how it was initiated: which object it was called, passed parameters, etc. the Event also has a name that allows you to bind handlers to a specific event referring to the event name.
Start
The minimum things needed to run all this:
the
-
the
- an instance of the class
EventManager
.
the - One or more event handler bound to one or more events. the
- Call
EventManager::trigger()
to trigger the event.
the
use Zend\EventManager\EventManager;
$events = new EventManager();
$events- > attach('do', function($e) {
$event = $e->getName();
$params = $e->getParams();
printf(
'Handled event "%s", with parameters %s',
$event,
json_encode($params)
);
});
$params = array('foo' => 'bar', 'baz' => 'bat');
$events- > trigger('do', null, $params);
The output will get:
the
Handled event "do", with parameters {"foo":"bar","baz":"bat"}
Nothing complicated!
note: the examples use an anonymous function, but you can use the name of a function, static class method or object method.
But what is the second argument of 'null' in method
$events- > trigger()
?As a rule, the object
EventManager
is used within a class, and the event is triggered within any method of this class. This second argument is the "context" or "purpose", and in the case described, would be an instance of that class. This gives access to event handlers for request object that sometimes can be useful/necessary.the
use Zend\EventManager\EventCollection,
Zend\EventManager\EventManager;
class Example
{
protected $events;
public function setEventManager(EventCollection $events)
{
$this- > events = $events;
}
public function events()
{
if (!$this->events) {
$this- > setEventManager(new EventManager(
array(__CLASS__, get_called_class())
);
}
return $this->events;
}
public function do($foo, $baz)
{
$params = compact('foo', 'baz');
$this->events ()- > trigger(__FUNCTION__, $this, $params);
}
}
$example = new Example();
$example->events()->attach('do', function($e) {
$event = $e->getName();
$target = get_class($e- > getTarget()); // "Example"
$params = $e->getParams();
printf(
'Handled event "%s" on target "%s", with parameters %s',
$event,
$target
json_encode($params)
);
});
$example- > do('bar', 'bat');
This example essentially does the same thing as the first. The main difference is that the second argument of the method
trigger()
, we pass to the handler a context (the object that triggered the processing of this event) and the handler gets it via the method $e- > getTarget()
and can do with them anything (within reason :) ).You may have 2 questions:
the
-
the
- What is
EventCollection
?
the - And what kind of arguments we pass to the constructor
EventManager
?
The response further.
EventCollection vs EventManager
One of the principles that I try to follow ZF2's the Liskov substitution Principle. The interpretation of this principle can be as follows: For any class, which in the future may need to override another class must be defined “basic” interface. And it allows developers to use a different implementation of a class by defining the methods of this interface.
Therefore developed interface
EventCollection
that describes an object capable of aggregating listeners for events, and initiation of these events. the EventManager
— the standard implementation, which will be included in ZF2.StaticEventManager
One aspect that the EventManager implementation provides is the ability to interact with the
StaticEventCollection
. This class allows you to attach handlers not just to named events, but to events initiated by a specific context or purpose. the EventManager
, if processing of events takes event handlers (signed on the current context) from the object StaticEventCollection
and executes them.How does it work?
the
use Zend\EventManager\StaticEventManager;
$events = StaticEventManager::getInstance();
$events- > attach('Example', 'do', function($e) {
$event = $e->getName();
$target = get_class($e- > getTarget()); // "Example"
$params = $e->getParams();
printf(
'Handled event "%s" on target "%s", with parameters %s',
$event,
$target
json_encode($params)
);
});
This example is almost identical to the previous one. The only difference is that the first argument to the method
attach()
, we pass a context 'Example' to which I would like to add our handler. In other words when processing an event 'do' if this event is triggered by the context 'Example', then call our handler.This is the place where the constructor parameters
EventManager
play the role. The constructor allows you to pass a string, or an array of strings defining the name/names of contexts that need to take event handlers from the StaticEventManager
. If you pass an array of contexts, then all event handlers of these contexts will be performed. Event handlers attached directly to EventManager
will be executed before the handlers defined in StaticEventManager
.Combine the definition of the Example class and a static event handler of 2 recent examples, and complete the following:
the
$example = new Example();
$example- > do('bar', 'bat');
The output will get:
the
Handled event "do" on target "Example", with parameters {"foo":"bar","baz":"bat"}
Now extend the Example class:
the
class SubExample extends Example
{
}
Note the parameters that we pass to the constructor
EventManager
is an array of __CLASS__
and get_called_class()
. This means that when calling do()
of class SubExample
, our event handler is also executed. If we in the designer only specified 'SubExample’, our handler will be called only when SubExample::do()
but not in Example::do()
.The names used as contexts or targets need not be class names; you can use arbitrary names. For example, if you have a set of classes that are responsible for Caching or logging, you can name contexts as "log" and "cache" and use these names rather than class names.
If you do not want the event Manager processed the static events, you can pass the parameter
null
method of setStaticConnections()
:the
$events->setStaticConnections(null);
To connect back the processing of static events:
the
$events->setStaticConnections(StaticEventManager::getInstance());
Listener Aggregates
You may need to sign a class on handling multiple events, and in this “class handler” to define methods to handle some events. To do this, you can implement in your “class handler” interface
HandlerAggregate
. This interface defines 2 methods attach(EventCollection $events)
and detach(EventCollection $events)
.(I do not understand what is translated, the example below is more intuitive).
the
use Zend\EventManager\Event
Zend\EventManager\EventCollection,
Zend\EventManager\HandlerAggregate,
Zend\Log\Logger;
class LogEvents implements HandlerAggregate
{
protected $handlers = array();
protected $log;
public function __construct(Logger $log)
{
$this->log = $log;
}
public function attach(EventCollection $events)
{
$this->handlers[] = $events- > attach('do', array($this, 'log'));
$this->handlers[] = $events- > attach('doSomethingElse', array($this, 'log'));
}
public function detach(EventCollection $events)
foreach ($this- > handlers as $key => $handler) {
$events- > detach($handler);
unset($this->handlers[$key];
}
$this->handlers = array();
}
public function log(Event $e)
{
$event = $e->getName();
$params = $e->getParams();
$log- > info(sprintf('%s: %s', $event, json_encode($params)));
}
}
To add this handler in the event Manager use:
the
$doLog = new LogEvents($logger);
$events->attachAggregate($doLog);
and any event that needs processing our handler (
LogEvents
) will be treated in the appropriate method of the class. This allows you to define complex event handlers in one place (stateful handlers).Pay attention to the method
detach()
. In the same way as attach()
as argument it takes the object EventManager
, and “detaches” all handlers (from our array of handlers — $this->handlers[]
) of the event Manager. This is possible because EventManager::attach()
returns an object representing a handler — which we are ‘attached’ earlier in the method LogEvents::attach()
.Result handlers
You may need to get the result of event handlers. You need to keep in mind that one event can be signed by multiple handlers. And the result of each processor must not conflict with the results of other handlers.
the
EventManager
returns the object ResponseCollection
. This class inherits from the class SplStack
, and gives you access to the results of all handlers (the Result of the last handler will be the beginning of the stack).ResponseCollection
, in addition to the methods SplStack
has additional methods:the
-
the
first()
— the execution result of the first handler
the last()
— the result of the last handler
the contains($value)
— check the result in the stack results, returnstrue
/false
.
Typically, when you trigger the event handling, you should not be heavily dependent on the result handlers. Moreover, when the initiation of the event, you can't always be sure which event handler will be subscribed to this event (maybe even there will be no handler, and no result, you will not get). However, you have the ability to abort handlers if the required result has already been obtained in one of the handlers.
Interrupt event handling
If one of the handlers got the result expected by the initiator of the event; or the processor decides that something goes wrong; or one of the handlers, for whatever reason, decide that you do not need to fulfill the following handlers — you have a mechanism to break the ‘stack’ event handlers.
An example where this may be necessary can serve as a caching mechanism based on the
EventManager
. At the beginning of your method, you initiate the event “the search of data in the cache”, and if one of the handlers will find him responsible in the cache for the desired data, then aborts execution of other handlers, and you return the data fetched from the cache. If not, then you generate the data and fire the event “write cache”the
EventManager
provides two ways to do this. The first way is to use a special method triggerUntil()
, which checks the result of each executed processor, and if the result meets certain requirements, then execution of subsequent handlers is aborted.Example:
the
public function someExpensiveCall($criteria1, $criteria2)
{
$params = compact('criteria1', 'criteria2');
$results = $this->events()->triggerUntil(__FUNCTION__, $this, $params, function ($r) {
return ($r instanceof SomeResultClass);
});
if ($results- > stopped()) {
return $results- > last();
}
// ... do some work ...
}
the arguments to the method
triggerUntil()
are like the arguments to the method trigger()
, with the exception of an additional argument at the end of the callback function, which verifies the performance of each handler and if it returns true
, then execution of subsequent handlers is aborted.Following this way, we know that the probable cause of the interrupt event processing is that the result of the last executed handler meets our criteria.
Another way to interrupt the processing of an event is the use of method
the stoppropagation(true)
in the body of the handler. That will cause the event Manager to stop the execution of subsequent handlers.the
$events- > attach('do', function ($e) {
$e->the stoppropagation();
return new SomeResultClass();
});
With this approach, you can be sure that event processing is interrupted because of compliance of the work product handler our criteria.
the Order of execution of handlers
You may want to specify the order of execution of handlers. For example, you want to handler, host Log, guaranteed to be executed despite the fact that other handlers can abort processing of this event at any time. Or if you implement Caching: a handler that searches the cache was executed before the other; and the processor, the leading entry in the cache, on the contrary, was carried out later.
the
EventManager::attach()
and StaticEventManager::attach()
have an optional argument priority
(the default is 1), with which you can control the execution priority of handlers. A handler with higher priority are executed before handlers with a lower priority.the
$priority = 100;
$events- > attach('Example', 'do', function($e) {
$event = $e->getName();
$target = get_class($e- > getTarget()); // "Example"
$params = $e->getParams();
printf(
'Handled event "%s" on target "%s", with parameters %s',
$event,
$target
json_encode($params)
);
}, $priority);
Matthew Weier O Phinney recommends use priorities only when absolutely necessary. And I agree with him.
putting it all together: a Simple caching mechanism
In the previous sections it was written that using events and interrupts the processing thereof — an interesting way to implement a caching mechanism in the application. Let's create a full example.
First define a method that could use caching.
Matthew Weier O Phinney in their examples often as the name of the event uses the
__FUNCTION__
, and thinks it is a good practice, because you can easily write a macro to trigger events, and allows to determine the uniqueness of these names (especially since the context usually is the class of initiating event). And to separate events generated by one method, use the Postfix style "do.pre", "do.post", "do.error", etc.In addition,
$params
passed to the event — is a list of arguments passed to the method. This is because the parameters are not stored in the object, and handlers may not get the options they want out of context. But the question remains, how to call the resulting setting for an event that writes to the cache? The example uses __RESULT__
, which is convenient because a double underline with two sides, usually reserved by the system.Our method might look something like this:
the
public function someExpensiveCall($criteria1, $criteria2)
{
$params = compact('criteria1', 'criteria1');
$results = $this->events()->triggerUntil(__FUNCTION__ . '.pre', $this, $params, function ($r) {
return ($r instanceof SomeResultClass);
});
if ($results- > stopped()) {
return $results- > last();
}
// ... do some work ...
$params['__RESULT__'] = $calculatedResult;
$this->events ()- > trigger(__FUNCTION__ . '.post', $this, $params);
return $calculatedResult;
}
Now define the event handlers working with cache. We need to attach handlers to the events 'someExpensiveCall.pre' and 'someExpensiveCall.post'. In the first case, if the data is found in cache, we return them. In the past, we keep data in the cache.
We also assume that the variable
$cache
defined earlier, and is similar to the Zend_Cache object. Handler 'someExpensiveCall.pre' we set a priority of 100, to ensure the execution of the handler before the others, and for 'someExpensiveCall.post' the priority of -100, if other handlers want to modify data before writing to the cache.the
$events- > attach('someExpensiveCall.pre', function($e) use ($cache) {
$params = $e->getParams();
$key = md5(json_encode($params));
$hit = $cache->load($key);
return $hit;
}, 100);
$events- > attach('someExpensiveCall.post', function($e) use ($cache) {
$params = $e->getParams();
$result = $params['__RESULT__'];
unset($params['__RESULT__']);
$key = md5(json_encode($params));
$cache->save($result, $key);
}, -100);
note: we mogliby defineHandlerAggregate
, and store$cache
in class property and not import it into an anonymous function.
Of course, we could implement a caching mechanism on the object itself, and not to make to the event handler. But this approach gives us the ability to plug in handlers for caching to other events (to implement a caching mechanism for other classes, keeping the logic of fetching from the cache and saving it to the cache in one place), or to attach other handlers to these events (which would be done for example by logging or validation). The fact is that if you design your class using the event — you make it more flexible and extensible.
Opinion
the
EventManager
is a new and powerful addition to Zend Framework. Now it is used with the new MVC prototype for the empowerment of some of its aspects. After the release of ZF2 event model, I am sure, will be very popular.Of course, there is some roughness, on removing which the people work.
I'd add — nothing really new there, it's nice that such a thing will appear in Zende — I will definitely use.
I think the text is saturated with terms and hard to read (partly because of the small experience in translation of articles).
I have nothing against criticism.
Original: http://weierophinney.net/matthew.
Comments
Post a Comment