Event System

306 views
Skip to first unread message

ikkez

unread,
Nov 18, 2015, 7:16:50 AM11/18/15
to Fat-Free Framework
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.zip
Tests 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.

xfra35

unread,
Nov 18, 2015, 8:08:50 AM11/18/15
to Fat-Free Framework
Hey ikkez, thanks for sharing. That's food for the next release :)

Do I understand well that if we have:
$events->on('user.register.save.jig','My->hook1');
$events->on('user.register.mail','My->hook2');
$events->on('user.register','My->hook3');

the hooks will be triggered in the following order: hook3,hook2,hook1, no matter which priority is passed to the on() function?

ikkez

unread,
Nov 18, 2015, 9:33:50 AM11/18/15
to Fat-Free Framework

not exactly. It's actually like this:


Anatol Buchholz

unread,
Nov 18, 2015, 10:00:19 AM11/18/15
to Fat-Free Framework
Great! Now I know what to play with on coming weekend ;)

Rayne

unread,
Nov 18, 2015, 10:55:09 AM11/18/15
to Fat-Free Framework
It looks promising. Thank you for sharing.

My two cents from the IRC channel: I expected to stop it [the chain] with returning `true`, like `event listener consumed and finished the event successfully`.

ethanpil

unread,
Oct 5, 2016, 10:38:43 AM10/5/16
to Fat-Free Framework
I was just searching the group for an event dispatcher needed for an upcoming project and found this. What is the status of this very promising looking addition?

ikkez

unread,
Oct 5, 2016, 10:58:26 AM10/5/16
to Fat-Free Framework
Well, it is working... maybe it's overkill to port the DOM-like event bubbling to php, but this actually offers a lot (maybe too much?) possibilities. though it should be still fast.
I'd appreciate if you give it a try and let us know how it goes and if it fits you needs. I can put this on github to make it more official.

@eazuka

unread,
Mar 20, 2017, 1:53:28 PM3/20/17
to Fat-Free Framework

Hi @Ikkez,

Really nice work you have done with this event system, once again thanks for sharing.
After a long time, i'm now having reasons to do proper testing and possibly use it on a project.

I have few questions though, this is in regards to removing hooks and local events:

Remove hooks: $events->off('get_total');

What is the benefit of using the $event->off()  and not just remove or comment out //$event->on('get_total', function($sum){});  ?

Local event:

$user = new \Model\User();
$events
->watch($user)->on('update.email','\Mailer->sendEmailActivationLink');

The same question applies here too. I still don't see the reason for using $event->watch() when i can still continue using the straightforward $events->on('userUpdate.email', function($data){
   
return ;
 
});


I'm sure you have reasons for implementing these 2 features but they are just not obvious to me at the moment, so some comments from you will be appreciated.

Awaiting your feedback

Cheers

Sascha

unread,
Mar 20, 2017, 2:15:42 PM3/20/17
to Fat-Free Framework, @eazuka via Fat-Free Framework
Removing an event or just commenting it out won’t work, if you just create a plugin for an established system. Wordpress is heavily using events. You definitely don’t want to search through the base code removing single events. 

Regarding watch(). Sometimes you may want to have events behave different. Like in unit tests for example. You don’T want to send an email to the specific user you just create for a test but instead you want to write the mail content to a file. You would create an object of a class and use watch() on that particular object to behave differently this time for example. Or actually do something else as well, since you can create multiple events for the same trigger.
--
-- You've received this message because you are subscribed to the Google Groups group. To post to this group, send an email to f3-fra...@googlegroups.com. To unsubscribe from this group, send an email to f3-framework...@googlegroups.com. For more options, visit this group at https://groups.google.com/d/forum/f3-framework?hl=en
---
You received this message because you are subscribed to the Google Groups "Fat-Free Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to f3-framework...@googlegroups.com.
To post to this group, send email to f3-fra...@googlegroups.com.
Visit this group at https://groups.google.com/group/f3-framework.
To view this discussion on the web visit https://groups.google.com/d/msgid/f3-framework/d249118a-1cbf-496a-9b85-cd0816200e97%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

@eazuka

unread,
Mar 21, 2017, 7:15:51 AM3/21/17
to Fat-Free Framework
Thanks for your comments Sascha, Ah, so event->off('get_total') is meant to turn-off specific even emitting so that event don't get dispatched to all event listeners within the system, now i understand better.

 
"You definitely don’t want to search through the base code removing single events."
in this day and age, this isn't a problem, it can be done simply with a search feature in an IDEs. But aside from this quoted statement, You said it wouldn't work, why? why would commenting out /* event->emit('get_total') */ or removing it totally not work, what would happen to the listeners? shouldn't it be same effect as event->off('get_total') ?




On Monday, March 20, 2017 at 6:15:42 PM UTC, Sascha wrote:
Removing an event or just commenting it out won’t work, if you just create a plugin for an established system. Wordpress is heavily using events. You definitely don’t want to search through the base code removing single events. 

Regarding watch(). Sometimes you may want to have events behave different. Like in unit tests for example. You don’T want to send an email to the specific user you just create for a test but instead you want to write the mail content to a file. You would create an object of a class and use watch() on that particular object to behave differently this time for example. Or actually do something else as well, since you can create multiple events for the same trigger.

Sascha

unread,
Mar 21, 2017, 7:20:16 AM3/21/17
to Fat-Free Framework, @eazuka via Fat-Free Framework
Read the full line. If you’re the only maintainer, sure, you could change everything at any place you want. But for large tools like Wordpress you can’t just change the code base. It will break every single update or you have to re-do your changes with every single update. Beside that, you have to document the change for other developers to make sure they don’t wonder why it isn’t working for them etc etc

Luis Henrique

unread,
Mar 22, 2017, 4:28:36 PM3/22/17
to Fat-Free Framework
Where i can find this package https://dl.dropboxusercontent.com/u/3077539/f3_event_lib.zip give me 404 file not found

ikkez

unread,
Mar 23, 2017, 6:41:04 AM3/23/17
to Fat-Free Framework
I put it on github now.

https://github.com/ikkez/f3-events

Luis Henrique

unread,
Mar 23, 2017, 7:54:26 AM3/23/17
to Fat-Free Framework
Thanks ikkez.
Reply all
Reply to author
Forward
0 new messages