Hey folks. There was an idea for an event system that could be part of the v4 Base. It's almost a year now since we worked on that, but haven't touched it for a while now. I took this topic back onto the desk, refactored the old approach and put it into a plugin for v3.5. So you already got the chance to play with it now and help to refine it. Any contribution is highly appreciated. If you think it is crap, it's okay - we can restart from scratch together - this is just a test concept. Here is what I have included so far:
- emit events from any point of your app
- attach one or multiple listeners to an event
- a listener (hook) can have a priority order
- additional options for listeners
- local events on specific objects
- send payload and context data with an event
- sub-events and event propagation
- stop the event chain
- works with F3 v3.5 and PHP v5.3
How does it work: The Event class is a child of Prefab, so you can get it everywhere like this:
// fetch the global Event instance
$events = \Event::instance();
Define a listener / hook:
// typical F3 callstring
$events->on('user_login', 'Notification->user_login');
// or with callbacks
$events->on('user_login', function(){
// ...
});
Send an event:
$events->emit('user_login');
Send payload with event:
$events->on('user_login', function($username){
\Logger::log($username.' logged in');
});
$events->emit('user_login', 'freakazoid');
Multiple listeners with prioritization:
$events->on('user_login', function($username){
\Logger::log($username.' logged in');
}, 10); // 10 is default priority
$events->on('user_login', function(){
\Flash::addMessage('You have logged in successfully');
}, 20); // 20 is a higher priority and is called first
$events->emit('user_login', 'freakazoid');
Stop the event chain:
$events->on('user_login', function($username){
\Logger::log($username.' logged in');
});
$events->on('user_login', function(){
\Flash::addMessage('You have logged in successfully');
return false; // <-- skip any other listener on the same event
}, 20);
$events->emit('user_login', 'freakazoid');
// The logger event isn't called anymore
Additional event context data:
$events->on('user_login', function($username,$context){
if ($context['lang'] == 'en')
\Flash::addMessage('You have logged in successfully');
elseif($context['lang'] == 'de')
\Flash::addMessage('Du hast dich erfolgreich angemeldet');
});
$events->emit('user_login', 'freakazoid', array('lang'=>'en'));
Additional listener options:
$events->on('user_login', function($username,$context,$event){
\Flash::addMessage('You have logged in successfully', $event['options']['type']);
}, 20, array('type'=>'success'));
I think these are the basic usage samples that could fit the most cases. Nevertheless here are some more advanced things you can do:
Filter payload:
$events->on('get_total', function($basket){
$sum = 0;
foreach($basket as $prod) {
$sum+=$prod;
}
return $sum;
});
$products = array(
'a' => 2,
'b' => 8,
'c' => 15,
);
$sum = $events->emit('get_total',$products);
echo $sum; // 25
Add a sub-event. These are called after the parent event. Listeners and sub-events follow the FIFO processing, which means the first that is registered is the first that will be called (First-In-First-Out)
$events->on('get_total.tax', function($sum){
return $sum+($sum*0.2);
});
$events->on('get_total.shipping', function($sum){
return $sum+5;
});
$sum = $events->emit('get_total',$products);
echo $sum; // 35
Remove hooks:
$events->off('get_total.tax');
$sum = $events->emit('get_total',$products);
echo $sum; // 30
There is also a mechanic build in which supports local events for mappers and such, which have implemented it:
$user = new \Model\User();
$events->watch($user)->on('update.email','\Mailer->sendEmailActivationLink');
I have a package here with the libs, unit tests and some more test cases:
https://dl.dropboxusercontent.com/u/3077539/f3_event_lib.zipTests are public at
http://f3.ikkez.de/event but to get this running in your local
F3-dev app just unzip it in its root dir and add this to your index.php:
// Event Tests
$f3->concat('AUTOLOAD',',event/');
\EventTests::init();
have a nice day.