File indexing completed on 2024-12-22 05:36:50
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Log 0017 * @subpackage Writer 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** Zend_Log_Writer_Abstract */ 0024 // require_once 'Zend/Log/Writer/Abstract.php'; 0025 0026 /** Zend_Log_Exception */ 0027 // require_once 'Zend/Log/Exception.php'; 0028 0029 /** Zend_Log_Formatter_Simple*/ 0030 // require_once 'Zend/Log/Formatter/Simple.php'; 0031 0032 /** 0033 * Class used for writing log messages to email via Zend_Mail. 0034 * 0035 * Allows for emailing log messages at and above a certain level via a 0036 * Zend_Mail object. Note that this class only sends the email upon 0037 * completion, so any log entries accumulated are sent in a single email. 0038 * 0039 * @category Zend 0040 * @package Zend_Log 0041 * @subpackage Writer 0042 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0043 * @license http://framework.zend.com/license/new-bsd New BSD License 0044 * @version $Id$ 0045 */ 0046 class Zend_Log_Writer_Mail extends Zend_Log_Writer_Abstract 0047 { 0048 /** 0049 * Array of formatted events to include in message body. 0050 * 0051 * @var array 0052 */ 0053 protected $_eventsToMail = array(); 0054 0055 /** 0056 * Array of formatted lines for use in an HTML email body; these events 0057 * are formatted with an optional formatter if the caller is using 0058 * Zend_Layout. 0059 * 0060 * @var array 0061 */ 0062 protected $_layoutEventsToMail = array(); 0063 0064 /** 0065 * Zend_Mail instance to use 0066 * 0067 * @var Zend_Mail 0068 */ 0069 protected $_mail; 0070 0071 /** 0072 * Zend_Layout instance to use; optional. 0073 * 0074 * @var Zend_Layout 0075 */ 0076 protected $_layout; 0077 0078 /** 0079 * Optional formatter for use when rendering with Zend_Layout. 0080 * 0081 * @var Zend_Log_Formatter_Interface 0082 */ 0083 protected $_layoutFormatter; 0084 0085 /** 0086 * Array keeping track of the number of entries per priority level. 0087 * 0088 * @var array 0089 */ 0090 protected $_numEntriesPerPriority = array(); 0091 0092 /** 0093 * Subject prepend text. 0094 * 0095 * Can only be used of the Zend_Mail object has not already had its 0096 * subject line set. Using this will cause the subject to have the entry 0097 * counts per-priority level appended to it. 0098 * 0099 * @var string|null 0100 */ 0101 protected $_subjectPrependText; 0102 0103 /** 0104 * MethodMap for Zend_Mail's headers 0105 * 0106 * @var array 0107 */ 0108 protected static $_methodMapHeaders = array( 0109 'from' => 'setFrom', 0110 'to' => 'addTo', 0111 'cc' => 'addCc', 0112 'bcc' => 'addBcc', 0113 ); 0114 0115 /** 0116 * Class constructor. 0117 * 0118 * Constructs the mail writer; requires a Zend_Mail instance, and takes an 0119 * optional Zend_Layout instance. If Zend_Layout is being used, 0120 * $this->_layout->events will be set for use in the layout template. 0121 * 0122 * @param Zend_Mail $mail Mail instance 0123 * @param Zend_Layout $layout Layout instance; optional 0124 * @return void 0125 */ 0126 public function __construct(Zend_Mail $mail, Zend_Layout $layout = null) 0127 { 0128 $this->_mail = $mail; 0129 if (null !== $layout) { 0130 $this->setLayout($layout); 0131 } 0132 $this->_formatter = new Zend_Log_Formatter_Simple(); 0133 } 0134 0135 /** 0136 * Create a new instance of Zend_Log_Writer_Mail 0137 * 0138 * @param array|Zend_Config $config 0139 * @return Zend_Log_Writer_Mail 0140 */ 0141 static public function factory($config) 0142 { 0143 $config = self::_parseConfig($config); 0144 $mail = self::_constructMailFromConfig($config); 0145 $writer = new self($mail); 0146 0147 if (isset($config['layout']) || isset($config['layoutOptions'])) { 0148 $writer->setLayout($config); 0149 } 0150 if (isset($config['layoutFormatter'])) { 0151 $layoutFormatter = new $config['layoutFormatter']; 0152 $writer->setLayoutFormatter($layoutFormatter); 0153 } 0154 if (isset($config['subjectPrependText'])) { 0155 $writer->setSubjectPrependText($config['subjectPrependText']); 0156 } 0157 0158 return $writer; 0159 } 0160 0161 /** 0162 * Set the layout 0163 * 0164 * @param Zend_Layout|array $layout 0165 * @return Zend_Log_Writer_Mail 0166 * @throws Zend_Log_Exception 0167 */ 0168 public function setLayout($layout) 0169 { 0170 if (is_array($layout)) { 0171 $layout = $this->_constructLayoutFromConfig($layout); 0172 } 0173 0174 if (!$layout instanceof Zend_Layout) { 0175 // require_once 'Zend/Log/Exception.php'; 0176 throw new Zend_Log_Exception('Mail must be an instance of Zend_Layout or an array'); 0177 } 0178 $this->_layout = $layout; 0179 0180 return $this; 0181 } 0182 0183 /** 0184 * Construct a Zend_Mail instance based on a configuration array 0185 * 0186 * @param array $config 0187 * @return Zend_Mail 0188 * @throws Zend_Log_Exception 0189 */ 0190 protected static function _constructMailFromConfig(array $config) 0191 { 0192 $mailClass = 'Zend_Mail'; 0193 if (isset($config['mail'])) { 0194 $mailClass = $config['mail']; 0195 } 0196 0197 if (!array_key_exists('charset', $config)) { 0198 $config['charset'] = null; 0199 } 0200 $mail = new $mailClass($config['charset']); 0201 if (!$mail instanceof Zend_Mail) { 0202 throw new Zend_Log_Exception($mail . 'must extend Zend_Mail'); 0203 } 0204 0205 if (isset($config['subject'])) { 0206 $mail->setSubject($config['subject']); 0207 } 0208 0209 $headerAddresses = array_intersect_key($config, self::$_methodMapHeaders); 0210 if (count($headerAddresses)) { 0211 foreach ($headerAddresses as $header => $address) { 0212 $method = self::$_methodMapHeaders[$header]; 0213 if (is_array($address) && isset($address['name']) 0214 && !is_numeric($address['name']) 0215 ) { 0216 $params = array( 0217 $address['email'], 0218 $address['name'] 0219 ); 0220 } else if (is_array($address) && isset($address['email'])) { 0221 $params = array($address['email']); 0222 } else { 0223 $params = array($address); 0224 } 0225 call_user_func_array(array($mail, $method), $params); 0226 } 0227 } 0228 0229 return $mail; 0230 } 0231 0232 /** 0233 * Construct a Zend_Layout instance based on a configuration array 0234 * 0235 * @param array $config 0236 * @return Zend_Layout 0237 * @throws Zend_Log_Exception 0238 */ 0239 protected function _constructLayoutFromConfig(array $config) 0240 { 0241 $config = array_merge(array( 0242 'layout' => 'Zend_Layout', 0243 'layoutOptions' => null 0244 ), $config); 0245 0246 $layoutClass = $config['layout']; 0247 $layout = new $layoutClass($config['layoutOptions']); 0248 if (!$layout instanceof Zend_Layout) { 0249 throw new Zend_Log_Exception($layout . 'must extend Zend_Layout'); 0250 } 0251 0252 return $layout; 0253 } 0254 0255 /** 0256 * Places event line into array of lines to be used as message body. 0257 * 0258 * Handles the formatting of both plaintext entries, as well as those 0259 * rendered with Zend_Layout. 0260 * 0261 * @param array $event Event data 0262 * @return void 0263 */ 0264 protected function _write($event) 0265 { 0266 // Track the number of entries per priority level. 0267 if (!isset($this->_numEntriesPerPriority[$event['priorityName']])) { 0268 $this->_numEntriesPerPriority[$event['priorityName']] = 1; 0269 } else { 0270 $this->_numEntriesPerPriority[$event['priorityName']]++; 0271 } 0272 0273 $formattedEvent = $this->_formatter->format($event); 0274 0275 // All plaintext events are to use the standard formatter. 0276 $this->_eventsToMail[] = $formattedEvent; 0277 0278 // If we have a Zend_Layout instance, use a specific formatter for the 0279 // layout if one exists. Otherwise, just use the event with its 0280 // default format. 0281 if ($this->_layout) { 0282 if ($this->_layoutFormatter) { 0283 $this->_layoutEventsToMail[] = 0284 $this->_layoutFormatter->format($event); 0285 } else { 0286 $this->_layoutEventsToMail[] = $formattedEvent; 0287 } 0288 } 0289 } 0290 0291 /** 0292 * Gets instance of Zend_Log_Formatter_Instance used for formatting a 0293 * message using Zend_Layout, if applicable. 0294 * 0295 * @return Zend_Log_Formatter_Interface|null The formatter, or null. 0296 */ 0297 public function getLayoutFormatter() 0298 { 0299 return $this->_layoutFormatter; 0300 } 0301 0302 /** 0303 * Sets a specific formatter for use with Zend_Layout events. 0304 * 0305 * Allows use of a second formatter on lines that will be rendered with 0306 * Zend_Layout. In the event that Zend_Layout is not being used, this 0307 * formatter cannot be set, so an exception will be thrown. 0308 * 0309 * @param Zend_Log_Formatter_Interface $formatter 0310 * @return Zend_Log_Writer_Mail 0311 * @throws Zend_Log_Exception 0312 */ 0313 public function setLayoutFormatter(Zend_Log_Formatter_Interface $formatter) 0314 { 0315 if (!$this->_layout) { 0316 throw new Zend_Log_Exception( 0317 'cannot set formatter for layout; ' . 0318 'a Zend_Layout instance is not in use'); 0319 } 0320 0321 $this->_layoutFormatter = $formatter; 0322 return $this; 0323 } 0324 0325 /** 0326 * Allows caller to have the mail subject dynamically set to contain the 0327 * entry counts per-priority level. 0328 * 0329 * Sets the text for use in the subject, with entry counts per-priority 0330 * level appended to the end. Since a Zend_Mail subject can only be set 0331 * once, this method cannot be used if the Zend_Mail object already has a 0332 * subject set. 0333 * 0334 * @param string $subject Subject prepend text. 0335 * @return Zend_Log_Writer_Mail 0336 * @throws Zend_Log_Exception 0337 */ 0338 public function setSubjectPrependText($subject) 0339 { 0340 if ($this->_mail->getSubject()) { 0341 throw new Zend_Log_Exception( 0342 'subject already set on mail; ' . 0343 'cannot set subject prepend text'); 0344 } 0345 0346 $this->_subjectPrependText = (string) $subject; 0347 return $this; 0348 } 0349 0350 /** 0351 * Sends mail to recipient(s) if log entries are present. Note that both 0352 * plaintext and HTML portions of email are handled here. 0353 * 0354 * @return void 0355 */ 0356 public function shutdown() 0357 { 0358 // If there are events to mail, use them as message body. Otherwise, 0359 // there is no mail to be sent. 0360 if (empty($this->_eventsToMail)) { 0361 return; 0362 } 0363 0364 if ($this->_subjectPrependText !== null) { 0365 // Tack on the summary of entries per-priority to the subject 0366 // line and set it on the Zend_Mail object. 0367 $numEntries = $this->_getFormattedNumEntriesPerPriority(); 0368 $this->_mail->setSubject( 0369 "{$this->_subjectPrependText} ({$numEntries})"); 0370 } 0371 0372 0373 // Always provide events to mail as plaintext. 0374 $this->_mail->setBodyText(implode('', $this->_eventsToMail)); 0375 0376 // If a Zend_Layout instance is being used, set its "events" 0377 // value to the lines formatted for use with the layout. 0378 if ($this->_layout) { 0379 // Set the required "messages" value for the layout. Here we 0380 // are assuming that the layout is for use with HTML. 0381 $this->_layout->events = 0382 implode('', $this->_layoutEventsToMail); 0383 0384 // If an exception occurs during rendering, convert it to a notice 0385 // so we can avoid an exception thrown without a stack frame. 0386 try { 0387 $this->_mail->setBodyHtml($this->_layout->render()); 0388 } catch (Exception $e) { 0389 trigger_error( 0390 "exception occurred when rendering layout; " . 0391 "unable to set html body for message; " . 0392 "message = {$e->getMessage()}; " . 0393 "code = {$e->getCode()}; " . 0394 "exception class = " . get_class($e), 0395 E_USER_NOTICE); 0396 } 0397 } 0398 0399 // Finally, send the mail. If an exception occurs, convert it into a 0400 // warning-level message so we can avoid an exception thrown without a 0401 // stack frame. 0402 try { 0403 $this->_mail->send(); 0404 } catch (Exception $e) { 0405 trigger_error( 0406 "unable to send log entries via email; " . 0407 "message = {$e->getMessage()}; " . 0408 "code = {$e->getCode()}; " . 0409 "exception class = " . get_class($e), 0410 E_USER_WARNING); 0411 } 0412 } 0413 0414 /** 0415 * Gets a string of number of entries per-priority level that occurred, or 0416 * an emptry string if none occurred. 0417 * 0418 * @return string 0419 */ 0420 protected function _getFormattedNumEntriesPerPriority() 0421 { 0422 $strings = array(); 0423 0424 foreach ($this->_numEntriesPerPriority as $priority => $numEntries) { 0425 $strings[] = "{$priority}={$numEntries}"; 0426 } 0427 0428 return implode(', ', $strings); 0429 } 0430 }