PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / diagnostics / DeveloperLogMailer.php

DeveloperLogMailer.php in 404 Solution trunk, at includes/diagnostics/DeveloperLogMailer.php

119 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 if (!defined('ABSPATH')) {
5 exit;
6 }
7
8 // allow-no-test-found: covered by tests/LoggingTest.php public emailLogFileToDeveloper fallback tests.
9
10 /**
11 * wp_mail fallback transport for developer error and heartbeat reports.
12 *
13 * Converts a FeedbackTransport payload into the legacy HTML email, attaches
14 * a debug-log zip when one can be built, dispatches through wp_mail(), and
15 * removes the temporary zip afterwards.
16 */
17 class ABJ_404_Solution_DeveloperLogMailer {
18
19 /** @var ABJ_404_Solution_ErrorEmailBodyFormatter */
20 private $formatter;
21 /** @var ABJ_404_Solution_DebugLogArchiveBuilder */
22 private $archiveBuilder;
23 /** @var callable */
24 private $debugLogger;
25 /** @var callable */
26 private $errorLogger;
27
28 /**
29 * @param ABJ_404_Solution_ErrorEmailBodyFormatter $formatter
30 * @param ABJ_404_Solution_DebugLogArchiveBuilder $archiveBuilder
31 * @param callable $debugLogger Receives debug-message strings.
32 * @param callable $errorLogger Receives error-message strings.
33 */
34 public function __construct(
35 ABJ_404_Solution_ErrorEmailBodyFormatter $formatter,
36 ABJ_404_Solution_DebugLogArchiveBuilder $archiveBuilder,
37 callable $debugLogger,
38 callable $errorLogger
39 ) {
40 $this->formatter = $formatter;
41 $this->archiveBuilder = $archiveBuilder;
42 $this->debugLogger = $debugLogger;
43 $this->errorLogger = $errorLogger;
44 }
45
46 /**
47 * Send the developer log email.
48 *
49 * @param array<string, mixed> $payload FeedbackTransport-built payload.
50 * @param string $debugFilePath
51 * @param string $oldDebugFilePath
52 * @param string $zipFilePath
53 * @param string $debugFilename
54 * @return bool True if wp_mail() reported success.
55 */
56 public function send(
57 array $payload,
58 string $debugFilePath,
59 string $oldDebugFilePath,
60 string $zipFilePath,
61 string $debugFilename
62 ): bool {
63 $previouslySentLine = isset($payload['previously_sent_line']) && is_scalar($payload['previously_sent_line'])
64 ? (int)$payload['previously_sent_line'] : 0;
65 call_user_func($this->debugLogger, "Creating zip file of error log file. " .
66 "Previously sent error line: " . $previouslySentLine);
67
68 $logFileZip = $this->archiveBuilder->build($zipFilePath, $debugFilePath, $oldDebugFilePath,
69 $this->diagnosticJournalPaths());
70 $subject = $this->formatter->buildSubject($payload);
71 $body = $this->formatter->buildBody($payload, $subject, $debugFilename);
72
73 $to = ABJ404_AUTHOR_EMAIL;
74 $headers = array('Content-Type: text/html; charset=UTF-8');
75 $adminEmail = get_option('admin_email');
76 $headers[] = 'From: ' . (is_scalar($adminEmail) ? (string)$adminEmail : '');
77
78 $attachments = array();
79 if ($logFileZip !== '' && file_exists($logFileZip)) {
80 $attachments[] = $logFileZip;
81 }
82
83 call_user_func($this->debugLogger, "Sending error log zip file as attachment.");
84 $result = wp_mail($to, $subject, $body, $headers, $attachments);
85
86 if ($logFileZip !== '' && file_exists($logFileZip)) {
87 ABJ_404_Solution_FileSystemService::safeUnlink($logFileZip);
88 }
89 if ((bool)$result) {
90 call_user_func($this->debugLogger, "Mail sent. Log zip file deleted.");
91 } else {
92 call_user_func($this->errorLogger,
93 "wp_mail() returned false while sending the developer error/heartbeat log email. " .
94 "Recipient: " . $to . ". Subject: " . $subject);
95 }
96 return (bool)$result;
97 }
98
99 /**
100 * Both AJAX diagnostic journals, whole, for the archive.
101 *
102 * The support payload can only carry a ranked, budgeted excerpt of these;
103 * the archive is the channel where a budget decision cannot lose evidence.
104 * Each journal names its own files, so no path string is duplicated here.
105 *
106 * @return array<int, string>
107 */
108 private function diagnosticJournalPaths(): array {
109 $paths = array();
110 if (class_exists('ABJ_404_Solution_CheckpointJournalReader')) {
111 $paths = array_merge($paths, ABJ_404_Solution_CheckpointJournalReader::supportArchivePaths());
112 }
113 if (class_exists('ABJ_404_Solution_AjaxTraceJournal')) {
114 $paths = array_merge($paths, ABJ_404_Solution_AjaxTraceJournal::supportArchivePaths());
115 }
116 return $paths;
117 }
118 }
119