If you know Java/AWT you'll probably see where I've stolen the idea from
Previously what happened was that the Pluggable copied itself as a property into a plugin, as well as copying the plugin into itself. It created a recursive object which allowed plugins to control the pluggable and vice-versa. Plugins could contain a set of methods which had pre-defined names like onSend() and the Pluggable would check each plugin in turn to see if that method existed and then run it if it so. It meant that I had to copy lots of local variables into properties of the pluggable before notifying plugins so that they could make changes.
Now what happens instead is that "event objects" are passed around and "event listeners" receive this information and do what they need with it. So for example, when the pluggable beings sending a message it creates a SendEvent object and puts inside it what it needs to. It then notifties all SendListeners of this event and then carries on what it was doing. The event objects all also have a reference to the Pluggable which allows the same as the old design but avoids the recursion. It also means I don't need to keep storing silly things in properties of the pluggable, but rather I can put them in the event object as event data.
A plugin implements whichever of the interfaces it wants to respond to events for and the pluggable places references in containers according to the interfaces the plugin implements.
Here's the example
A mapper to check what's in the interface (built-in):
Code: Select all
<?php
/**
* Swift Mailer Mapper for Event Listeners
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift::Events
* @license GNU Lesser General Public License
*/
/**
* Maps event listener names to the methods they implement
* @package Swift::Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Events_ListenerMapper
{
/**
* The mapped names (Class => Method(s))
* @var array
*/
protected static $map = array(
"SendListener" => "sendPerformed",
"BeforeSendListener" => "beforeSendPerformed",
"CommandListener" => "commandSent",
"BeforeCommandListener" => "beforeCommandSent",
"ResponseListener" => "responseReceived",
"ConnectListener" => "connectPerformed",
"DisconnectListener" => "disconnectPerformed"
);
/**
* Get the name of the method which needs running based upon the listener name
* @return string
*/
public static function getNotifyMethod($listener)
{
if (isset(self::$map[$listener])) return self::$map[$listener];
else return false;
}
}Code: Select all
/**
* Add a new plugin to Swift
* Plugins must implement one or more event listeners
* @param Swift_Events_Listener The plugin to load
*/
public function attachPlugin(Swift_Events_Listener $plugin, $id)
{
foreach (array_keys($this->listeners) as $key)
{
$listener = "Swift_Events_" . $key;
if ($plugin instanceof $listener) $this->listeners[$key][$id] = $plugin;
}
}
// ... SNIP ...
/**
* Send a new type of event to all objects which are listening for it
* @param Swift_Events The event to send
* @param string The type of event
*/
public function notifyListeners($e, $type)
{
if (!empty($this->listeners[$type]) && $notifyMethod = Swift_Events_ListenerMapper::getNotifyMethod($type))
{
$e->setSwift($this);
foreach ($this->listeners[$type] as $k => $listener)
{
$listener->$notifyMethod($e);
}
}
else $e = null;
}
// ... SNIP ...
/**
* Send a message to any number of recipients
* @param Swift_Message The message to send. This does not need to (and shouldn't really) have any of the recipient headers set.
* @param mixed The recipients to send to. Can be Swift_Address or Swift_RecipientList. Note that all addresses apart from Bcc recipients will appear in the message headers
* @param Swift_Address The address to send the message from
* @return int The number of successful recipients
* @throws Swift_Connection_Exception If sending fails for any reason.
*/
public function send(Swift_Message $message, Swift_AddressContainer $recipients, Swift_Address $from)
{
$send_event = new Swift_Events_SendEvent($message, $list, $from, 0);
$this->notifyListeners($send_event, "BeforeSendListener");
// ... SNIP ...
// ... ALL CODE WHICH DEALS WITH SENDING ...
// ... MAKES USE OF $send_event
$send_event->setNumSent($num_sent);
$this->notifyListeners($send_event, "SendListener");
// ... SNIP ...
return $num_sent;
}Code: Select all
<?php
/**
* Swift Mailer Send Event
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift::Events
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../Events.php";
/**
* Generated every time a message is sent with Swift
* @package Swift::Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Events_SendEvent extends Swift_Events
{
/**
* A reference to the message being sent
* @var Swift_Message
*/
protected $message = null;
/**
* A reference to the sender address object
* @var Swift_Address
*/
protected $sender = null;
/**
* A reference to the recipients being sent to
* @var Swift_RecipientList
*/
protected $recipients = null;
/**
* The number of recipients sent to so
* @var int
*/
protected $sent = null;
/**
* Constructor
* @param Swift_Message The message being sent
* @param Swift_RecipientList The recipients
* @param Swift_Address The sender address
* @param int The number of addresses sent to
*/
public function __construct(Swift_Message $message, Swift_RecipientList $list, Swift_Address $from, $sent=0)
{
$this->message = $message;
$this->recipients = $list;
$this->sender = $from;
$this->sent = $sent;
}
/**
* Get the message being sent
* @return Swift_Message
*/
public function getMessage()
{
return $this->message;
}
/**
* Get the list of recipients
* @return Swift_RecipientList
*/
public function getRecipients()
{
return $this->recipients;
}
/**
* Get the sender's address
* @return Swift_Address
*/
public function getSender()
{
return $this->sender;
}
/**
* Set the number of recipients to how many were sent
* @param int
*/
public function setNumSent($sent)
{
$this->sent = (int) $sent;
}
/**
* Get the total number of addresses to which the email sent successfully
* @return int
*/
public function getNumSent()
{
return $this->sent;
}
}Code: Select all
<?php
/**
* Swift Mailer Send Event Listener Interface
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift::Events
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/Listener.php";
require_once dirname(__FILE__) . "/SendEvent.php";
/**
* Contains the list of methods a plugin requiring the use of a SendEvent must implement
* @package Swift::Events
* @author Chris Corbyn <chris@w3style.co.uk>
*/
interface Swift_Events_SendListener extends Swift_Events_Listener
{
/**
* Executes when Swift sends a message
* @param Swift_Events_SendEvent Information about the message being sent
*/
public function sendPerformed(Swift_Events_SendEvent $e);
}Code: Select all
<?php
/**
* Swift Mailer Rotating Connection Controller
* Please read the LICENSE file
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift::Plugin
* @license GNU Lesser General Public License
*/
/**
* Swift Rotating Connection Controller
* Invokes the nextConnection() method of Swift_Connection_Rotator upon sending a given number of messages
* @package Swift::Plugin
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_Plugin_ConnectionRotator implements Swift_Events_SendListener
{
/**
* The number of emails which must be sent before the connection is rotated
* @var int Threshold number of emails
*/
protected $threshold = 1;
/**
* The total number of emails sent on this connection
* @var int
*/
protected $count = 0;
/**
* Constructor
* @param int The number of emails to send before rotating
*/
public function __construct($threshold=1)
{
$this->setThreshold($threshold);
}
/**
* Set the number of emails to send before a connection rotation is tried
* @param int Number of emails
*/
public function setThreshold($threshold)
{
$this->threshold = (int) $threshold;
}
/**
* Get the number of emails which must be sent before a rotation occurs
* @return int
*/
public function getThreshold()
{
return $this->threshold;
}
/**
* Swift's SendEvent listener.
* Invoked when Swift sends a message
* @param Swift_Events_SendEvent The event information
* @throws Swift_Connection_Exception If the connection cannot be rotated
*/
public function sendPerformed(Swift_Events_SendEvent $e)
{
if (!is_a($e->getSwift()->connection, "Swift_Connection_Rotator"))
{
throw new Swift_Connection_Exception("The ConnectionRotator plugin cannot be used with connections other than Swift_Connection_Rotator.");
}
$this->count++;
if ($this->count >= $this->getThreshold())
{
$e->getSwift()->connection->nextConnection();
$this->count = 0;
}
}
}