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