PageRenderTime 21ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Log/Writer/Mail.php

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