web/lib/Zend/Log/Writer/Mail.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     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 }