PluginProbe
phpinfo() WP – Site Health, PHP Compatibility & Server Audit / 7.2.5
phpinfo() WP – Site Health, PHP Compatibility & Server Audit v7.2.5
7.2.7 7.2.6 7.2.5 7.2.4 7.2.3 7.2.0 7.2.1 7.2.2 7.1.0 7.0.3 7.0.4 7.0.5 trunk 6.0 7.0.0 7.0.1 7.0.2
phpinfo-wp / includes / class-mail-check.php

class-mail-check.php in phpinfo() WP – Site Health, PHP Compatibility & Server Audit 7.2.5, at includes/class-mail-check.php

250 lines 9.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined('ABSPATH') or die('Unauthorized Access');
3
4 class Phpinfo_WP_Mail_Check {
5
6 private static function _pro(): bool { return Phpinfo_WP_License::is_valid(); }
7
8 public static function audit(): array {
9 if (!self::_pro()) return [];
10
11 $from = get_option('admin_email', '');
12 $parts = explode('@', $from);
13 $domain = isset($parts[1]) ? strtolower($parts[1]) : '';
14
15 $spf = $domain ? self::_lookup_spf($domain) : ['present' => false];
16 $dmarc = $domain ? self::_lookup_dmarc($domain) : ['present' => false];
17 $mx = $domain ? self::_lookup_mx($domain) : [];
18
19 $smtp_plugin = self::_detect_smtp_plugin();
20
21 return [
22 'from_email' => $from,
23 'domain' => $domain,
24 'spf' => $spf,
25 'dmarc' => $dmarc,
26 'mx' => $mx,
27 'smtp_plugin' => $smtp_plugin,
28 'using_php_mail' => empty($smtp_plugin),
29 ];
30 }
31
32 private static function _lookup_spf(string $domain): array {
33 if (!function_exists('dns_get_record')) return ['present' => false, 'error' => 'dns_get_record unavailable'];
34 $records = @dns_get_record($domain, DNS_TXT);
35 if (!$records) return ['present' => false];
36 foreach ($records as $r) {
37 $txt = $r['txt'] ?? '';
38 if (stripos($txt, 'v=spf1') === 0) {
39 $strict = strpos($txt, '-all') !== false;
40 $soft = strpos($txt, '~all') !== false;
41 return [
42 'present' => true,
43 'value' => $txt,
44 'strict' => $strict,
45 'soft' => $soft,
46 'warning' => (!$strict && !$soft) ? 'SPF has no "all" mechanism — policy is undefined.' : null,
47 ];
48 }
49 }
50 return ['present' => false];
51 }
52
53 private static function _lookup_dmarc(string $domain): array {
54 if (!function_exists('dns_get_record')) return ['present' => false, 'error' => 'dns_get_record unavailable'];
55 $records = @dns_get_record('_dmarc.' . $domain, DNS_TXT);
56 if (!$records) return ['present' => false];
57 foreach ($records as $r) {
58 $txt = $r['txt'] ?? '';
59 if (stripos($txt, 'v=DMARC1') === 0) {
60 preg_match('/p\s*=\s*(\w+)/', $txt, $m);
61 $policy = strtolower($m[1] ?? 'none');
62 return [
63 'present' => true,
64 'value' => $txt,
65 'policy' => $policy,
66 'warning' => $policy === 'none' ? 'DMARC policy is "none" — no enforcement, monitoring only.' : null,
67 ];
68 }
69 }
70 return ['present' => false];
71 }
72
73 private static function _lookup_mx(string $domain): array {
74 if (!function_exists('dns_get_record')) return [];
75 $records = @dns_get_record($domain, DNS_MX);
76 if (!$records) return [];
77 usort($records, function ($a, $b) {
78 return ($a['pri'] ?? 99) <=> ($b['pri'] ?? 99);
79 });
80 $out = [];
81 foreach ($records as $r) {
82 $out[] = ['priority' => (int)($r['pri'] ?? 0), 'host' => $r['target'] ?? ''];
83 }
84 return $out;
85 }
86
87 private static function _detect_smtp_plugin(): ?string {
88 $known = [
89 'WP_Mail_SMTP' => 'WP Mail SMTP',
90 'WPMailSMTP\\Core' => 'WP Mail SMTP',
91 'Easy_WP_SMTP' => 'Easy WP SMTP',
92 'FluentMail\\App\\Application' => 'FluentSMTP',
93 'POST_SMTP_VERSION' => 'Post SMTP',
94 'SendGrid\\WPPlugin' => 'SendGrid',
95 'PostmanSMTPVersion' => 'Postman SMTP',
96 ];
97 foreach ($known as $sym => $name) {
98 if (class_exists($sym) || defined($sym)) return $name;
99 }
100 return null;
101 }
102
103 public static function discover_routing_emails(): array {
104 if (!self::_pro()) return [];
105
106 $emails = [];
107 $admin_email = get_option('admin_email');
108
109 // 1. WordPress Admin Email
110 if (is_email($admin_email)) {
111 $emails[] = [
112 'source' => 'WordPress Admin Email',
113 'email' => $admin_email,
114 ];
115 }
116
117 // 2. Contact Form 7
118 if (post_type_exists('wpcf7_contact_form')) {
119 $cf7_forms = get_posts(['post_type' => 'wpcf7_contact_form', 'numberposts' => -1]);
120 foreach ($cf7_forms as $form) {
121 $mail = get_post_meta($form->ID, '_mail', true);
122 if (is_array($mail) && !empty($mail['recipient'])) {
123 $recs = explode(',', $mail['recipient']);
124 foreach ($recs as $rec) {
125 $rec = trim($rec);
126 // Try to extract if it's "Name <email@example.com>"
127 if (preg_match('/<([^>]+)>/', $rec, $m)) {
128 $rec = $m[1];
129 }
130 if (is_email($rec)) {
131 $emails[] = [
132 'source' => 'Contact Form 7: ' . $form->post_title,
133 'email' => $rec,
134 ];
135 }
136 }
137 }
138 }
139 }
140
141 // 3. WPForms
142 if (post_type_exists('wpforms')) {
143 $wpf_forms = get_posts(['post_type' => 'wpforms', 'numberposts' => -1]);
144 foreach ($wpf_forms as $form) {
145 $data = json_decode($form->post_content, true);
146 if (isset($data['settings']['notifications'])) {
147 foreach ($data['settings']['notifications'] as $notif) {
148 if (!empty($notif['email'])) {
149 $recs = explode(',', $notif['email']);
150 foreach ($recs as $rec) {
151 $rec = trim($rec);
152 if ($rec === '{admin_email}') {
153 $rec = $admin_email;
154 }
155 if (is_email($rec)) {
156 $emails[] = [
157 'source' => 'WPForms: ' . $form->post_title,
158 'email' => $rec,
159 ];
160 }
161 }
162 }
163 }
164 }
165 }
166 }
167
168 // 4. Gravity Forms
169 if (class_exists('GFAPI')) {
170 $gf_forms = \GFAPI::get_forms();
171 if (is_array($gf_forms)) {
172 foreach ($gf_forms as $form) {
173 if (!empty($form['notifications'])) {
174 foreach ($form['notifications'] as $notif) {
175 if (!empty($notif['to'])) {
176 $recs = explode(',', $notif['to']);
177 foreach ($recs as $rec) {
178 $rec = trim($rec);
179 if ($rec === '{admin_email}') {
180 $rec = $admin_email;
181 }
182 if (is_email($rec)) {
183 $emails[] = [
184 'source' => 'Gravity Forms: ' . ($form['title'] ?? 'Form'),
185 'email' => $rec,
186 ];
187 }
188 }
189 }
190 }
191 }
192 }
193 }
194 }
195
196 // De-duplicate and check MX
197 $unique = [];
198 foreach ($emails as $e) {
199 $email_lower = strtolower($e['email']);
200 if (!isset($unique[$email_lower])) {
201 $parts = explode('@', $e['email']);
202 $domain = $parts[1] ?? '';
203 $mx = $domain ? self::_lookup_mx($domain) : [];
204 $e['mx_ok'] = !empty($mx);
205 $e['sources'] = [$e['source']];
206 $unique[$email_lower] = $e;
207 } else {
208 if (!in_array($e['source'], $unique[$email_lower]['sources'], true)) {
209 $unique[$email_lower]['sources'][] = $e['source'];
210 }
211 }
212 }
213
214 // Format the final source string
215 foreach ($unique as $email_lower => &$e) {
216 $e['source'] = implode(', ', $e['sources']);
217 unset($e['sources']);
218 }
219 unset($e);
220
221 return array_values($unique);
222 }
223
224 public static function send_test(string $to): array {
225 if (!self::_pro()) return ['ok' => false, 'error' => 'Pro license required.'];
226 if (!is_email($to)) return ['ok' => false, 'error' => 'Invalid recipient email.'];
227
228 $errors = [];
229 $listener = function($wp_error) use (&$errors) {
230 $errors[] = $wp_error->get_error_message();
231 };
232 add_action('wp_mail_failed', $listener);
233
234 $subject = '[phpinfo() WP] Mail deliverability test';
235 $body = "This is a test email sent at " . current_time('mysql', true) . " UTC\n"
236 . "From: " . get_option('admin_email') . "\n"
237 . "Site: " . get_site_url();
238 $ok = wp_mail($to, $subject, $body);
239
240 remove_action('wp_mail_failed', $listener);
241
242 return [
243 'ok' => $ok && !$errors,
244 'sent_to' => $to,
245 'errors' => $errors,
246 'method' => self::_detect_smtp_plugin() ?: 'PHP mail()',
247 ];
248 }
249 }
250