File: /wordpress/plugins/wp-cloud-client/1.1.1/src/Debug/WpConfigEditor.php
<?php
declare(strict_types=1);
namespace VPlugins\WPCloudClient\Debug;
/**
* Reads and writes WordPress debug constants in wp-config.php.
*
* Only toggles boolean define() calls. Constants defined with non-boolean
* values (e.g. WP_DEBUG_LOG = '/path/to/log') are detected so we never
* insert a duplicate, but their existing value is preserved untouched.
* If a constant is absent it is inserted before the
* "That's all, stop editing!" marker.
*/
final class WpConfigEditor {
/**
* Absolute path to wp-config.php.
*
* @var string
*/
private string $config_path;
/**
* Class constructor.
*
* @param string $config_path Explicit path to wp-config.php; auto-detected when empty.
*
* @throws \RuntimeException If wp-config.php cannot be located.
*/
public function __construct( string $config_path = '' ) {
$this->config_path = '' !== $config_path ? $config_path : $this->findConfigPath();
}
/**
* Return the absolute path to the wp-config.php file being edited.
*
* @return string Absolute file path.
*/
public function getConfigPath(): string {
return $this->config_path;
}
/**
* Return current boolean values for the four debug constants.
*
* A null value means the constant is not defined in the file.
*
* @return array{WP_DEBUG: bool|null, WP_DEBUG_LOG: bool|null, WP_DEBUG_DISPLAY: bool|null, SCRIPT_DEBUG: bool|null}
*
* @throws \RuntimeException If the file cannot be read.
*/
public function getConstants(): array {
$content = $this->readFile();
return [
'WP_DEBUG' => $this->parseConstant( $content, 'WP_DEBUG' ),
'WP_DEBUG_LOG' => $this->parseConstant( $content, 'WP_DEBUG_LOG' ),
'WP_DEBUG_DISPLAY' => $this->parseConstant( $content, 'WP_DEBUG_DISPLAY' ),
'SCRIPT_DEBUG' => $this->parseConstant( $content, 'SCRIPT_DEBUG' ),
];
}
/**
* Set a single boolean constant in wp-config.php.
*
* Inserts a new define() if the constant is not already present.
*
* @param string $name Constant name (e.g. 'WP_DEBUG').
* @param bool $value Desired value.
*
* @throws \RuntimeException If the file cannot be read or written.
*/
public function setConstant( string $name, bool $value ): void {
$content = $this->readFile();
$value_str = $value ? 'true' : 'false';
if ( preg_match( $this->definePattern( $name ), $content ) ) {
// Existing boolean define — replace it.
$content = preg_replace( $this->definePattern( $name ), "define( '{$name}', {$value_str} )", $content );
} elseif ( preg_match( $this->existsPattern( $name ), $content ) ) {
// Non-boolean define exists (e.g. string path) — preserve it, do not insert a duplicate.
return;
} else {
$content = $this->insertDefine( $content, $name, $value_str );
}
$this->writeFile( $content );
}
/**
* Locate wp-config.php using standard WordPress path conventions.
*
* @return string Absolute path.
*
* @throws \RuntimeException If the file cannot be found.
*/
private function findConfigPath(): string {
$candidates = [
ABSPATH . 'wp-config.php',
dirname( ABSPATH ) . '/wp-config.php',
];
foreach ( $candidates as $path ) {
if ( file_exists( $path ) ) {
return $path;
}
}
throw new \RuntimeException( 'wp-config.php not found.' );
}
/**
* Read the config file contents.
*
* @return string File contents.
*
* @throws \RuntimeException If the file is not readable or read fails.
*/
private function readFile(): string {
if ( ! is_readable( $this->config_path ) ) {
throw new \RuntimeException( sprintf( 'Cannot read wp-config.php at %s.', $this->config_path ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
$content = file_get_contents( $this->config_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
if ( false === $content ) {
throw new \RuntimeException( sprintf( 'Cannot read wp-config.php at %s.', $this->config_path ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
return $content;
}
/**
* Write content back to the config file.
*
* @param string $content New file content.
*
* @throws \RuntimeException If the write fails.
*/
private function writeFile( string $content ): void {
$result = file_put_contents( $this->config_path, $content, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
if ( false === $result ) {
$err = error_get_last()['message'] ?? 'unknown error';
throw new \RuntimeException( sprintf( 'Cannot write wp-config.php at %s: %s', $this->config_path, $err ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
}
/**
* Build a regex that matches a boolean define() call for the given constant name.
*
* @param string $name Constant name.
* @return string Regex pattern.
*/
private function definePattern( string $name ): string {
return '/define\s*\(\s*[\'"]' . preg_quote( $name, '/' ) . '[\'"]\s*,\s*(true|false)\s*\)/';
}
/**
* Build a regex that detects any define() for the given constant name, regardless of value type.
*
* Used to prevent inserting a duplicate define() when a non-boolean value already exists.
*
* @param string $name Constant name.
* @return string Regex pattern.
*/
private function existsPattern( string $name ): string {
return '/define\s*\(\s*[\'"]' . preg_quote( $name, '/' ) . '[\'"]\s*,/';
}
/**
* Insert a define() before the standard WP stop-editing marker, or append at end.
*
* @param string $content Current file content.
* @param string $name Constant name.
* @param string $value_str 'true' or 'false'.
* @return string Updated file content.
*/
private function insertDefine( string $content, string $name, string $value_str ): string {
$marker = "/* That's all, stop editing!";
$line = "define( '{$name}', {$value_str} );";
if ( str_contains( $content, $marker ) ) {
return str_replace( $marker, "{$line}\n{$marker}", $content );
}
// Marker missing (heavily customised wp-config.php). Appending at end may
// land below the wp-settings.php require, in which case the define won't
// affect WP runtime. Acceptable trade-off vs. failing the request.
return $content . "\n{$line}\n";
}
/**
* Parse a single boolean constant from file content.
*
* @param string $content File content.
* @param string $name Constant name.
* @return bool|null True/false if found, null if absent.
*/
private function parseConstant( string $content, string $name ): ?bool {
if ( preg_match( $this->definePattern( $name ), $content, $matches ) ) {
return 'true' === strtolower( $matches[1] );
}
return null;
}
}