1<?php 2 3/* 4 * This file is part of SwiftMailer. 5 * (c) 2004-2009 Chris Corbyn 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11/** 12 * Throttles the rate at which emails are sent. 13 * 14 * @author Chris Corbyn 15 */ 16class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin implements Swift_Plugins_Sleeper, Swift_Plugins_Timer 17{ 18 /** Flag for throttling in bytes per minute */ 19 const BYTES_PER_MINUTE = 0x01; 20 21 /** Flag for throttling in emails per second (Amazon SES) */ 22 const MESSAGES_PER_SECOND = 0x11; 23 24 /** Flag for throttling in emails per minute */ 25 const MESSAGES_PER_MINUTE = 0x10; 26 27 /** 28 * The Sleeper instance for sleeping. 29 * 30 * @var Swift_Plugins_Sleeper 31 */ 32 private $sleeper; 33 34 /** 35 * The Timer instance which provides the timestamp. 36 * 37 * @var Swift_Plugins_Timer 38 */ 39 private $timer; 40 41 /** 42 * The time at which the first email was sent. 43 * 44 * @var int 45 */ 46 private $start; 47 48 /** 49 * The rate at which messages should be sent. 50 * 51 * @var int 52 */ 53 private $rate; 54 55 /** 56 * The mode for throttling. 57 * 58 * This is {@link BYTES_PER_MINUTE} or {@link MESSAGES_PER_MINUTE} 59 * 60 * @var int 61 */ 62 private $mode; 63 64 /** 65 * An internal counter of the number of messages sent. 66 * 67 * @var int 68 */ 69 private $messages = 0; 70 71 /** 72 * Create a new ThrottlerPlugin. 73 * 74 * @param int $rate 75 * @param int $mode, defaults to {@link BYTES_PER_MINUTE} 76 * @param Swift_Plugins_Sleeper $sleeper (only needed in testing) 77 * @param Swift_Plugins_Timer $timer (only needed in testing) 78 */ 79 public function __construct($rate, $mode = self::BYTES_PER_MINUTE, Swift_Plugins_Sleeper $sleeper = null, Swift_Plugins_Timer $timer = null) 80 { 81 $this->rate = $rate; 82 $this->mode = $mode; 83 $this->sleeper = $sleeper; 84 $this->timer = $timer; 85 } 86 87 /** 88 * Invoked immediately before the Message is sent. 89 * 90 * @param Swift_Events_SendEvent $evt 91 */ 92 public function beforeSendPerformed(Swift_Events_SendEvent $evt) 93 { 94 $time = $this->getTimestamp(); 95 if (!isset($this->start)) { 96 $this->start = $time; 97 } 98 $duration = $time - $this->start; 99 100 switch ($this->mode) { 101 case self::BYTES_PER_MINUTE: 102 $sleep = $this->throttleBytesPerMinute($duration); 103 break; 104 case self::MESSAGES_PER_SECOND: 105 $sleep = $this->throttleMessagesPerSecond($duration); 106 break; 107 case self::MESSAGES_PER_MINUTE: 108 $sleep = $this->throttleMessagesPerMinute($duration); 109 break; 110 default: 111 $sleep = 0; 112 break; 113 } 114 115 if ($sleep > 0) { 116 $this->sleep($sleep); 117 } 118 } 119 120 /** 121 * Invoked when a Message is sent. 122 * 123 * @param Swift_Events_SendEvent $evt 124 */ 125 public function sendPerformed(Swift_Events_SendEvent $evt) 126 { 127 parent::sendPerformed($evt); 128 ++$this->messages; 129 } 130 131 /** 132 * Sleep for $seconds. 133 * 134 * @param int $seconds 135 */ 136 public function sleep($seconds) 137 { 138 if (isset($this->sleeper)) { 139 $this->sleeper->sleep($seconds); 140 } else { 141 sleep($seconds); 142 } 143 } 144 145 /** 146 * Get the current UNIX timestamp. 147 * 148 * @return int 149 */ 150 public function getTimestamp() 151 { 152 if (isset($this->timer)) { 153 return $this->timer->getTimestamp(); 154 } 155 156 return time(); 157 } 158 159 /** 160 * Get a number of seconds to sleep for. 161 * 162 * @param int $timePassed 163 * 164 * @return int 165 */ 166 private function throttleBytesPerMinute($timePassed) 167 { 168 $expectedDuration = $this->getBytesOut() / ($this->rate / 60); 169 170 return (int) ceil($expectedDuration - $timePassed); 171 } 172 173 /** 174 * Get a number of seconds to sleep for. 175 * 176 * @param int $timePassed 177 * 178 * @return int 179 */ 180 private function throttleMessagesPerSecond($timePassed) 181 { 182 $expectedDuration = $this->messages / $this->rate; 183 184 return (int) ceil($expectedDuration - $timePassed); 185 } 186 187 /** 188 * Get a number of seconds to sleep for. 189 * 190 * @param int $timePassed 191 * 192 * @return int 193 */ 194 private function throttleMessagesPerMinute($timePassed) 195 { 196 $expectedDuration = $this->messages / ($this->rate / 60); 197 198 return (int) ceil($expectedDuration - $timePassed); 199 } 200} 201