PluginProbe
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… / 2.11.4
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… v2.11.4
2.11.11 2.11.10 2.11.9 2.11.7 2.11.8 2.11.6 2.11.5 2.11.4 2.11.3 2.11.1 2.11.2 2.11.0 2.10.5 2.10.4 2.10.3 2.10.2 2.10.1 2.10.0 2.9.9 2.9.8 2.9.6 2.9.7 2.9.5 2.9.4 2.9.3 All 86 releases
vigilante / includes / class-wpconfig-security.php

class-wpconfig-security.php in Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… 2.11.4, at includes/class-wpconfig-security.php

725 lines 25.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WP-Config Security Class
4 *
5 * Manages wp-config.php security constants with MULTIPLE safety checks
6 * Uses comment/uncomment strategy to handle existing constants
7 *
8 * @package Vigilante
9 */
10
11 // Prevent direct access
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 /**
17 * Class Vigilante_Wpconfig_Security
18 *
19 * Applies security constants to wp-config.php
20 */
21 class Vigilante_Wpconfig_Security {
22
23 /**
24 * Settings instance
25 *
26 * @var Vigilante_Settings
27 */
28 private $settings;
29
30 /**
31 * Security options
32 *
33 * @var array
34 */
35 private $options;
36
37 /**
38 * Path to wp-config.php
39 *
40 * @var string
41 */
42 private $wpconfig_path;
43
44 /**
45 * Marker for our constants
46 */
47 const MARKER_START = '/* BEGIN Vigilante Security Constants */';
48 const MARKER_END = '/* END Vigilante Security Constants */';
49
50 /**
51 * Marker for commented original lines
52 */
53 const ORIGINAL_MARKER = '// [VIGILANTE_ORIGINAL] ';
54
55 /**
56 * Old plugin markers to clean
57 */
58 const OLD_MARKER_START = '/* BEGIN AyudaWP Security Constants */';
59 const OLD_MARKER_END = '/* END AyudaWP Security Constants */';
60
61 /**
62 * Minimum valid wp-config.php size in bytes
63 */
64 const MIN_CONFIG_SIZE = 1000;
65
66 /**
67 * Constants managed by this plugin
68 *
69 * @var array
70 */
71 private $managed_constants = array(
72 'DISALLOW_FILE_EDIT',
73 'DISALLOW_FILE_MODS',
74 'FORCE_SSL_ADMIN',
75 'FORCE_SSL_LOGIN',
76 'WP_DEBUG',
77 'WP_DEBUG_LOG',
78 'WP_DEBUG_DISPLAY',
79 'SCRIPT_DEBUG',
80 'DISABLE_WP_CRON',
81 );
82
83 /**
84 * Constructor
85 *
86 * @param Vigilante_Settings $settings Settings instance.
87 */
88 public function __construct( $settings ) {
89 $this->settings = $settings;
90 $this->options = $settings->get_section( 'wp_hardening' );
91 $this->wpconfig_path = ABSPATH . 'wp-config.php';
92 }
93
94 /**
95 * Apply security constants to wp-config.php
96 *
97 * @return bool|WP_Error
98 */
99 public function apply_security_constants() {
100 // Safety check 0: on a network this file belongs to the main site
101 if ( ! Vigilante_Settings::can_write_shared_files() ) {
102 return new WP_Error( 'network_not_owner', Vigilante_Settings::get_shared_files_notice() );
103 }
104
105 // Safety check 1: File must exist and be writable
106 if ( ! $this->is_wpconfig_writable() ) {
107 return new WP_Error( 'not_writable', __( 'wp-config.php is not writable', 'vigilante' ) );
108 }
109
110 // Safety check 2: Create backup BEFORE any modification
111 $backup_result = $this->create_backup();
112 if ( is_wp_error( $backup_result ) ) {
113 return $backup_result;
114 }
115
116 // First clean up old plugin constants
117 $this->remove_old_constants();
118
119 // Restore any previously commented constants (clean slate for upgrades)
120 // This ensures constants no longer managed by current version get uncommented
121 $this->uncomment_original_constants();
122
123 // Comment out existing managed constants
124 $comment_result = $this->comment_existing_constants();
125 if ( is_wp_error( $comment_result ) ) {
126 return $comment_result;
127 }
128
129 // Generate and write our constants block
130 $constants = $this->generate_constants();
131 $result = $this->write_constants( $constants );
132
133 // Regenerate critical file baseline so the integrity scan does not
134 // flag our own modifications as unauthorized changes.
135 if ( true === $result ) {
136 /**
137 * Fires after Vigilante successfully writes to wp-config.php.
138 * Used by the file integrity module to update the baseline hash.
139 */
140 do_action( 'vigilante_critical_file_written', 'wp-config.php' );
141 }
142
143 return $result;
144 }
145
146 /**
147 * Create a backup of wp-config.php before modification
148 *
149 * @return bool|WP_Error
150 */
151 private function create_backup() {
152 if ( ! file_exists( $this->wpconfig_path ) ) {
153 return new WP_Error( 'no_config', __( 'wp-config.php does not exist', 'vigilante' ) );
154 }
155
156 $content = $this->read_file_directly( $this->wpconfig_path );
157
158 if ( false === $content || strlen( $content ) < self::MIN_CONFIG_SIZE ) {
159 return new WP_Error( 'invalid_config', __( 'wp-config.php appears invalid or too small', 'vigilante' ) );
160 }
161
162 // Validate it looks like a real wp-config.php
163 if ( ! $this->validate_wpconfig_content( $content ) ) {
164 return new WP_Error( 'invalid_config', __( 'wp-config.php does not appear to be a valid WordPress configuration file', 'vigilante' ) );
165 }
166
167 // Store the backup in a private database option, never as a file under
168 // the web root. wp-config.php holds DB credentials and salts; a file in
169 // wp-content could be served by a misconfigured server. The option is
170 // not reachable over HTTP and is not autoloaded.
171 $stored = update_option(
172 'vigilante_wpconfig_backup',
173 array(
174 'content' => $content,
175 'time' => time(),
176 ),
177 false
178 );
179
180 // update_option() returns false both on failure and when the value is
181 // unchanged; only treat it as an error if the content was not stored.
182 if ( false === $stored && $content !== $this->get_wpconfig_backup_content() ) {
183 return new WP_Error( 'backup_failed', __( 'Could not create wp-config.php backup', 'vigilante' ) );
184 }
185
186 return true;
187 }
188
189 /**
190 * Get the stored wp-config.php backup content, or '' if none.
191 *
192 * @return string
193 */
194 private function get_wpconfig_backup_content() {
195 $backup = get_option( 'vigilante_wpconfig_backup' );
196 return ( is_array( $backup ) && isset( $backup['content'] ) ) ? (string) $backup['content'] : '';
197 }
198
199 /**
200 * Read file directly without WP_Filesystem (more reliable)
201 *
202 * @param string $path File path.
203 * @return string|false
204 */
205 private function read_file_directly( $path ) {
206 if ( ! file_exists( $path ) || ! is_readable( $path ) ) {
207 return false;
208 }
209 return file_get_contents( $path ); // phpcs:ignore
210 }
211
212 /**
213 * Validate that content looks like a real wp-config.php
214 *
215 * @param string $content File content.
216 * @return bool
217 */
218 private function validate_wpconfig_content( $content ) {
219 // Must contain PHP opening tag
220 if ( strpos( $content, '<?php' ) === false ) {
221 return false;
222 }
223
224 // Must contain database configuration
225 if ( strpos( $content, 'DB_NAME' ) === false ) {
226 return false;
227 }
228
229 if ( strpos( $content, 'DB_USER' ) === false ) {
230 return false;
231 }
232
233 if ( strpos( $content, 'DB_PASSWORD' ) === false ) {
234 return false;
235 }
236
237 // Must contain table prefix
238 if ( strpos( $content, '$table_prefix' ) === false ) {
239 return false;
240 }
241
242 return true;
243 }
244
245 /**
246 * Comment out existing managed constants in wp-config.php
247 *
248 * @return bool|WP_Error
249 */
250 private function comment_existing_constants() {
251 $content = $this->read_file_directly( $this->wpconfig_path );
252
253 if ( false === $content || ! $this->validate_wpconfig_content( $content ) ) {
254 return new WP_Error( 'read_failed', __( 'Could not read wp-config.php', 'vigilante' ) );
255 }
256
257 $modified = false;
258
259 foreach ( $this->managed_constants as $constant ) {
260 // Pattern to match define statements for this constant
261 // Matches: define( 'CONSTANT', value ); or define('CONSTANT', value);
262 // Does NOT match already commented lines (commented lines have // prefix before define)
263 $pattern = '/^(\s*)(define\s*\(\s*[\'"]' . preg_quote( $constant, '/' ) . '[\'"]\s*,\s*[^)]+\)\s*;)/m';
264
265 // Loop to comment ALL occurrences, not just the first
266 // wp-config.php files may have duplicate defines (e.g. multiple WP_DEBUG)
267 $safety = 0;
268 while ( preg_match( $pattern, $content, $matches ) && $safety < 20 ) {
269 $safety++;
270 $full_line = $matches[0];
271
272 // Already commented by us — no more uncommented matches possible
273 if ( strpos( $full_line, self::ORIGINAL_MARKER ) !== false ) {
274 break;
275 }
276
277 // Check if this line is inside our Vigilante block (skip it)
278 $marker_pos = strpos( $content, self::MARKER_START );
279 if ( $marker_pos !== false ) {
280 $line_pos = strpos( $content, $full_line );
281 $end_marker_pos = strpos( $content, self::MARKER_END );
282 if ( $line_pos > $marker_pos && $line_pos < $end_marker_pos ) {
283 break; // Inside our block, stop processing this constant
284 }
285 }
286
287 // Comment out this occurrence
288 $replacement = $matches[1] . self::ORIGINAL_MARKER . $matches[2];
289 $content = preg_replace( $pattern, $replacement, $content, 1 );
290 $modified = true;
291 }
292 }
293
294 if ( $modified ) {
295 // Validate BEFORE writing
296 if ( ! $this->validate_wpconfig_content( $content ) ) {
297 return new WP_Error( 'invalid_after_comment', __( 'wp-config.php would be invalid after commenting constants', 'vigilante' ) );
298 }
299
300 if ( ! $this->write_file_directly( $this->wpconfig_path, $content ) ) {
301 return new WP_Error( 'write_failed', __( 'Could not write to wp-config.php', 'vigilante' ) );
302 }
303 }
304
305 return true;
306 }
307
308 /**
309 * Uncomment original constants that were commented by us
310 *
311 * @return bool
312 */
313 private function uncomment_original_constants() {
314 $content = $this->read_file_directly( $this->wpconfig_path );
315
316 if ( false === $content ) {
317 return false;
318 }
319
320 // Find and uncomment lines marked with our original marker
321 $pattern = '/^(\s*)' . preg_quote( self::ORIGINAL_MARKER, '/' ) . '(.+)$/m';
322
323 if ( preg_match( $pattern, $content ) ) {
324 $content = preg_replace( $pattern, '$1$2', $content );
325
326 // Validate BEFORE writing
327 if ( ! $this->validate_wpconfig_content( $content ) ) {
328 return false;
329 }
330
331 return $this->write_file_directly( $this->wpconfig_path, $content );
332 }
333
334 return true;
335 }
336
337 /**
338 * Remove old Easy Vigilante constants from wp-config.php
339 *
340 * @return bool
341 */
342 public function remove_old_constants() {
343 $content = $this->read_file_directly( $this->wpconfig_path );
344
345 if ( false === $content || ! $this->validate_wpconfig_content( $content ) ) {
346 return false;
347 }
348
349 $modified = false;
350
351 // Remove old AyudaWP Security Constants block
352 $pattern = '/' . preg_quote( self::OLD_MARKER_START, '/' ) . '.*?' . preg_quote( self::OLD_MARKER_END, '/' ) . '\s*/s';
353 if ( preg_match( $pattern, $content ) ) {
354 $content = preg_replace( $pattern, '', $content );
355 $modified = true;
356 }
357
358 if ( $modified ) {
359 // Validate BEFORE writing
360 if ( ! $this->validate_wpconfig_content( $content ) ) {
361 return false;
362 }
363 return $this->write_file_directly( $this->wpconfig_path, $content );
364 }
365
366 return true;
367 }
368
369 /**
370 * Remove our security constants from wp-config.php and restore originals
371 *
372 * @return bool|WP_Error
373 */
374 public function remove_constants() {
375 // On a network, a subsite deactivating the plugin must not strip the
376 // constants the main site put there for everyone.
377 if ( ! Vigilante_Settings::can_write_shared_files() ) {
378 return new WP_Error( 'network_not_owner', Vigilante_Settings::get_shared_files_notice() );
379 }
380
381 if ( ! file_exists( $this->wpconfig_path ) ) {
382 return true;
383 }
384
385 $content = $this->read_file_directly( $this->wpconfig_path );
386
387 if ( false === $content ) {
388 return false;
389 }
390
391 // If our markers don't exist, just try to uncomment originals
392 if ( strpos( $content, self::MARKER_START ) === false ) {
393 return $this->uncomment_original_constants();
394 }
395
396 // Validate before modification
397 if ( ! $this->validate_wpconfig_content( $content ) ) {
398 return false;
399 }
400
401 // Remove our section
402 $pattern = '/' . preg_quote( self::MARKER_START, '/' ) . '.*?' . preg_quote( self::MARKER_END, '/' ) . '\s*/s';
403 $new_content = preg_replace( $pattern, '', $content );
404
405 // CRITICAL: Validate result BEFORE writing
406 if ( ! $this->validate_wpconfig_content( $new_content ) ) {
407 // Something went wrong, don't write
408 return false;
409 }
410
411 // Clean up multiple empty lines
412 $new_content = preg_replace( '/\n{3,}/', "\n\n", $new_content );
413
414 // Write the file without our block
415 if ( ! $this->write_file_directly( $this->wpconfig_path, $new_content ) ) {
416 return false;
417 }
418
419 // Now uncomment the original constants
420 return $this->uncomment_original_constants();
421 }
422
423 /**
424 * Generate security constants block (without conditional checks)
425 *
426 * @return string
427 */
428 public function generate_constants() {
429 $constants = array();
430
431 $constants[] = self::MARKER_START;
432 $constants[] = '// Vigilante for WordPress - v' . VIGILANTE_VERSION;
433 $constants[] = '// Generated: ' . gmdate( 'Y-m-d H:i:s' ) . ' UTC';
434 $constants[] = '// Note: Original constants (if any) are commented with [VIGILANTE_ORIGINAL] marker';
435 $constants[] = '// Each define() is wrapped in "if ( ! defined() )" so the block is safe on';
436 $constants[] = '// non-standard setups that pre-define WordPress constants before wp-config.php';
437 $constants[] = '// is parsed (would otherwise trigger a "Constant already defined" fatal).';
438 $constants[] = '';
439
440 // File editing/modification
441 if ( ! empty( $this->options['disallow_file_edit'] ) ) {
442 $constants[] = "// Disable file editing in admin";
443 $constants[] = "if ( ! defined( 'DISALLOW_FILE_EDIT' ) ) { define( 'DISALLOW_FILE_EDIT', true ); }";
444 $constants[] = '';
445 }
446
447 if ( ! empty( $this->options['disallow_file_mods'] ) ) {
448 $constants[] = "// Disable file modifications (plugins/themes install/update)";
449 $constants[] = "if ( ! defined( 'DISALLOW_FILE_MODS' ) ) { define( 'DISALLOW_FILE_MODS', true ); }";
450 $constants[] = '';
451 }
452
453 // SSL settings
454 if ( ! empty( $this->options['force_ssl_admin'] ) ) {
455 $constants[] = "// Force SSL for admin";
456 $constants[] = "if ( ! defined( 'FORCE_SSL_ADMIN' ) ) { define( 'FORCE_SSL_ADMIN', true ); }";
457 $constants[] = '';
458 }
459
460 if ( ! empty( $this->options['force_ssl_login'] ) ) {
461 $constants[] = "// Force SSL for login";
462 $constants[] = "if ( ! defined( 'FORCE_SSL_LOGIN' ) ) { define( 'FORCE_SSL_LOGIN', true ); }";
463 $constants[] = '';
464 }
465
466 // Debug settings - generate when "Hide PHP errors from visitors" is unchecked (development mode)
467 if ( empty( $this->options['wp_debug'] ) ) {
468 $constants[] = "// Debug settings (enabled for development)";
469 $constants[] = "if ( ! defined( 'WP_DEBUG' ) ) { define( 'WP_DEBUG', true ); }";
470 $constants[] = "if ( ! defined( 'WP_DEBUG_LOG' ) ) { define( 'WP_DEBUG_LOG', true ); }";
471 $constants[] = "if ( ! defined( 'WP_DEBUG_DISPLAY' ) ) { define( 'WP_DEBUG_DISPLAY', false ); }";
472 $constants[] = "if ( ! defined( 'SCRIPT_DEBUG' ) ) { define( 'SCRIPT_DEBUG', false ); }";
473 $constants[] = '';
474 } else {
475 $constants[] = "// Debug disabled (production)";
476 $constants[] = "if ( ! defined( 'WP_DEBUG' ) ) { define( 'WP_DEBUG', false ); }";
477 $constants[] = '';
478 }
479
480 // Disable WordPress's built-in pseudo-cron (page-view trigger). Pairs with the
481 // .htaccess block from firewall.protect_wp_cron — this constant alone does NOT
482 // block external HTTP access to wp-cron.php, only the auto-spawn from front-end
483 // page views. Both pieces are needed for full coverage; both require a real
484 // server-side cron job calling wp-cron.php from CLI.
485 if ( ! empty( $this->options['disable_wp_cron'] ) ) {
486 $constants[] = "// Disable WordPress pseudo-cron (use real server-side cron instead)";
487 $constants[] = "if ( ! defined( 'DISABLE_WP_CRON' ) ) { define( 'DISABLE_WP_CRON', true ); }";
488 $constants[] = '';
489 }
490
491 $constants[] = self::MARKER_END;
492 $constants[] = '';
493
494 return implode( "\n", $constants );
495 }
496
497 /**
498 * Write constants to wp-config.php with multiple safety checks
499 *
500 * @param string $constants Constants block to write.
501 * @return bool|WP_Error
502 */
503 private function write_constants( $constants ) {
504 // SAFETY CHECK 1: Read file directly (not via WP_Filesystem which can fail)
505 $content = $this->read_file_directly( $this->wpconfig_path );
506
507 // SAFETY CHECK 2: Verify we got valid content
508 if ( false === $content || strlen( $content ) < self::MIN_CONFIG_SIZE ) {
509 return new WP_Error( 'read_failed', __( 'Could not read wp-config.php or file is too small', 'vigilante' ) );
510 }
511
512 // SAFETY CHECK 3: Validate it's a real wp-config.php
513 if ( ! $this->validate_wpconfig_content( $content ) ) {
514 return new WP_Error( 'invalid_config', __( 'wp-config.php does not appear to be valid', 'vigilante' ) );
515 }
516
517 // Store original for comparison
518 $original_content = $content;
519
520 // Remove existing Vigilante constants block
521 $pattern = '/' . preg_quote( self::MARKER_START, '/' ) . '.*?' . preg_quote( self::MARKER_END, '/' ) . '\s*/s';
522 $content = preg_replace( $pattern, '', $content );
523
524 // Remove old plugin constants block
525 $old_pattern = '/' . preg_quote( self::OLD_MARKER_START, '/' ) . '.*?' . preg_quote( self::OLD_MARKER_END, '/' ) . '\s*/s';
526 $content = preg_replace( $old_pattern, '', $content );
527
528 // Clean up multiple empty lines
529 $content = preg_replace( '/\n{3,}/', "\n\n", $content );
530
531 // SAFETY CHECK 4: Content should still be valid after removal
532 if ( ! $this->validate_wpconfig_content( $content ) ) {
533 return new WP_Error( 'invalid_after_clean', __( 'wp-config.php became invalid after cleanup', 'vigilante' ) );
534 }
535
536 // Find the best place to insert constants
537 $inserted = false;
538
539 // Method 1: Before "That's all, stop editing" comment
540 // This comment may be translated in localized wp-config files, so we use a broad pattern
541 // that matches the block comment immediately before the ABSPATH section.
542 // Known variants: "That's all, stop editing!", "C'est tout, ne touchez plus à ce qui suit",
543 // "Das war's, Schluss mit dem Editieren!", "Ya está. ¡Deja de editar!", etc.
544 $stop_editing_patterns = array(
545 // English (default)
546 "/(\/\*[^*]*That's all,?\s*stop editing[^*]*\*\/)/i",
547 // Broad match: any block comment on its own line(s) immediately before "Absolute path"
548 // This catches translated versions without needing every language
549 '/(\n\/\*[^\n*]{5,80}\*\/)\s*\n+\s*\/\*\*\s*Absolute path/i',
550 );
551
552 foreach ( $stop_editing_patterns as $pattern ) {
553 if ( preg_match( $pattern, $content, $matches ) ) {
554 $content = str_replace(
555 $matches[1],
556 $constants . "\n\n" . $matches[1],
557 $content
558 );
559 $inserted = true;
560 break;
561 }
562 }
563
564 // Method 2: Before "/** Absolute path to the WordPress directory" PHPDoc comment
565 // This is a code comment in wp-config-sample.php and is NOT translatable
566 if ( ! $inserted && preg_match( '/(\/\*\*\s*Absolute path to the WordPress directory)/i', $content, $matches ) ) {
567 $content = str_replace(
568 $matches[1],
569 $constants . "\n\n" . $matches[1],
570 $content
571 );
572 $inserted = true;
573 }
574
575 // Method 3: Before ABSPATH definition (language-independent)
576 if ( ! $inserted && preg_match( '/(if\s*\(\s*!\s*defined\s*\(\s*[\'"]ABSPATH[\'"]\s*\)\s*\))/i', $content, $matches ) ) {
577 $content = str_replace(
578 $matches[1],
579 $constants . "\n\n" . $matches[1],
580 $content
581 );
582 $inserted = true;
583 }
584
585 // Method 4: Before require_once wp-settings.php (language-independent)
586 if ( ! $inserted && preg_match( '/(require[_once\s\(]+[\'"]?.*wp-settings\.php[\'"]?\s*\)?;)/i', $content, $matches ) ) {
587 $content = str_replace(
588 $matches[1],
589 $constants . "\n\n" . $matches[1],
590 $content
591 );
592 $inserted = true;
593 }
594
595 // Method 5: After $table_prefix (safest fallback)
596 if ( ! $inserted && preg_match( '/(\$table_prefix\s*=\s*[\'"][^\'"]+[\'"]\s*;)/i', $content, $matches ) ) {
597 $content = str_replace(
598 $matches[1],
599 $matches[1] . "\n\n" . $constants,
600 $content
601 );
602 $inserted = true;
603 }
604
605 if ( ! $inserted ) {
606 return new WP_Error( 'insert_failed', __( 'Could not find a safe place to insert constants', 'vigilante' ) );
607 }
608
609 // SAFETY CHECK 5: Final content must still be valid
610 if ( ! $this->validate_wpconfig_content( $content ) ) {
611 return new WP_Error( 'invalid_final', __( 'Final wp-config.php would be invalid, aborting', 'vigilante' ) );
612 }
613
614 // SAFETY CHECK 6: Final content should be at least as big as original (minus our old block)
615 if ( strlen( $content ) < strlen( $original_content ) * 0.5 ) {
616 return new WP_Error( 'size_check_failed', __( 'Final wp-config.php would be too small, aborting', 'vigilante' ) );
617 }
618
619 // All checks passed, write the file
620 if ( $this->write_file_directly( $this->wpconfig_path, $content ) ) {
621 return true;
622 }
623
624 return new WP_Error( 'write_failed', __( 'Failed to write wp-config.php', 'vigilante' ) );
625 }
626
627 /**
628 * Write file directly (more reliable than WP_Filesystem)
629 *
630 * @param string $path File path.
631 * @param string $content Content to write.
632 * @return bool
633 */
634 private function write_file_directly( $path, $content ) {
635 return false !== file_put_contents( $path, $content ); // phpcs:ignore
636 }
637
638 /**
639 * Check if wp-config.php is writable
640 *
641 * @return bool
642 */
643 public function is_wpconfig_writable() {
644 if ( ! file_exists( $this->wpconfig_path ) ) {
645 return false;
646 }
647
648 // Initialize WP_Filesystem
649 global $wp_filesystem;
650 if ( ! function_exists( 'WP_Filesystem' ) ) {
651 require_once ABSPATH . 'wp-admin/includes/file.php';
652 }
653 WP_Filesystem();
654
655 if ( ! $wp_filesystem ) {
656 return false;
657 }
658
659 return $wp_filesystem->is_writable( $this->wpconfig_path );
660 }
661
662 /**
663 * Verify if our constants are currently active
664 *
665 * @return bool
666 */
667 public function are_constants_active() {
668 $content = $this->read_file_directly( $this->wpconfig_path );
669 if ( false === $content ) {
670 return false;
671 }
672 return strpos( $content, self::MARKER_START ) !== false;
673 }
674
675 /**
676 * Get current defined constants status
677 *
678 * @return array
679 */
680 public function get_constants_status() {
681 return array(
682 'DISALLOW_FILE_EDIT' => defined( 'DISALLOW_FILE_EDIT' ) ? DISALLOW_FILE_EDIT : null,
683 'DISALLOW_FILE_MODS' => defined( 'DISALLOW_FILE_MODS' ) ? DISALLOW_FILE_MODS : null,
684 'FORCE_SSL_ADMIN' => defined( 'FORCE_SSL_ADMIN' ) ? FORCE_SSL_ADMIN : null,
685 'FORCE_SSL_LOGIN' => defined( 'FORCE_SSL_LOGIN' ) ? FORCE_SSL_LOGIN : null,
686 'WP_DEBUG' => defined( 'WP_DEBUG' ) ? WP_DEBUG : null,
687 'WP_DEBUG_LOG' => defined( 'WP_DEBUG_LOG' ) ? WP_DEBUG_LOG : null,
688 'WP_DEBUG_DISPLAY' => defined( 'WP_DEBUG_DISPLAY' ) ? WP_DEBUG_DISPLAY : null,
689 );
690 }
691
692 /**
693 * Check if there are commented original constants
694 *
695 * @return bool
696 */
697 public function has_commented_originals() {
698 $content = $this->read_file_directly( $this->wpconfig_path );
699 if ( false === $content ) {
700 return false;
701 }
702 return strpos( $content, self::ORIGINAL_MARKER ) !== false;
703 }
704
705 /**
706 * Get list of commented original constants
707 *
708 * @return array
709 */
710 public function get_commented_originals() {
711 $content = $this->read_file_directly( $this->wpconfig_path );
712 if ( false === $content ) {
713 return array();
714 }
715
716 $originals = array();
717 $pattern = '/' . preg_quote( self::ORIGINAL_MARKER, '/' ) . '(.+)$/m';
718
719 if ( preg_match_all( $pattern, $content, $matches ) ) {
720 $originals = $matches[1];
721 }
722
723 return $originals;
724 }
725 }