An introduction to symfony events. Includes:
- Symfony Events Overview
- Conceptual Examples
- Utilizing Symfony Core Events
- Creating Your Own Event System
The 7 Things I Know About Cyber Security After 25 Years | April 2024
Symfony Events
1. Symfony Events
Every event happens for a reason
Brent Shaffer
@bshaffer
CentreSource Interactive Agency
www.brentertainment.com
June 1st, 2010
2. Symfony Events
“All [symfony] events are blessings given to us to learn from.”
- Elizabeth Kubler-Ross
“Great [symfony] events make me quiet and calm; it
is only [fat controllers] that irritate my nerves.”
- Queen Elizabeth
“Keep [symfony events] around for when shit gets
crazy. Cause shit will get crazy!”
- Carp Guy
3. What are Symfony Events
Overview
• Symfony events are part of the Event Dispatcher component
• Implements the Observer design pattern (Design Patterns, GoF)
• “...The subject maintains a list of its dependants, called observers, and notifies them
automatically of any state changes”
• Lightweight, consists of only 2 classes
1. sfEventDispatcher - object responsible for maintaining a register of listeners and
calling them whenever an event is notified
2. sfEvent - object that stores information about the notified event
4. sfEventDispatcher
Connect
- Connects a listener to a given event name.
$dispatcher->connect('event.name', array($php, 'callable'));
Notify
- Notifies all listeners of a given event.
$dispatcher->notify(sfEvent $event);
Notify Until
- Notifies all listeners of a given event until one returns a non null value.
$dispatcher->notifyUntil(sfEvent $event);
Filter
- Filters a value by calling all listeners of a given event.
$dispatcher->filter(sfEvent $event, $value);
5. sfEventDispatcher
Where is it?
sfContext and sfApplicationConfiguration hold a single sfEventDispatcher
instance and facilitate it to other classes
How do I access it?
// sfContext and sfApplicationConfiguration:
$this->context->getEventDispatcher();
sfApplicationConfiguration::getActive()->getEventDispatcher();
// sfFormSymfony subclasses
self::getEventDispatcher();
// sfActions subclass / sfBaseTask subclass / sfPHPView:
$this->dispatcher
6. sfEvent
Constructor
public function __construct($subject, $name, $parameters = array())
Subject The subject of the event (the object notifying the event, or object the event is about. It can also be null);
Name The event name
Parameters An array of parameters to pass to the listeners (an empty array by default)
The event object can also be accessed as an array to get its parameters
$method = $event['method'];
// Is the same as
$parameters = $event->getParameters();
$method = $parameters['method'];
7. Conceptual Example
Receptionist with Four Employees
Notify
A client calls in for Employee One to call them back Receptionist
(event dispatcher)
// aClient.class.php
$this->dispatcher
->connect('employee_one.becomes_available', 1 4
array($this, 'callMe'));
A lawyer calls in, needs Employee One to sign
2 3
some lawyer stuff
// aLawyer.class.php
$this->dispatcher
->connect('employee_one.becomes_available', array($this, 'signMyStuff'));
Employee One lets secretary know when he’s available.
// EmployeeOne.class.php
public function becomesAvailable() {
$this->dispatcher->notify(new sfEvent($this, 'employee_one.available'));
}
8. Conceptual Example
Receptionist with Four Employees
Notify
Employee Three wants to know when employee 2 Receptionist
(event dispatcher)
leaves, so he can eat his leftovers
// EmployeeThree.class.php
$this->dispatcher->connect('employee_two.goes_home',
1 4
array($this, 'stealLeftovers'));
2 3
public function stealLeftovers(sfEvent $event) {
$employeeTwo = $event->getSubject();
$this->eat($employeeTwo->getLeftovers());
}
Employee Two lets secretary know when he’s leaving.
// EmployeeTwo.class.php
public function goesHome() {
$this->dispatcher->notify(new sfEvent($this, 'employee_two.goes_home'));
}
9. Conceptual Example
Receptionist with Four Employees
Notify Until
Employee 4 puts out an add in the newspaper Receptionist
(event dispatcher)
Now Hiring: Personal assistant.
Strong communication skills.
Willing to take excessive sexual 1 4
harassment. Call before 5:00 PM
2 3
Individuals call in to register
// aProspect.class.php
$this->dispatcher->connect('employee_four.job_offering', array($this, 'callMe'));
// aFeministMovement.class.php
$this->dispatcher->connect('employee_four.job_offering', array($this, 'sue'));
Employee Four notifies the registered listeners until he finds an employee
// EmployeeFour.class.php (at 5:00)
$event = $this->dispatcher->notifyUntil(new sfEvent($this, 'employee_four.job_offering'));
if($event->isProcessed()) $employee = $event->getReturnValue();
10. Conceptual Example
Receptionist with Four Employees
Filter
Employee 4 puts out an add in the newspaper, Receptionist
yadda yadda, but this time, he wants a list. (event dispatcher)
// aProspect.class.php
$this->dispatcher
1 4
->connect('employee_four.job_offering',
array($this, 'addToList'));
2 3
public function addToList($event, $list) {
$list[] = $self;
$event->setReturnValue($list);
}
Employee Four notifies the registered listener and provides the item to filter
// EmployeeFour.class.php (at 5:00)
$event = $this->dispatcher->filter(new sfEvent($this, 'employee_four.job_offering'), $list);
$list = $event->getReturnValue();
11. Utilizing Symfony Core Events
Lazy-add mobile templates
We will now demonstrate how useful symfony core events can be in your
application
We will show how to add mobile-specific layouts to your existing application.
“Lazy-add” - add mobile templates as you go while keeping everything without a
mobile template still accessible. In other words, if a mobile template exists, render it
for mobile devices. Otherwise, render the default template.
Based largely on the blog post “How to create an optimized version of your website
for the iPhone in symfony 1.1”
12. Utilizing Symfony Core Events
Lazy-add mobile templates
Connect to “filter parameters” event, add request format for the appropriate
user agent
// ProjectConfiguration.class.php
public function setup()
{
...
$this->dispatcher->connect('request.filter_parameters',
array($this, 'filterRequestParameters'));
}
public function filterRequestParameters(sfEvent $event, $parameters)
{
$request = $event->getSubject();
if (preg_match('#Mobile/.+Safari#i', $request->getHttpHeader('User-Agent'))) {
$request->setRequestFormat('m');
}
return $parameters;
}
13. Utilizing Symfony Core Events
Lazy-add mobile templates
Connect to “view.configure_format” event to only set if the template exists
// ProjectConfiguration.class.php
public function setup()
{
...
$this->dispatcher->connect('view.configure_format', array($this, 'configureMobileFormat'));
}
public function configureMobileFormat(sfEvent $event)
{
if ('m' == $event['format']) {
$view = $event->getSubject();
$dir = sfConfig::get('sf_app_module_dir').'/'.$view->getModuleName().'/templates/'.
$view->getActionName().$view->getViewName().$view->getExtension();
if (!file_exists($dir)) {
$view->setExtension('.php');
}
}
}
14. Utilizing Symfony Core Events
Lazy-add mobile templates
Set our mobile format in factories.yml
// factories.yml
all:
request:
class: sfWebRequest
param:
formats:
m: text/html
...
Now, we can add mobile templates as we go, while keeping the rest of the
site as-is!
15. Creating Your Own Events
Alert System
We will now demonstrate how creating your own set of events can be useful.
Using events keeps your libraries modular and decoupled. It also makes them very
easy to unit test.
You want to build a system in your application that sends email alerts out for
various triggers. You’re smart, so you will accomplish this using the event dispatcher
// ProjectConfiguration.class.php
public function setup()
{
...
include_once sfConfig::get('sf_lib_dir').'/alert/sfAlertDispatcher.class.php';
$this->alertDispatcher = new sfAlertDispatcher();
$this->alertDispatcher->connectEvents($this->dispatcher);
}
16. Creating Your Own Events
Alert System
Handle your events inside your model. Keep things modular.
// lib/alert/sfAlertDispatcher.class.php
class cfitAlertDispatcher
{
public function connectEvents(sfEventDispatcher $dispatcher)
{
// = Raise Alerts =
$dispatcher->connect('contact.form_submitted', array($this, 'alertContactForm'));
$dispatcher->connect('support.ticket_submitted', array($this, 'alertTicketSubmitted'));
// = Resolve Alerts =
$dispatcher->connect('support.ticket_closed', array($this, 'resolveSupportTicket'));
}
}
17. Creating Your Own Events
Alert System
Write your implementation logic for the appropriate listeners
// lib/alert/sfAlertDispatcher.class.php
class cfitAlertDispatcher
{
...
public function alertContactForm(sfEvent $event)
{
// ...do alert logic, etc
$form = $event->getSubject();
$this->mailer->send($this->getContactEmailFromForm($form));
}
public function alertTicketSubmitted(sfEvent $event)
{
// Notice how easy this logic will be to unit test!
$ticket = $event->getSubject();
if($ticket->isAtleastCritical()) {
$this->mailer->send($this->getEmailFromTicket($ticket));
}
}
...
}
18. Creating Your Own Events
Alert System
Add the logic in your application for triggering the events
// contactActions.class.php
...
$this->dispatcher->notify(new sfEvent($form, 'contact.form_submitted'));
// ticketActions.class.php
...
$dispatcher->notify(new sfEvent($ticket, 'support.ticket_submitted'));
...
// Ticket.class.php
public function close(sfEventDispatcher $dispatcher)
{
...
$dispatcher->notify(new sfEvent($this, 'support.ticket_closed'));
}
19. Creating Your Own Events
Alert System
And that’s it! The rest is a matter of implementation logic for your specific
application
Notice how easy cfitAlertDispatcher is to unit test. Get rid of all those fat controllers!
And one last quote...
“...Gather in your resources, rally all your faculties, marshal all your energies, focus
all your capacities upon mastery of [symfony events]”
- John Haggai