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 / views / htaccess.php

htaccess.php in phpinfo() WP – Site Health, PHP Compatibility & Server Audit 7.2.5, at views/htaccess.php

963 lines 46.2 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 if (!function_exists('get_home_path')) {
5 require_once ABSPATH . 'wp-admin/includes/file.php';
6 }
7 $root_dir = get_home_path();
8 $content_dir = WP_CONTENT_DIR;
9 $log_dir = "$content_dir/logs/phpinfo-WP";
10 $log_file = "$log_dir/log.txt";
11
12 if (!file_exists($log_dir)) wp_mkdir_p($log_dir);
13 if (!file_exists($log_file)) file_put_contents($log_file, '');
14
15 global $current_user;
16 $user = $current_user->user_login;
17 $notice = '';
18 $notice_type = 'success';
19 $writable = is_writable($root_dir);
20
21 // Detect which method applies to this server
22 $sapi = php_sapi_name();
23 $server_software = strtolower($_SERVER['SERVER_SOFTWARE'] ?? '');
24 $is_litespeed = strpos($server_software, 'litespeed') !== false;
25 $is_nginx = strpos($server_software, 'nginx') !== false;
26
27 // apache2handler = Apache + mod_php → .htaccess php_value works
28 // Everything else (fpm-fcgi, litespeed, cgi, etc.) → .user.ini
29 $mode = ($sapi === 'apache2handler' && !$is_litespeed) ? 'htaccess' : 'userini';
30
31 $target_file = $root_dir . ($mode === 'htaccess' ? '.htaccess' : '.user.ini');
32 $cache_file = $root_dir . ($mode === 'htaccess' ? 'htaccess-phpinfo.txt' : 'userini-phpinfo.txt');
33 $user_ini_ttl = (int) ini_get('user_ini.cache_ttl') ?: 300;
34
35 // Determine Server Type for Snippets
36 $snippet_server = $is_nginx ? 'nginx' : 'apache'; // LiteSpeed uses Apache syntax (.htaccess)
37
38 $snippets = [
39 'gzip' => [
40 'title' => 'Aggressive GZIP / Brotli Compression',
41 'desc' => 'Compresses HTML, CSS, JS, and JSON before sending it to the browser. Massively reduces page size and improves TTFB.',
42 'apache'=> "<IfModule mod_deflate.c>\n AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json\n</IfModule>",
43 'nginx' => "gzip on;\ngzip_comp_level 5;\ngzip_min_length 256;\ngzip_proxied any;\ngzip_vary on;\ngzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;"
44 ],
45 'browser_cache' => [
46 'title' => 'Browser Caching (Expires Headers)',
47 'desc' => 'Instructs browsers to save static assets (images, fonts, css) locally for 1 year, drastically speeding up repeat visits.',
48 'apache'=> "<IfModule mod_expires.c>\n ExpiresActive On\n ExpiresByType image/jpg \"access 1 year\"\n ExpiresByType image/jpeg \"access 1 year\"\n ExpiresByType image/gif \"access 1 year\"\n ExpiresByType image/png \"access 1 year\"\n ExpiresByType image/webp \"access 1 year\"\n ExpiresByType text/css \"access 1 month\"\n ExpiresByType application/javascript \"access 1 month\"\n ExpiresByType font/woff2 \"access 1 year\"\n ExpiresDefault \"access 1 month\"\n</IfModule>",
49 'nginx' => "location ~* \\.(jpg|jpeg|gif|png|webp|ico|css|js|woff2|woff|ttf)$ {\n expires 365d;\n add_header Cache-Control \"public, no-transform\";\n}"
50 ],
51 'security' => [
52 'title' => 'Security Headers (Basic)',
53 'desc' => 'Prevents clickjacking (X-Frame-Options) and MIME-type sniffing (X-Content-Type-Options).',
54 'apache'=> "<IfModule mod_headers.c>\n Header always set X-Frame-Options \"SAMEORIGIN\"\n Header always set X-Content-Type-Options \"nosniff\"\n Header set X-XSS-Protection \"1; mode=block\"\n</IfModule>",
55 'nginx' => "add_header X-Frame-Options \"SAMEORIGIN\" always;\nadd_header X-Content-Type-Options \"nosniff\" always;\nadd_header X-XSS-Protection \"1; mode=block\" always;"
56 ],
57 'bots' => [
58 'title' => 'Block Bad Bots (Basic)',
59 'desc' => 'Blocks common aggressive scrapers and vulnerability scanners to save server CPU.',
60 'apache'=> "<IfModule mod_rewrite.c>\n RewriteEngine On\n RewriteCond %{HTTP_USER_AGENT} (SemrushBot|AhrefsBot|DotBot|MJ12bot) [NC]\n RewriteRule .* - [F,L]\n</IfModule>",
61 'nginx' => "if (\$http_user_agent ~* (SemrushBot|AhrefsBot|DotBot|MJ12bot)) {\n return 403;\n}"
62 ]
63 ];
64
65 if ($writable && isset($_POST['phpinfo_nonce']) && wp_verify_nonce($_POST['phpinfo_nonce'], 'phpinfo_nonce')) {
66
67 if (isset($_POST['append_snippet'])) {
68 $snippet_id = sanitize_text_field($_POST['append_snippet']);
69 if (isset($snippets[$snippet_id]) && $snippet_server === 'apache') {
70 if (!Phpinfo_WP_License::is_valid()) {
71 $notice = 'Snippet injection requires phpinfo() WP Pro.';
72 $notice_type = 'error';
73 } else {
74 $code = $snippets[$snippet_id]['apache'];
75 $current = file_exists("$root_dir.htaccess") ? file_get_contents("$root_dir.htaccess") : '';
76 $block_name = strtoupper($snippet_id);
77 // Remove existing block if present so we don't duplicate
78 $current = preg_replace("/\n?# BEGIN phpinfo-wp-{$block_name}.*?# END phpinfo-wp-{$block_name}\n?/s", "\n", $current);
79 $new = rtrim($current) . "\n\n# BEGIN phpinfo-wp-{$block_name}\n" . $code . "\n# END phpinfo-wp-{$block_name}\n";
80 file_put_contents("$root_dir.htaccess", $new);
81 $notice = "Successfully appended <strong>{$snippets[$snippet_id]['title']}</strong> to .htaccess.<br /><span style='display:inline-block; margin-top: 6px; font-size:12.5px; opacity:0.9;'>⚡ <strong>Note:</strong> You must clear your cache (plugin cache, CDN, and browser cache) to see the results!</span>";
82 file_put_contents($log_file, ".htaccess snippet {$snippet_id} added on " . current_time('mysql') . " by {$user}<br />", FILE_APPEND);
83 }
84 }
85
86 } elseif (isset($_POST['rollback_snippet'])) {
87 $snippet_id = sanitize_text_field($_POST['rollback_snippet']);
88 if (isset($snippets[$snippet_id]) && $snippet_server === 'apache') {
89 if (!Phpinfo_WP_License::is_valid()) {
90 $notice = 'Snippet rollback requires phpinfo() WP Pro.';
91 $notice_type = 'error';
92 } else {
93 $block_name = strtoupper($snippet_id);
94 $current = file_exists("$root_dir.htaccess") ? file_get_contents("$root_dir.htaccess") : '';
95 $new = preg_replace("/\n?# BEGIN phpinfo-wp-{$block_name}.*?# END phpinfo-wp-{$block_name}\n?/s", "\n", $current);
96 file_put_contents("$root_dir.htaccess", $new);
97 $notice = "Rolled back <strong>{$snippets[$snippet_id]['title']}</strong> — block removed from .htaccess.";
98 file_put_contents($log_file, ".htaccess snippet {$snippet_id} rolled back on " . current_time('mysql') . " by {$user}<br />", FILE_APPEND);
99 }
100 }
101
102 } elseif (isset($_POST['inject_all_snippets'])) {
103 if (!Phpinfo_WP_License::is_valid()) {
104 $notice = 'Snippet injection requires phpinfo() WP Pro.';
105 $notice_type = 'error';
106 } elseif ($snippet_server !== 'apache') {
107 $notice = 'Bulk injection is only available on Apache/LiteSpeed servers.';
108 $notice_type = 'error';
109 } else {
110 $current = file_exists("$root_dir.htaccess") ? file_get_contents("$root_dir.htaccess") : '';
111 foreach ($snippets as $sid => $snip) {
112 $block_name = strtoupper($sid);
113 $current = preg_replace("/\n?# BEGIN phpinfo-wp-{$block_name}.*?# END phpinfo-wp-{$block_name}\n?/s", "\n", $current);
114 $current = rtrim($current) . "\n\n# BEGIN phpinfo-wp-{$block_name}\n" . $snip['apache'] . "\n# END phpinfo-wp-{$block_name}\n";
115 }
116 file_put_contents("$root_dir.htaccess", $current);
117 $notice = "All 4 snippets injected into .htaccess.<br /><span style='display:inline-block; margin-top: 6px; font-size:12.5px; opacity:0.9;'>⚡ <strong>Note:</strong> Clear your cache (plugin, CDN, browser) to see the results!</span>";
118 file_put_contents($log_file, "All .htaccess snippets injected on " . current_time('mysql') . " by {$user}<br />", FILE_APPEND);
119 }
120
121 } elseif (isset($_POST['rollback_all_snippets'])) {
122 if (!Phpinfo_WP_License::is_valid()) {
123 $notice = 'Snippet rollback requires phpinfo() WP Pro.';
124 $notice_type = 'error';
125 } else {
126 $current = file_exists("$root_dir.htaccess") ? file_get_contents("$root_dir.htaccess") : '';
127 foreach ($snippets as $sid => $snip) {
128 $block_name = strtoupper($sid);
129 $current = preg_replace("/\n?# BEGIN phpinfo-wp-{$block_name}.*?# END phpinfo-wp-{$block_name}\n?/s", "\n", $current);
130 }
131 file_put_contents("$root_dir.htaccess", $current);
132 $notice = 'All 4 snippets have been rolled back and removed from .htaccess.';
133 file_put_contents($log_file, "All .htaccess snippets rolled back on " . current_time('mysql') . " by {$user}<br />", FILE_APPEND);
134 }
135 } elseif ($mode === 'htaccess') {
136
137 if (isset($_POST['backup'])) {
138 file_put_contents("$root_dir.htaccess.bak", '#BACKED UP by phpinfo() WP' . PHP_EOL . file_get_contents("$root_dir.htaccess"));
139 $notice = 'Backup created: <code>.htaccess.bak</code>';
140 file_put_contents($log_file, ".htaccess backed up on " . current_time('mysql') . " by {$user}<br />", FILE_APPEND);
141
142 } elseif (isset($_POST['restore'])) {
143 if (!file_exists("$root_dir.htaccess.bak")) {
144 $notice = 'No backup file found. Take a backup first.';
145 $notice_type = 'error';
146 } else {
147 file_put_contents("$root_dir.htaccess", file_get_contents("$root_dir.htaccess.bak"));
148 $notice = '.htaccess restored from backup.';
149 file_put_contents($log_file, ".htaccess restored on " . current_time('mysql') . " by {$user}<br />", FILE_APPEND);
150 }
151
152 } elseif (isset($_POST['save'])) {
153 $custom_raw = $_POST['htaccess'] ?? '';
154 $php_lines = '';
155 foreach (explode("\n", $custom_raw) as $line) {
156 $line = trim($line);
157 if ($line !== '' && strncmp($line, '#', strlen('#')) !== 0) {
158 $php_lines .= 'php_value ' . $line . "\n";
159 }
160 }
161
162 $current = file_exists("$root_dir.htaccess") ? file_get_contents("$root_dir.htaccess") : '';
163 $base = preg_replace('/\n?# BEGIN phpinfo-wp\n.*?# END phpinfo-wp\n?/s', '', $current); // Fixed regex
164 // If the old regex left it without END, let's just append
165 $base = str_replace('# BEGIN phpinfo-wp', '', $base);
166 $new = rtrim($base) . "\n\n# BEGIN phpinfo-wp\n" . $php_lines . "# END phpinfo-wp\n";
167
168 file_put_contents("$root_dir.htaccess", $new);
169
170 $test = wp_remote_get(get_site_url(), ['timeout' => 8, 'sslverify' => false]);
171 if (!is_wp_error($test) && wp_remote_retrieve_response_code($test) === 500) {
172 file_put_contents("$root_dir.htaccess", $current);
173 $notice = 'Save aborted — site returned HTTP 500. Original .htaccess restored automatically.';
174 $notice_type = 'error';
175 } else {
176 file_put_contents($cache_file, $custom_raw);
177 $notice = '.htaccess saved successfully.';
178 file_put_contents($log_file, ".htaccess edited on " . current_time('mysql') . " by {$user}<br />", FILE_APPEND);
179 }
180 }
181
182 } else { // .user.ini mode
183
184 if (isset($_POST['save'])) {
185 $custom_raw = $_POST['htaccess'] ?? '';
186 $ini_lines = '';
187 foreach (explode("\n", $custom_raw) as $line) {
188 $line = trim($line);
189 if ($line === '' || strncmp($line, ';', strlen(';')) === 0) continue;
190 if (strpos($line, '=') !== false) {
191 $ini_lines .= $line . "\n";
192 } else {
193 [$k, $v] = array_pad(explode(' ', $line, 2), 2, '');
194 $ini_lines .= trim($k) . ' = ' . trim($v) . "\n";
195 }
196 }
197
198 $current = file_exists($target_file) ? file_get_contents($target_file) : '';
199 $base = preg_replace('/\n?; BEGIN phpinfo-wp.*?; END phpinfo-wp\n?/s', '', $current);
200 $new = rtrim($base) . "\n\n; BEGIN phpinfo-wp\n" . $ini_lines . "; END phpinfo-wp\n";
201
202 file_put_contents($target_file, $new);
203 file_put_contents($cache_file, $custom_raw);
204 $notice = ".user.ini saved. Changes take effect within {$user_ini_ttl} seconds (PHP-FPM cache TTL).";
205 file_put_contents($log_file, ".user.ini edited on " . current_time('mysql') . " by {$user}<br />", FILE_APPEND);
206 }
207 }
208 }
209
210 $existing = file_exists($cache_file) ? file_get_contents($cache_file) : '';
211 $preview = file_exists($target_file) ? file_get_contents($target_file) : '';
212
213 Phpinfo_wp::thankyou();
214 ?>
215
216 <div class="phpinfowp-info-page phpinfowp-htaccess-page">
217
218 <?php
219 // Mode banner properties
220 $mode_label = $mode === 'htaccess' ? 'Apache + mod_php' : 'PHP-FPM / Nginx / LiteSpeed';
221 $mode_color = $mode === 'htaccess' ? '#ef4444' : '#10b981';
222 $mode_bg = $mode === 'htaccess' ? '#fef2f2' : '#ecfdf5';
223 $mode_border = $mode === 'htaccess' ? '#fee2e2' : '#d1fae5';
224 $mode_icon_bg = $mode === 'htaccess' ? '#fecaca' : '#a7f3d0';
225 $mode_file = $mode === 'htaccess' ? '.htaccess' : '.user.ini';
226 ?>
227
228 <style>
229 /* Environment Cards Grid */
230 .phpinfowp-env-grid {
231 display: grid;
232 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
233 gap: 14px;
234 margin-bottom: 24px;
235 }
236 .phpinfowp-env-card {
237 background: #fff;
238 border: 1px solid #dcdcde;
239 border-radius: 6px;
240 padding: 16px 18px;
241 box-shadow: none;
242 display: flex;
243 align-items: flex-start;
244 gap: 14px;
245 transition: all 0.2s ease;
246 }
247 .phpinfowp-env-card:hover {
248 border-color: #ccd0d4;
249 }
250 .phpinfowp-env-icon-box {
251 width: 40px;
252 height: 40px;
253 border-radius: 6px;
254 display: flex;
255 align-items: center;
256 justify-content: center;
257 flex-shrink: 0;
258 }
259 .phpinfowp-env-details {
260 flex: 1;
261 min-width: 0;
262 }
263 .phpinfowp-env-label {
264 font-size: 11px;
265 font-weight: 600;
266 text-transform: uppercase;
267 letter-spacing: 0.5px;
268 color: #666;
269 margin-bottom: 4px;
270 }
271 .phpinfowp-env-value {
272 font-size: 14px;
273 font-weight: 700;
274 color: #1d2327;
275 display: flex;
276 align-items: center;
277 gap: 8px;
278 margin-bottom: 6px;
279 }
280 .phpinfowp-env-sub {
281 font-size: 12px;
282 color: #666;
283 line-height: 1.4;
284 }
285
286 /* Active Status badge with pulsing dot */
287 @keyframes phpinfowp-pulse {
288 0% { transform: scale(0.95); opacity: 0.5; }
289 50% { transform: scale(1.15); opacity: 1; }
290 100% { transform: scale(0.95); opacity: 0.5; }
291 }
292 .phpinfowp-pulse-dot {
293 width: 6px;
294 height: 6px;
295 border-radius: 50%;
296 background: #10b981;
297 display: inline-block;
298 animation: phpinfowp-pulse 2s infinite ease-in-out;
299 }
300
301 /* Mock IDE / Terminal Window Wrapper */
302 .phpinfowp-ide-window {
303 background: #1e1e2e;
304 border-radius: 6px;
305 border: 1px solid #313244;
306 box-shadow: 0 4px 12px rgba(0,0,0,0.1);
307 overflow: hidden;
308 margin-bottom: 16px;
309 }
310 .phpinfowp-ide-header {
311 background: #181825;
312 padding: 10px 16px;
313 display: flex;
314 align-items: center;
315 justify-content: space-between;
316 border-bottom: 1px solid #313244;
317 }
318 .phpinfowp-ide-footer {
319 background: #181825;
320 padding: 12px 16px;
321 border-top: 1px solid #313244;
322 font-size: 12px;
323 color: #a6adc8 !important;
324 line-height: 1.5;
325 }
326 .phpinfowp-ide-footer strong {
327 color: #cdd6f4 !important;
328 }
329 .phpinfowp-ide-footer code {
330 background: #313244 !important;
331 color: #cdd6f4 !important;
332 border: 1px solid #45475a !important;
333 padding: 2px 4px !important;
334 font-size: 11px !important;
335 }
336 .phpinfowp-ide-dots {
337 display: flex;
338 gap: 6px;
339 align-items: center;
340 }
341 .phpinfowp-ide-dot {
342 width: 11px;
343 height: 11px;
344 border-radius: 50%;
345 display: inline-block;
346 }
347 .phpinfowp-ide-dot.red { background: #ff5f56; }
348 .phpinfowp-ide-dot.yellow { background: #ffbd2e; }
349 .phpinfowp-ide-dot.green { background: #27c93f; }
350
351 .phpinfowp-ide-tabs {
352 display: flex;
353 margin-bottom: -13px;
354 margin-left: 16px;
355 }
356 .phpinfowp-ide-tab {
357 background: #1e1e2e;
358 color: #cdd6f4;
359 padding: 6px 14px;
360 border-radius: 6px 6px 0 0;
361 font-size: 12px;
362 font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
363 display: flex;
364 align-items: center;
365 gap: 6px;
366 border: 1px solid #313244;
367 border-bottom: none;
368 }
369 .phpinfowp-ide-body {
370 position: relative;
371 }
372
373 /* IDE Editor Textarea */
374 .phpinfowp-ide-textarea {
375 width: 100%;
376 min-height: 280px;
377 background: #1e1e2e !important;
378 color: #cdd6f4 !important;
379 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Fira Code", monospace !important;
380 font-size: 13px !important;
381 line-height: 1.6 !important;
382 padding: 16px !important;
383 border: none !important;
384 outline: none !important;
385 box-shadow: none !important;
386 resize: vertical;
387 display: block;
388 box-sizing: border-box;
389 }
390 .phpinfowp-ide-textarea::placeholder {
391 color: #585b70;
392 font-style: italic;
393 }
394 .phpinfowp-ide-textarea:focus {
395 outline: none !important;
396 box-shadow: none !important;
397 }
398
399 /* IDE Read-only Preview */
400 .phpinfowp-ide-preview {
401 width: 100%;
402 height: 280px;
403 background: #1e1e2e;
404 color: #cdd6f4;
405 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Fira Code", monospace;
406 font-size: 12px;
407 line-height: 1.6;
408 padding: 16px;
409 margin: 0;
410 overflow-y: auto;
411 overflow-x: auto;
412 white-space: pre-wrap;
413 word-break: break-all;
414 box-sizing: border-box;
415 }
416
417 /* custom scrollbars */
418 .phpinfowp-ide-textarea::-webkit-scrollbar,
419 .phpinfowp-ide-preview::-webkit-scrollbar {
420 width: 8px;
421 height: 8px;
422 }
423 .phpinfowp-ide-textarea::-webkit-scrollbar-track,
424 .phpinfowp-ide-preview::-webkit-scrollbar-track {
425 background: #181825;
426 }
427 .phpinfowp-ide-textarea::-webkit-scrollbar-thumb,
428 .phpinfowp-ide-preview::-webkit-scrollbar-thumb {
429 background: #313244;
430 border-radius: 4px;
431 }
432 .phpinfowp-ide-textarea::-webkit-scrollbar-thumb:hover,
433 .phpinfowp-ide-preview::-webkit-scrollbar-thumb:hover {
434 background: #45475a;
435 }
436
437 /* Page columns grid layout */
438 .phpinfowp-htaccess-layout {
439 display: grid;
440 grid-template-columns: minmax(0, 7fr) minmax(0, 3fr);
441 gap: 20px;
442 align-items: flex-start;
443 }
444 @media (max-width: 960px) {
445 .phpinfowp-htaccess-layout {
446 grid-template-columns: 1fr;
447 }
448 }
449
450
451 .phpinfowp-section-desc {
452 font-size: 13px;
453 color: #666;
454 line-height: 1.5;
455 margin: 0 0 14px 0;
456 }
457
458 /* Action Buttons bar */
459 .phpinfowp-action-row {
460 display: flex;
461 justify-content: space-between;
462 align-items: center;
463 margin-top: 14px;
464 flex-wrap: wrap;
465 gap: 10px;
466 }
467 .phpinfowp-btn {
468 display: inline-flex;
469 align-items: center;
470 justify-content: center;
471 gap: 6px;
472 padding: 8px 16px;
473 border-radius: 6px;
474 font-size: 13px;
475 font-weight: 600;
476 cursor: pointer;
477 transition: all 0.15s ease;
478 border: 1px solid transparent;
479 text-decoration: none;
480 line-height: 1.4;
481 }
482 .phpinfowp-btn-primary {
483 background: #777BB3;
484 color: #fff;
485 border-color: #696da6;
486 }
487 .phpinfowp-btn-primary:hover {
488 background: #696da6;
489 color: #fff;
490 }
491 .phpinfowp-btn-secondary {
492 background: #fff;
493 color: #334155;
494 border-color: #cbd5e1;
495 }
496 .phpinfowp-btn-secondary:hover {
497 background: #f8fafc;
498 border-color: #94a3b8;
499 color: #0f172a;
500 }
501 .phpinfowp-btn-danger {
502 background: #fff;
503 color: #ef4444;
504 border-color: #fca5a5;
505 }
506 .phpinfowp-btn-danger:hover {
507 background: #fef2f2;
508 border-color: #ef4444;
509 color: #dc2626;
510 }
511
512 /* Snippet Library cards */
513 .phpinfowp-snippet-card {
514 background: #fff;
515 border: 1px solid #dcdcde;
516 border-radius: 6px;
517 padding: 16px 18px;
518 margin-bottom: 14px;
519 box-shadow: none;
520 transition: all 0.2s ease;
521 }
522 .phpinfowp-snippet-card:hover {
523 border-color: #ccd0d4;
524 }
525 .phpinfowp-snippet-title-row {
526 display: flex;
527 align-items: center;
528 justify-content: space-between;
529 margin-bottom: 8px;
530 flex-wrap: wrap;
531 gap: 8px;
532 }
533 .phpinfowp-badge {
534 display: inline-flex;
535 align-items: center;
536 font-size: 10px;
537 font-weight: 700;
538 padding: 3px 8px;
539 border-radius: 20px;
540 text-transform: uppercase;
541 letter-spacing: 0.5px;
542 }
543 .phpinfowp-badge-pro {
544 background: #e0e7ff;
545 color: #4f46e5;
546 }
547 .phpinfowp-badge-neutral {
548 background: #f1f5f9;
549 color: #475569;
550 }
551 .phpinfowp-badge-success {
552 background: #d1fae5;
553 color: #059669;
554 }
555
556 /* Inline Notification Boxes */
557 .phpinfowp-custom-alert {
558 margin-bottom: 20px;
559 padding: 12px 16px;
560 border-radius: 6px;
561 display: flex;
562 align-items: flex-start;
563 gap: 12px;
564 }
565 .phpinfowp-custom-alert p {
566 margin: 0;
567 font-size: 13px;
568 line-height: 1.5;
569 }
570 </style>
571
572 <!-- Title Header -->
573 <div class="phpinfowp-page-header">
574 <div>
575 <h1><?php _e('PHP Config Editor', 'phpinfo-wp'); ?></h1>
576 <p class="phpinfowp-page-subtitle"><?php _e('Configure php.ini directives and web server rules safely from your WordPress dashboard.', 'phpinfo-wp'); ?></p>
577 </div>
578 </div>
579
580 <!-- Detected Environment Status Cards -->
581 <div class="phpinfowp-env-grid">
582 <!-- Environment Card -->
583 <div class="phpinfowp-env-card">
584 <div class="phpinfowp-env-icon-box" style="background: <?php echo $mode_icon_bg; ?>; color: <?php echo $mode_color; ?>;">
585 <span class="dashicons <?php echo $mode === 'htaccess' ? 'dashicons-admin-generic' : 'dashicons-networking'; ?>" style="font-size:20px; width:20px; height:20px;"></span>
586 </div>
587 <div class="phpinfowp-env-details">
588 <div class="phpinfowp-env-label"><?php _e('Detected Server', 'phpinfo-wp'); ?></div>
589 <div class="phpinfowp-env-value">
590 <?php echo esc_html($mode_label); ?>
591 <span class="phpinfowp-badge" style="background: <?php echo $mode_color; ?>15; color: <?php echo $mode_color; ?>; padding: 2px 6px;">
592 <span class="phpinfowp-pulse-dot" style="background: <?php echo $mode_color; ?>; margin-right: 4px;"></span>
593 Active
594 </span>
595 </div>
596 <div class="phpinfowp-env-sub">Web server settings handled via <?php echo esc_html($mode_file); ?> directives.</div>
597 </div>
598 </div>
599
600 <!-- Target File Card -->
601 <div class="phpinfowp-env-card">
602 <div class="phpinfowp-env-icon-box" style="background: #f1f5f9; color: #475569;">
603 <span class="dashicons dashicons-editor-code" style="font-size:20px; width:20px; height:20px;"></span>
604 </div>
605 <div class="phpinfowp-env-details">
606 <div class="phpinfowp-env-label"><?php _e('Target Config File', 'phpinfo-wp'); ?></div>
607 <div class="phpinfowp-env-value" style="font-family: monospace; font-size: 13.5px;"><?php echo esc_html($mode_file); ?></div>
608 <div class="phpinfowp-env-sub" style="word-break: break-all; font-family: monospace; font-size: 11px; background: #f8fafc; padding: 4px 6px; border-radius: 4px; border: 1px solid #e2e8f0; margin-top: 4px;">
609 <?php echo esc_html($target_file); ?>
610 </div>
611 </div>
612 </div>
613
614 <!-- Cache TTL Card -->
615 <div class="phpinfowp-env-card">
616 <?php if ($mode === 'userini'): ?>
617 <div class="phpinfowp-env-icon-box" style="background: #fef3c7; color: #d97706;">
618 <span class="dashicons dashicons-clock" style="font-size:20px; width:20px; height:20px;"></span>
619 </div>
620 <div class="phpinfowp-env-details">
621 <div class="phpinfowp-env-label"><?php _e('Propagation Time', 'phpinfo-wp'); ?></div>
622 <div class="phpinfowp-env-value">~<?php echo esc_html($user_ini_ttl); ?> Seconds</div>
623 <div class="phpinfowp-env-sub"><?php _e('Changes apply after FPM reload (cache TTL).', 'phpinfo-wp'); ?></div>
624 </div>
625 <?php else: ?>
626 <div class="phpinfowp-env-icon-box" style="background: #ecfdf5; color: #10b981;">
627 <span class="dashicons dashicons-yes-alt" style="font-size:20px; width:20px; height:20px;"></span>
628 </div>
629 <div class="phpinfowp-env-details">
630 <div class="phpinfowp-env-label"><?php _e('Propagation Time', 'phpinfo-wp'); ?></div>
631 <div class="phpinfowp-env-value"><?php _e('Instant', 'phpinfo-wp'); ?></div>
632 <div class="phpinfowp-env-sub"><?php _e('Directives are parsed and applied immediately.', 'phpinfo-wp'); ?></div>
633 </div>
634 <?php endif; ?>
635 </div>
636 </div>
637
638 <!-- Notifications / Alerts -->
639 <?php if (!$writable): ?>
640 <div class="phpinfowp-custom-alert" style="background: #fef2f2; border: 1px solid #fca5a5; border-left: 4px solid #ef4444;">
641 <span class="dashicons dashicons-dismiss" style="color: #ef4444; font-size: 20px; width: 20px; height: 20px; flex-shrink: 0;"></span>
642 <p style="color: #991b1b;">
643 <strong><?php _e('Write permission denied.', 'phpinfo-wp'); ?></strong> The directory <code><?php echo esc_html($root_dir); ?></code> is not writable. Settings cannot be saved.
644 </p>
645 </div>
646 <?php elseif ($notice): ?>
647 <div class="phpinfowp-custom-alert" style="background: <?php echo $notice_type === 'success' ? '#f0fdf4' : '#fef2f2'; ?>; border: 1px solid <?php echo $notice_type === 'success' ? '#bbf7d0' : '#fca5a5'; ?>; border-left: 4px solid <?php echo $notice_type === 'success' ? '#22c55e' : '#ef4444'; ?>;">
648 <span class="dashicons <?php echo $notice_type === 'success' ? 'dashicons-yes' : 'dashicons-dismiss'; ?>" style="color: <?php echo $notice_type === 'success' ? '#22c55e' : '#ef4444'; ?>; font-size: 20px; width: 20px; height: 20px; flex-shrink: 0;"></span>
649 <p style="color: <?php echo $notice_type === 'success' ? '#166534' : '#991b1b'; ?>;">
650 <?php echo wp_kses($notice, ['code' => [], 'strong' => [], 'br' => []]); ?>
651 </p>
652 </div>
653 <?php endif; ?>
654
655 <!-- Two Column Layout: Editor & Live File View -->
656 <div class="phpinfowp-htaccess-layout">
657
658 <!-- Left Column: Editor -->
659 <div class="phpinfowp-htaccess-editor-col">
660 <h2 class="phpinfowp-section-heading"><?php _e('PHP Directives', 'phpinfo-wp'); ?></h2>
661
662 <?php
663 $placeholder = $mode === 'htaccess'
664 ? "upload_max_filesize 64M\npost_max_size 64M\nmax_execution_time 120\nmax_input_vars 3000"
665 : "upload_max_filesize = 64M\npost_max_size = 64M\nmax_execution_time = 120\nmax_input_vars = 3000";
666 ?>
667
668 <form method="post" style="margin-bottom: 40px;">
669 <input type="hidden" name="phpinfo_nonce" value="<?php echo wp_create_nonce('phpinfo_nonce'); ?>">
670
671 <!-- Mock IDE Code Window -->
672 <div class="phpinfowp-ide-window">
673 <div class="phpinfowp-ide-header">
674 <div class="phpinfowp-ide-dots">
675 <span class="phpinfowp-ide-dot red"></span>
676 <span class="phpinfowp-ide-dot yellow"></span>
677 <span class="phpinfowp-ide-dot green"></span>
678 <div class="phpinfowp-ide-tabs">
679 <div class="phpinfowp-ide-tab">
680 <span class="dashicons dashicons-editor-code" style="font-size: 14px; width: 14px; height: 14px; margin-top: 2px;"></span>
681 <span><?php echo esc_html($mode_file); ?></span>
682 </div>
683 </div>
684 </div>
685 <span class="phpinfowp-badge phpinfowp-badge-neutral"><?php _e('Editor', 'phpinfo-wp'); ?></span>
686 </div>
687 <div class="phpinfowp-ide-body">
688 <textarea name="htaccess" id="htaccess-editor" class="phpinfowp-ide-textarea"
689 placeholder="<?php echo esc_attr($placeholder); ?>"
690 spellcheck="false"><?php echo esc_textarea($existing); ?></textarea>
691 </div>
692 <div class="phpinfowp-ide-footer">
693 <?php if ($mode === 'htaccess'): ?>
694 One directive per line &mdash; write the name and value only, without the <code>php_value</code> prefix.
695 We will wrap your configuration inside a <code># BEGIN phpinfo-wp</code> block.<br>
696 <strong><?php _e('Auto-rollback:', 'phpinfo-wp'); ?></strong> If the site returns HTTP 500 after saving, your original <code>.htaccess</code> will be restored automatically.
697 <?php else: ?>
698 One directive per line in standard <code>php.ini</code> format: <strong>directive = value</strong>.<br>
699 Shorthands like <strong><?php _e('directive value', 'phpinfo-wp'); ?></strong> are normalized automatically.
700 Your settings are placed in a <code>; BEGIN phpinfo-wp</code> block inside <code>.user.ini</code>.
701 <?php endif; ?>
702 </div>
703 </div>
704
705 <div class="phpinfowp-action-row">
706 <button type="submit" name="save" class="phpinfowp-btn phpinfowp-btn-primary">
707 <span class="dashicons dashicons-saved" style="font-size: 16px; width: 16px; height: 16px;"></span>
708 Save PHP Settings
709 </button>
710
711 <?php if ($mode === 'htaccess'): ?>
712 <div style="display:flex; gap:8px">
713 <button type="submit" name="backup" class="phpinfowp-btn phpinfowp-btn-secondary"
714 onclick="return confirm('Create a .htaccess.bak backup file?')">
715 <span class="dashicons dashicons-backup" style="font-size: 16px; width: 16px; height: 16px;"></span>
716 Backup
717 </button>
718 <button type="submit" name="restore" class="phpinfowp-btn phpinfowp-btn-danger"
719 onclick="return confirm('Restore .htaccess from the last backup? Current changes will be lost.')">
720 <span class="dashicons dashicons-undo" style="font-size: 16px; width: 16px; height: 16px;"></span>
721 Restore Backup
722 </button>
723 </div>
724 <?php endif; ?>
725 </div>
726 </form>
727
728 <!-- Snippet Library -->
729 <hr style="margin: 40px 0 30px; border:0; border-top: 1px solid #e2e8f0;">
730 <h2 class="phpinfowp-section-heading"><?php _e('Web Server Snippet Library', 'phpinfo-wp'); ?></h2>
731
732 <?php if ($snippet_server === 'nginx'): ?>
733 <p class="phpinfowp-section-desc">
734 <strong><?php _e('Nginx Detected.', 'phpinfo-wp'); ?></strong> <?php _e('Nginx does not use <code>.htaccess</code>. Applying these rules requires root access to edit your <code>nginx.conf</code>.', 'phpinfo-wp'); ?>
735 </p>
736 <div class="phpinfowp-custom-alert" style="background: #f8fafc; border: 1px solid #cbd5e1; border-left: 4px solid #64748b; margin-bottom: 20px; box-shadow: 0 1px 2px rgba(0,0,0,0.02);">
737 <span class="dashicons dashicons-shield" style="color: #64748b; font-size: 20px; width: 20px; height: 20px; flex-shrink: 0; margin-top: 1px;"></span>
738 <p style="color: #334155; font-size: 13px; line-height: 1.5; margin: 0; font-weight: 500;">
739 <strong><?php _e('No root access?', 'phpinfo-wp'); ?></strong> <?php _e('If you are on managed WordPress hosting, caching is usually handled for you automatically. Otherwise, we highly recommend using a free CDN like <strong>Cloudflare</strong> to handle caching and compression automatically without editing server files.', 'phpinfo-wp'); ?>
740 </p>
741 </div>
742 <?php else: ?>
743 <p class="phpinfowp-section-desc">
744 <strong><?php _e('Apache/LiteSpeed Detected.', 'phpinfo-wp'); ?></strong> Click any button below to instantly inject the optimized rules directly into your <code>.htaccess</code> file.
745 </p>
746 <?php endif; ?>
747
748 <!-- Cache warning banner above snippets -->
749 <div class="phpinfowp-custom-alert" style="background: #eff6ff; border: 1px solid #bfdbfe; border-left: 4px solid #3b82f6; margin-bottom: 20px; box-shadow: 0 1px 2px rgba(0,0,0,0.02);">
750 <span class="dashicons dashicons-info" style="color: #3b82f6; font-size: 20px; width: 20px; height: 20px; flex-shrink: 0; margin-top: 1px;"></span>
751 <p style="color: #1e3a8a; font-size: 13px; line-height: 1.5; margin: 0; font-weight: 500;">
752 <strong><?php _e('Clear Cache:', 'phpinfo-wp'); ?></strong> You must clear your cache (plugin cache, server cache, CDN, and browser cache) after injecting or setting the code manually to see the results!
753 </p>
754 </div>
755
756 <?php
757 // Pre-calculate which snippets are injected for the bulk bar
758 $preview_for_bulk = file_exists("$root_dir.htaccess") ? file_get_contents("$root_dir.htaccess") : '';
759 $all_injected = true;
760 $none_injected = true;
761 foreach ($snippets as $_sid => $_snip) {
762 $block_check = strtoupper($_sid);
763 if (strpos($preview_for_bulk, "# BEGIN phpinfo-wp-{$block_check}") !== false) {
764 $none_injected = false;
765 } else {
766 $all_injected = false;
767 }
768 }
769 $is_pro_bulk = Phpinfo_WP_License::is_valid();
770 ?>
771
772 <?php if ($snippet_server === 'apache'): ?>
773 <!-- Bulk Action Bar -->
774 <div style="display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:10px; margin-bottom:20px; padding:14px 18px; background:#f8fafc; border:1px solid #e2e8f0; border-radius:8px;">
775 <div>
776 <p style="margin:0; font-size:13px; font-weight:700; color:#0f172a;"><?php _e('Bulk Actions', 'phpinfo-wp'); ?></p>
777 <p style="margin:2px 0 0; font-size:12px; color:#64748b;"><?php _e('Inject or roll back all 4 snippets at once.', 'phpinfo-wp'); ?></p>
778 </div>
779 <div style="display:flex; gap:8px; flex-wrap:wrap;">
780 <?php if ($is_pro_bulk): ?>
781 <form method="post" style="margin:0;">
782 <input type="hidden" name="phpinfo_nonce" value="<?php echo wp_create_nonce('phpinfo_nonce'); ?>">
783 <button type="submit" name="inject_all_snippets" value="1"
784 class="phpinfowp-btn phpinfowp-btn-primary"
785 <?php echo $all_injected ? 'disabled style="opacity:0.5; cursor:not-allowed;"' : ''; ?>
786 onclick="return confirm('Inject all 4 optimization snippets into .htaccess?')">
787 <span class="dashicons dashicons-upload" style="font-size:16px; width:16px; height:16px;"></span>
788 Inject All 4
789 </button>
790 </form>
791 <form method="post" style="margin:0;">
792 <input type="hidden" name="phpinfo_nonce" value="<?php echo wp_create_nonce('phpinfo_nonce'); ?>">
793 <button type="submit" name="rollback_all_snippets" value="1"
794 class="phpinfowp-btn phpinfowp-btn-danger"
795 <?php echo $none_injected ? 'disabled style="opacity:0.5; cursor:not-allowed;"' : ''; ?>
796 onclick="return confirm('Remove ALL 4 injected snippets from .htaccess?')">
797 <span class="dashicons dashicons-undo" style="font-size:16px; width:16px; height:16px;"></span>
798 Rollback All 4
799 </button>
800 </form>
801 <?php else: ?>
802 <a href="https://exeebit.com/phpinfo-wp#pricing" target="_blank" rel="noopener" class="phpinfowp-btn phpinfowp-btn-primary" style="background:#4f46e5; border-color:#4338ca;">
803 <span class="dashicons dashicons-lock" style="font-size:14px; width:14px; height:14px;"></span>
804 Upgrade to Pro
805 </a>
806 <?php endif; ?>
807 </div>
808 </div>
809 <?php endif; ?>
810
811 <div class="phpinfowp-snippets">
812 <?php
813 $is_pro = Phpinfo_WP_License::is_valid();
814 foreach ($snippets as $id => $snippet):
815 $real_code = $snippet_server === 'nginx' ? $snippet['nginx'] : $snippet['apache'];
816 $snippet_code = $is_pro ? $real_code : ($snippet_server === 'nginx'
817 ? "# Nginx configuration rule\n# Unlock phpinfo() WP Pro to copy this optimization rule."
818 : "# Apache/LiteSpeed .htaccess rule\n# Unlock phpinfo() WP Pro to copy this optimization rule.");
819 $is_injected = ($snippet_server === 'apache' && strpos($preview, "# BEGIN phpinfo-wp-" . strtoupper($id)) !== false);
820 ?>
821 <div class="phpinfowp-snippet-card" style="<?php echo !$is_pro ? 'border-color: #e2e8f0; background: #fafafa;' : ''; ?>">
822 <div class="phpinfowp-snippet-title-row">
823 <h3 style="margin:0; font-size:15px; font-weight:700; color:#0f172a; display:flex; align-items:center; gap:8px;">
824 <?php echo esc_html($snippet['title']); ?>
825 <?php if (!$is_pro): ?>
826 <span class="dashicons dashicons-lock" style="font-size:16px; width:16px; height:16px; color:#6366f1; margin-top:2px;"></span>
827 <?php endif; ?>
828 </h3>
829 <div style="display:flex; gap:6px;">
830 <?php if (!$is_pro): ?>
831 <span class="phpinfowp-badge phpinfowp-badge-pro" style="background:#e0e7ff; color:#4f46e5;"><?php _e('PRO', 'phpinfo-wp'); ?></span>
832 <?php endif; ?>
833 <span class="phpinfowp-badge phpinfowp-badge-neutral"><?php echo $snippet_server === 'nginx' ? 'Nginx' : 'Apache'; ?></span>
834 </div>
835 </div>
836 <p class="phpinfowp-snippet-desc" style="margin-bottom: 12px;"><?php echo esc_html($snippet['desc']); ?></p>
837
838 <div style="margin-bottom: 14px; position: relative;">
839 <div class="phpinfowp-ide-window" style="margin-bottom:0; box-shadow:none; border-radius:6px; <?php echo !$is_pro ? 'filter: blur(4px); opacity: 0.6; pointer-events: none; user-select: none;' : ''; ?>">
840 <div class="phpinfowp-ide-header" style="padding: 6px 12px; background: #181825;">
841 <div class="phpinfowp-ide-dots">
842 <span class="phpinfowp-ide-dot red" style="width:7px; height:7px;"></span>
843 <span class="phpinfowp-ide-dot yellow" style="width:7px; height:7px;"></span>
844 <span class="phpinfowp-ide-dot green" style="width:7px; height:7px;"></span>
845 </div>
846 <span style="font-size:11px; color:#585b70; font-family:monospace;"><?php echo $snippet_server === 'nginx' ? 'nginx.conf' : '.htaccess'; ?></span>
847 </div>
848 <div class="phpinfowp-ide-body">
849 <pre style="margin:0; padding:10px 14px; background:#1e1e2e; color:#a6adc8; font-family:ui-monospace,SFMono-Regular,Consolas,monospace; font-size:11.5px; line-height:1.5; overflow-x:auto; max-height:120px;"><?php echo esc_html($snippet_code); ?></pre>
850 </div>
851 </div>
852 </div>
853
854 <?php if (!$is_pro): ?>
855 <div style="background: #f8fafc; border: 1px dashed #cbd5e1; border-radius: 6px; padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; align-items: flex-start;">
856 <p style="margin: 0; font-size: 12.5px; color: #475569; line-height: 1.4;">
857 <strong><?php _e('These settings can make your site up to 2x faster!', 'phpinfo-wp'); ?></strong> Unlock this optimization snippet and boost your performance instantly.
858 </p>
859 <div style="display: flex; align-items: center; justify-content: space-between; width: 100%; flex-wrap: wrap; gap: 10px;">
860 <a href="https://exeebit.com/phpinfo-wp#pricing" target="_blank" rel="noopener" class="phpinfowp-btn phpinfowp-btn-primary" style="padding: 6px 12px; font-size: 12.5px; background: #4f46e5; border-color: #4338ca;">
861 <span class="dashicons dashicons-lock" style="font-size: 14px; width: 14px; height: 14px;"></span>
862 Upgrade to Pro
863 </a>
864 <span style="font-size: 11px; color: #64748b; font-style: italic;"><?php _e('14-day risk-free refund guarantee', 'phpinfo-wp'); ?></span>
865 </div>
866 </div>
867 <?php else: ?>
868 <?php if ($snippet_server === 'nginx'): ?>
869 <button type="button" class="phpinfowp-btn phpinfowp-btn-secondary" onclick="phpinfowp_copy_snippet(this)">
870 <span class="dashicons dashicons-admin-page" style="font-size:16px; width:16px; height:16px;"></span>
871 Copy to Clipboard
872 </button>
873 <?php else: ?>
874 <?php if ($is_injected): ?>
875 <div style="display:flex; align-items:center; gap:8px; flex-wrap:wrap;">
876 <button type="button" class="phpinfowp-btn" style="background: #ecfdf5; color: #059669; border-color: #a7f3d0; cursor: default;" disabled>
877 <span class="dashicons dashicons-yes" style="font-size:16px; width:16px; height:16px;"></span>
878 Injected
879 </button>
880 <form method="post" style="margin: 0;">
881 <input type="hidden" name="phpinfo_nonce" value="<?php echo wp_create_nonce('phpinfo_nonce'); ?>">
882 <button type="submit" name="rollback_snippet" value="<?php echo esc_attr($id); ?>" class="phpinfowp-btn phpinfowp-btn-danger"
883 onclick="return confirm('Remove the &quot;<?php echo esc_js($snippet['title']); ?>&quot; block from .htaccess?')">
884 <span class="dashicons dashicons-undo" style="font-size:16px; width:16px; height:16px;"></span>
885 Rollback
886 </button>
887 </form>
888 </div>
889 <?php else: ?>
890 <form method="post" style="margin: 0;">
891 <input type="hidden" name="phpinfo_nonce" value="<?php echo wp_create_nonce('phpinfo_nonce'); ?>">
892 <button type="submit" name="append_snippet" value="<?php echo esc_attr($id); ?>" class="phpinfowp-btn phpinfowp-btn-secondary">
893 <span class="dashicons dashicons-upload" style="font-size:16px; width:16px; height:16px;"></span>
894 Inject into .htaccess
895 </button>
896 </form>
897 <?php endif; ?>
898 <?php endif; ?>
899 <?php endif; ?>
900 </div>
901 <?php endforeach; ?>
902 </div>
903
904 </div>
905
906 <!-- Right Column: Live file preview -->
907 <div class="phpinfowp-htaccess-preview-col">
908 <h2 class="phpinfowp-section-heading">Current <code><?php echo esc_html($mode_file); ?></code></h2>
909
910 <!-- Mock IDE Preview Window -->
911 <div class="phpinfowp-ide-window">
912 <div class="phpinfowp-ide-header">
913 <div class="phpinfowp-ide-dots">
914 <span class="phpinfowp-ide-dot red"></span>
915 <span class="phpinfowp-ide-dot yellow"></span>
916 <span class="phpinfowp-ide-dot green"></span>
917 <div class="phpinfowp-ide-tabs">
918 <div class="phpinfowp-ide-tab">
919 <span class="dashicons dashicons-visibility" style="font-size: 14px; width: 14px; height: 14px; margin-top: 2px;"></span>
920 <span><?php echo esc_html($mode_file); ?></span>
921 </div>
922 </div>
923 </div>
924 <span class="phpinfowp-badge phpinfowp-badge-pro"><?php _e('Read-Only', 'phpinfo-wp'); ?></span>
925 </div>
926 <div class="phpinfowp-ide-body">
927 <?php if ($preview): ?>
928 <pre class="phpinfowp-ide-preview"><?php echo esc_html($preview); ?></pre>
929 <?php else: ?>
930 <div class="phpinfowp-ide-preview" style="display:flex; align-items:center; justify-content:center; color:#585b70; font-style:italic;"><?php _e('File does not exist yet — it will be created on first save.', 'phpinfo-wp'); ?></div>
931 <?php endif; ?>
932 </div>
933 <div class="phpinfowp-ide-footer"><?php _e('Active directives on disk.', 'phpinfo-wp'); ?></div>
934 </div>
935
936 </div>
937
938 </div>
939 </div>
940
941 <script>
942 function phpinfowp_copy_snippet(btn) {
943 const card = btn.closest('.phpinfowp-snippet-card');
944 const code = card.querySelector('pre').textContent;
945 navigator.clipboard.writeText(code).then(() => {
946 const originalText = btn.innerHTML;
947 btn.innerHTML = '<span class="dashicons dashicons-yes" style="font-size:16px; width:16px; height:16px;"></span> Copied!';
948 btn.style.background = '#d1fae5';
949 btn.style.borderColor = '#059669';
950 btn.style.color = '#047857';
951 setTimeout(() => {
952 btn.innerHTML = originalText;
953 btn.style.background = '';
954 btn.style.borderColor = '';
955 btn.style.color = '';
956 }, 2000);
957 }).catch(err => {
958 alert('Failed to copy text: ' + err);
959 });
960 }
961 </script>
962
963