|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Log |
|
17 * @subpackage Writer |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 * @version $Id: Mail.php 22971 2010-09-18 20:32:24Z mikaelkael $ |
|
21 */ |
|
22 |
|
23 /** Zend_Log_Writer_Abstract */ |
|
24 require_once 'Zend/Log/Writer/Abstract.php'; |
|
25 |
|
26 /** Zend_Log_Exception */ |
|
27 require_once 'Zend/Log/Exception.php'; |
|
28 |
|
29 /** Zend_Log_Formatter_Simple*/ |
|
30 require_once 'Zend/Log/Formatter/Simple.php'; |
|
31 |
|
32 /** |
|
33 * Class used for writing log messages to email via Zend_Mail. |
|
34 * |
|
35 * Allows for emailing log messages at and above a certain level via a |
|
36 * Zend_Mail object. Note that this class only sends the email upon |
|
37 * completion, so any log entries accumulated are sent in a single email. |
|
38 * |
|
39 * @category Zend |
|
40 * @package Zend_Log |
|
41 * @subpackage Writer |
|
42 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
43 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
44 * @version $Id: Mail.php 22971 2010-09-18 20:32:24Z mikaelkael $ |
|
45 */ |
|
46 class Zend_Log_Writer_Mail extends Zend_Log_Writer_Abstract |
|
47 { |
|
48 /** |
|
49 * Array of formatted events to include in message body. |
|
50 * |
|
51 * @var array |
|
52 */ |
|
53 protected $_eventsToMail = array(); |
|
54 |
|
55 /** |
|
56 * Array of formatted lines for use in an HTML email body; these events |
|
57 * are formatted with an optional formatter if the caller is using |
|
58 * Zend_Layout. |
|
59 * |
|
60 * @var array |
|
61 */ |
|
62 protected $_layoutEventsToMail = array(); |
|
63 |
|
64 /** |
|
65 * Zend_Mail instance to use |
|
66 * |
|
67 * @var Zend_Mail |
|
68 */ |
|
69 protected $_mail; |
|
70 |
|
71 /** |
|
72 * Zend_Layout instance to use; optional. |
|
73 * |
|
74 * @var Zend_Layout |
|
75 */ |
|
76 protected $_layout; |
|
77 |
|
78 /** |
|
79 * Optional formatter for use when rendering with Zend_Layout. |
|
80 * |
|
81 * @var Zend_Log_Formatter_Interface |
|
82 */ |
|
83 protected $_layoutFormatter; |
|
84 |
|
85 /** |
|
86 * Array keeping track of the number of entries per priority level. |
|
87 * |
|
88 * @var array |
|
89 */ |
|
90 protected $_numEntriesPerPriority = array(); |
|
91 |
|
92 /** |
|
93 * Subject prepend text. |
|
94 * |
|
95 * Can only be used of the Zend_Mail object has not already had its |
|
96 * subject line set. Using this will cause the subject to have the entry |
|
97 * counts per-priority level appended to it. |
|
98 * |
|
99 * @var string|null |
|
100 */ |
|
101 protected $_subjectPrependText; |
|
102 |
|
103 /** |
|
104 * MethodMap for Zend_Mail's headers |
|
105 * |
|
106 * @var array |
|
107 */ |
|
108 protected static $_methodMapHeaders = array( |
|
109 'from' => 'setFrom', |
|
110 'to' => 'addTo', |
|
111 'cc' => 'addCc', |
|
112 'bcc' => 'addBcc', |
|
113 ); |
|
114 |
|
115 /** |
|
116 * Class constructor. |
|
117 * |
|
118 * Constructs the mail writer; requires a Zend_Mail instance, and takes an |
|
119 * optional Zend_Layout instance. If Zend_Layout is being used, |
|
120 * $this->_layout->events will be set for use in the layout template. |
|
121 * |
|
122 * @param Zend_Mail $mail Mail instance |
|
123 * @param Zend_Layout $layout Layout instance; optional |
|
124 * @return void |
|
125 */ |
|
126 public function __construct(Zend_Mail $mail, Zend_Layout $layout = null) |
|
127 { |
|
128 $this->_mail = $mail; |
|
129 if (null !== $layout) { |
|
130 $this->setLayout($layout); |
|
131 } |
|
132 $this->_formatter = new Zend_Log_Formatter_Simple(); |
|
133 } |
|
134 |
|
135 /** |
|
136 * Create a new instance of Zend_Log_Writer_Mail |
|
137 * |
|
138 * @param array|Zend_Config $config |
|
139 * @return Zend_Log_Writer_Mail |
|
140 */ |
|
141 static public function factory($config) |
|
142 { |
|
143 $config = self::_parseConfig($config); |
|
144 $mail = self::_constructMailFromConfig($config); |
|
145 $writer = new self($mail); |
|
146 |
|
147 if (isset($config['layout']) || isset($config['layoutOptions'])) { |
|
148 $writer->setLayout($config); |
|
149 } |
|
150 if (isset($config['layoutFormatter'])) { |
|
151 $layoutFormatter = new $config['layoutFormatter']; |
|
152 $writer->setLayoutFormatter($layoutFormatter); |
|
153 } |
|
154 if (isset($config['subjectPrependText'])) { |
|
155 $writer->setSubjectPrependText($config['subjectPrependText']); |
|
156 } |
|
157 |
|
158 return $writer; |
|
159 } |
|
160 |
|
161 /** |
|
162 * Set the layout |
|
163 * |
|
164 * @param Zend_Layout|array $layout |
|
165 * @return Zend_Log_Writer_Mail |
|
166 * @throws Zend_Log_Exception |
|
167 */ |
|
168 public function setLayout($layout) |
|
169 { |
|
170 if (is_array($layout)) { |
|
171 $layout = $this->_constructLayoutFromConfig($layout); |
|
172 } |
|
173 |
|
174 if (!$layout instanceof Zend_Layout) { |
|
175 require_once 'Zend/Log/Exception.php'; |
|
176 throw new Zend_Log_Exception('Mail must be an instance of Zend_Layout or an array'); |
|
177 } |
|
178 $this->_layout = $layout; |
|
179 |
|
180 return $this; |
|
181 } |
|
182 |
|
183 /** |
|
184 * Construct a Zend_Mail instance based on a configuration array |
|
185 * |
|
186 * @param array $config |
|
187 * @return Zend_Mail |
|
188 */ |
|
189 protected static function _constructMailFromConfig(array $config) |
|
190 { |
|
191 $mailClass = 'Zend_Mail'; |
|
192 if (isset($config['mail'])) { |
|
193 $mailClass = $config['mail']; |
|
194 } |
|
195 |
|
196 if (!array_key_exists('charset', $config)) { |
|
197 $config['charset'] = null; |
|
198 } |
|
199 $mail = new $mailClass($config['charset']); |
|
200 if (!$mail instanceof Zend_Mail) { |
|
201 throw new Zend_Log_Exception($mail . 'must extend Zend_Mail'); |
|
202 } |
|
203 |
|
204 if (isset($config['subject'])) { |
|
205 $mail->setSubject($config['subject']); |
|
206 } |
|
207 |
|
208 $headerAddresses = array_intersect_key($config, self::$_methodMapHeaders); |
|
209 if (count($headerAddresses)) { |
|
210 foreach ($headerAddresses as $header => $address) { |
|
211 $method = self::$_methodMapHeaders[$header]; |
|
212 if (is_array($address) && isset($address['name']) |
|
213 && !is_numeric($address['name']) |
|
214 ) { |
|
215 $params = array( |
|
216 $address['email'], |
|
217 $address['name'] |
|
218 ); |
|
219 } else if (is_array($address) && isset($address['email'])) { |
|
220 $params = array($address['email']); |
|
221 } else { |
|
222 $params = array($address); |
|
223 } |
|
224 call_user_func_array(array($mail, $method), $params); |
|
225 } |
|
226 } |
|
227 |
|
228 return $mail; |
|
229 } |
|
230 |
|
231 /** |
|
232 * Construct a Zend_Layout instance based on a configuration array |
|
233 * |
|
234 * @param array $config |
|
235 * @return Zend_Layout |
|
236 */ |
|
237 protected function _constructLayoutFromConfig(array $config) |
|
238 { |
|
239 $config = array_merge(array( |
|
240 'layout' => 'Zend_Layout', |
|
241 'layoutOptions' => null |
|
242 ), $config); |
|
243 |
|
244 $layoutClass = $config['layout']; |
|
245 $layout = new $layoutClass($config['layoutOptions']); |
|
246 if (!$layout instanceof Zend_Layout) { |
|
247 throw new Zend_Log_Exception($layout . 'must extend Zend_Layout'); |
|
248 } |
|
249 |
|
250 return $layout; |
|
251 } |
|
252 |
|
253 /** |
|
254 * Places event line into array of lines to be used as message body. |
|
255 * |
|
256 * Handles the formatting of both plaintext entries, as well as those |
|
257 * rendered with Zend_Layout. |
|
258 * |
|
259 * @param array $event Event data |
|
260 * @return void |
|
261 */ |
|
262 protected function _write($event) |
|
263 { |
|
264 // Track the number of entries per priority level. |
|
265 if (!isset($this->_numEntriesPerPriority[$event['priorityName']])) { |
|
266 $this->_numEntriesPerPriority[$event['priorityName']] = 1; |
|
267 } else { |
|
268 $this->_numEntriesPerPriority[$event['priorityName']]++; |
|
269 } |
|
270 |
|
271 $formattedEvent = $this->_formatter->format($event); |
|
272 |
|
273 // All plaintext events are to use the standard formatter. |
|
274 $this->_eventsToMail[] = $formattedEvent; |
|
275 |
|
276 // If we have a Zend_Layout instance, use a specific formatter for the |
|
277 // layout if one exists. Otherwise, just use the event with its |
|
278 // default format. |
|
279 if ($this->_layout) { |
|
280 if ($this->_layoutFormatter) { |
|
281 $this->_layoutEventsToMail[] = |
|
282 $this->_layoutFormatter->format($event); |
|
283 } else { |
|
284 $this->_layoutEventsToMail[] = $formattedEvent; |
|
285 } |
|
286 } |
|
287 } |
|
288 |
|
289 /** |
|
290 * Gets instance of Zend_Log_Formatter_Instance used for formatting a |
|
291 * message using Zend_Layout, if applicable. |
|
292 * |
|
293 * @return Zend_Log_Formatter_Interface|null The formatter, or null. |
|
294 */ |
|
295 public function getLayoutFormatter() |
|
296 { |
|
297 return $this->_layoutFormatter; |
|
298 } |
|
299 |
|
300 /** |
|
301 * Sets a specific formatter for use with Zend_Layout events. |
|
302 * |
|
303 * Allows use of a second formatter on lines that will be rendered with |
|
304 * Zend_Layout. In the event that Zend_Layout is not being used, this |
|
305 * formatter cannot be set, so an exception will be thrown. |
|
306 * |
|
307 * @param Zend_Log_Formatter_Interface $formatter |
|
308 * @return Zend_Log_Writer_Mail |
|
309 * @throws Zend_Log_Exception |
|
310 */ |
|
311 public function setLayoutFormatter(Zend_Log_Formatter_Interface $formatter) |
|
312 { |
|
313 if (!$this->_layout) { |
|
314 throw new Zend_Log_Exception( |
|
315 'cannot set formatter for layout; ' . |
|
316 'a Zend_Layout instance is not in use'); |
|
317 } |
|
318 |
|
319 $this->_layoutFormatter = $formatter; |
|
320 return $this; |
|
321 } |
|
322 |
|
323 /** |
|
324 * Allows caller to have the mail subject dynamically set to contain the |
|
325 * entry counts per-priority level. |
|
326 * |
|
327 * Sets the text for use in the subject, with entry counts per-priority |
|
328 * level appended to the end. Since a Zend_Mail subject can only be set |
|
329 * once, this method cannot be used if the Zend_Mail object already has a |
|
330 * subject set. |
|
331 * |
|
332 * @param string $subject Subject prepend text. |
|
333 * @return Zend_Log_Writer_Mail |
|
334 */ |
|
335 public function setSubjectPrependText($subject) |
|
336 { |
|
337 if ($this->_mail->getSubject()) { |
|
338 throw new Zend_Log_Exception( |
|
339 'subject already set on mail; ' . |
|
340 'cannot set subject prepend text'); |
|
341 } |
|
342 |
|
343 $this->_subjectPrependText = (string) $subject; |
|
344 return $this; |
|
345 } |
|
346 |
|
347 /** |
|
348 * Sends mail to recipient(s) if log entries are present. Note that both |
|
349 * plaintext and HTML portions of email are handled here. |
|
350 * |
|
351 * @return void |
|
352 */ |
|
353 public function shutdown() |
|
354 { |
|
355 // If there are events to mail, use them as message body. Otherwise, |
|
356 // there is no mail to be sent. |
|
357 if (empty($this->_eventsToMail)) { |
|
358 return; |
|
359 } |
|
360 |
|
361 if ($this->_subjectPrependText !== null) { |
|
362 // Tack on the summary of entries per-priority to the subject |
|
363 // line and set it on the Zend_Mail object. |
|
364 $numEntries = $this->_getFormattedNumEntriesPerPriority(); |
|
365 $this->_mail->setSubject( |
|
366 "{$this->_subjectPrependText} ({$numEntries})"); |
|
367 } |
|
368 |
|
369 |
|
370 // Always provide events to mail as plaintext. |
|
371 $this->_mail->setBodyText(implode('', $this->_eventsToMail)); |
|
372 |
|
373 // If a Zend_Layout instance is being used, set its "events" |
|
374 // value to the lines formatted for use with the layout. |
|
375 if ($this->_layout) { |
|
376 // Set the required "messages" value for the layout. Here we |
|
377 // are assuming that the layout is for use with HTML. |
|
378 $this->_layout->events = |
|
379 implode('', $this->_layoutEventsToMail); |
|
380 |
|
381 // If an exception occurs during rendering, convert it to a notice |
|
382 // so we can avoid an exception thrown without a stack frame. |
|
383 try { |
|
384 $this->_mail->setBodyHtml($this->_layout->render()); |
|
385 } catch (Exception $e) { |
|
386 trigger_error( |
|
387 "exception occurred when rendering layout; " . |
|
388 "unable to set html body for message; " . |
|
389 "message = {$e->getMessage()}; " . |
|
390 "code = {$e->getCode()}; " . |
|
391 "exception class = " . get_class($e), |
|
392 E_USER_NOTICE); |
|
393 } |
|
394 } |
|
395 |
|
396 // Finally, send the mail. If an exception occurs, convert it into a |
|
397 // warning-level message so we can avoid an exception thrown without a |
|
398 // stack frame. |
|
399 try { |
|
400 $this->_mail->send(); |
|
401 } catch (Exception $e) { |
|
402 trigger_error( |
|
403 "unable to send log entries via email; " . |
|
404 "message = {$e->getMessage()}; " . |
|
405 "code = {$e->getCode()}; " . |
|
406 "exception class = " . get_class($e), |
|
407 E_USER_WARNING); |
|
408 } |
|
409 } |
|
410 |
|
411 /** |
|
412 * Gets a string of number of entries per-priority level that occurred, or |
|
413 * an emptry string if none occurred. |
|
414 * |
|
415 * @return string |
|
416 */ |
|
417 protected function _getFormattedNumEntriesPerPriority() |
|
418 { |
|
419 $strings = array(); |
|
420 |
|
421 foreach ($this->_numEntriesPerPriority as $priority => $numEntries) { |
|
422 $strings[] = "{$priority}={$numEntries}"; |
|
423 } |
|
424 |
|
425 return implode(', ', $strings); |
|
426 } |
|
427 } |