diff --git a/scripts/install-wordpress.sh b/scripts/install-wordpress.sh index 6174be1..b50331e 100644 --- a/scripts/install-wordpress.sh +++ b/scripts/install-wordpress.sh @@ -3,6 +3,7 @@ # Script to automate WordPress installation. # Includes dependency checks, WP-CLI installation, database setup, # WordPress core installation, and configuration. +# v2.1 - Fixes WP-CLI execution path issues when run as root. # # --- Configuration --- @@ -22,8 +23,6 @@ BLUE='\033[0;34m' NC='\033[0m' # No Color # --- Default Values --- -# Avoid insecure defaults. Consider making these mandatory or generating random ones. -# Leaving them blank to force user input or argument passing. WP_ADMIN_USER="" WP_ADMIN_PASS="" WP_ADMIN_EMAIL="" @@ -32,9 +31,10 @@ DOMAIN="" # Domain will be determined or required DB_HOST="127.0.0.1" DB_ROOT_USER="root" DB_ROOT_PASS="" # Require user to provide this for security -SKIP_DB_ROOT_RESET="false" # By default, perform the root password reset (use --skip-db-root-reset to disable) -WEB_USER="litespeed" # Web server user (e.g., www-data, apache, nginx) +# PERFORM_DB_ROOT_RESET is set during arg parsing +WEB_USER="litespeed" # Web server user WEB_GROUP="litespeed" # Web server group +WP_CLI_PATH="/usr/local/bin/wp" # Path to WP-CLI executable # --- Helper Functions --- @@ -56,6 +56,8 @@ warning() { # Print error messages and exit error_exit() { printf "${RED}[ERROR] %s${NC}\n" "$@" >&2 + # Attempt cleanup before exiting + cleanup &> /dev/null || true exit 1 } @@ -69,7 +71,8 @@ usage() { printf " --wpusername=USERNAME WordPress admin username (mandatory)\n" printf " --wppassword=PASSWORD WordPress admin password (mandatory)\n" printf " --wpemail=EMAIL WordPress admin email (mandatory)\n" - printf " --dbrootpass=PASSWORD Current MySQL/MariaDB root password (mandatory unless resetting)\n" + # Make dbrootpass conditional in description + printf " --dbrootpass=PASSWORD Current MySQL/MariaDB root password (required IF NOT using --reset-db-root-pass)\n" printf "\n" printf "Optional Options:\n" printf " --wproot=PATH WordPress installation directory (default: %s)\n" "$WP_ROOT" @@ -77,7 +80,7 @@ usage() { printf " --webuser=USER Web server user (default: %s)\n" "$WEB_USER" printf " --webgroup=GROUP Web server group (default: %s)\n" "$WEB_GROUP" printf " --dbhost=HOST Database host (default: %s)\n" "$DB_HOST" - printf " --reset-db-root-pass Perform the risky root password reset (requires sudo without password)\n" + printf " --reset-db-root-pass Perform the risky root password reset (requires script runner with root privileges)\n" printf " -h, --help Display this help message\n" printf "\n" printf "Example:\n" @@ -93,28 +96,30 @@ command_exists() { # Function to generate a random secure password generate_password() { - openssl rand -base64 16 # Increased length slightly + openssl rand -base64 16 } # Function to clean up temporary files +# Define WP_CLI_CONFIG_PATH early so cleanup function knows about it +WP_CLI_CONFIG_PATH="/tmp/wp-cli-config-$RANDOM.yml" cleanup() { - info "Cleaning up temporary files..." - rm -f "$WP_CLI_CONFIG_PATH" - # Add any other cleanup tasks here + # Check if file exists before trying to remove + if [[ -n "${WP_CLI_CONFIG_PATH-}" && -f "$WP_CLI_CONFIG_PATH" ]]; then + info "Cleaning up temporary WP-CLI config: $WP_CLI_CONFIG_PATH" + rm -f "$WP_CLI_CONFIG_PATH" + fi } # --- Argument Parsing --- -# Using getopt for better argument handling TEMP=$(getopt -o h --longoptions help,wpusername:,wppassword:,wpemail:,wproot:,domain:,dbhost:,dbrootpass:,reset-db-root-pass,webuser:,webgroup: -n "$0" -- "$@") if [ $? != 0 ]; then - error_exit "Terminating... Invalid arguments." + error_exit "Terminating... Invalid arguments provided." fi -# Note the quotes around "$TEMP": they are essential! eval set -- "$TEMP" unset TEMP -PERFORM_DB_ROOT_RESET="false" +PERFORM_DB_ROOT_RESET="false" # Default value while true; do case "$1" in @@ -129,90 +134,161 @@ while true; do --webuser) WEB_USER="$2"; shift 2 ;; --webgroup) WEB_GROUP="$2"; shift 2 ;; -h|--help) usage ;; - --) shift ; break ;; # End of options - *) error_exit "Internal error! Unexpected option: $1";; + --) shift ; break ;; + *) error_exit "Internal error parsing options! Unexpected option: $1";; esac done +# Set trap *after* WP_CLI_CONFIG_PATH is defined and potentially used +trap cleanup EXIT SIGINT SIGTERM + # --- Validation --- info "Validating parameters..." - if [[ -z "$WP_ADMIN_USER" ]]; then error_exit "WordPress admin username (--wpusername) is required."; fi if [[ -z "$WP_ADMIN_PASS" ]]; then error_exit "WordPress admin password (--wppassword) is required."; fi if [[ -z "$WP_ADMIN_EMAIL" ]]; then error_exit "WordPress admin email (--wpemail) is required."; fi -if [[ ! "$WP_ADMIN_EMAIL" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then error_exit "Invalid email format for --wpemail."; fi +if [[ ! "$WP_ADMIN_EMAIL" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then error_exit "Invalid email format for --wpemail: '$WP_ADMIN_EMAIL'"; fi if [[ "$PERFORM_DB_ROOT_RESET" == "false" && -z "$DB_ROOT_PASS" ]]; then error_exit "Database root password (--dbrootpass) is required unless --reset-db-root-pass is used."; fi -if [[ "$PERFORM_DB_ROOT_RESET" == "true" && -n "$DB_ROOT_PASS" ]]; then warning "Both --reset-db-root-pass and --dbrootpass provided. Will perform reset and ignore provided root password."; fi +if [[ "$PERFORM_DB_ROOT_RESET" == "true" && -n "$DB_ROOT_PASS" ]]; then warning "Both --reset-db-root-pass and --dbrootpass provided. Reset will be performed, provided password ignored."; fi if [[ ! -d "$WP_ROOT" ]]; then error_exit "WordPress root directory '$WP_ROOT' does not exist or is not a directory."; fi if ! id "$WEB_USER" &>/dev/null; then error_exit "Web user '$WEB_USER' does not exist."; fi if ! getent group "$WEB_GROUP" &>/dev/null; then error_exit "Web group '$WEB_GROUP' does not exist."; fi +success "Parameters validated." # --- Determine Domain --- if [[ -z "$DOMAIN" ]]; then - if ! command_exists hostname; then error_exit "'hostname' command not found. Please specify --domain."; fi + if ! command_exists hostname; then error_exit "'hostname' command not found. Please install it or specify --domain."; fi FULL_HOSTNAME=$(hostname -f) - # Attempt to remove common node prefixes, make this more robust if needed - DOMAIN=$(echo "$FULL_HOSTNAME" | sed -E 's/^(node[0-9]*-|wp-|web-|host-)//') - if [[ -z "$DOMAIN" || "$DOMAIN" == "$FULL_HOSTNAME" ]]; then - warning "Could not reliably determine domain from hostname '$FULL_HOSTNAME'. Using it as is." + DOMAIN=$(echo "$FULL_HOSTNAME" | sed -E 's/^(node[0-9]*-|wp-|web-|host-|localhost)//') # Slightly more aggressive cleaning + if [[ -z "$DOMAIN" || "$DOMAIN" == "$FULL_HOSTNAME" || "$DOMAIN" == "localdomain" ]]; then # Handle cases where sed didn't change much + warning "Could not reliably determine a public domain from hostname '$FULL_HOSTNAME'. Using it as is." DOMAIN="$FULL_HOSTNAME" + # Consider erroring out if domain detection is critical and fails + # error_exit "Failed to determine domain automatically. Please specify using --domain." fi - info "Auto-detected domain: $DOMAIN (Use --domain to override)" + info "Auto-detected domain: $DOMAIN (Use --domain to override if incorrect)" else info "Using specified domain: $DOMAIN" fi # --- Dependency Checks --- info "Checking dependencies..." -declare -a dependencies=("php" "mysql" "curl" "openssl" "sudo" "hostname" "sed" "systemctl" "getopt") -for cmd in "${dependencies[@]}"; do - if ! command_exists "$cmd"; then - error_exit "Required command '$cmd' is not installed. Please install it first." - fi -done -# Specific check for pkill if reset is chosen +# Group related checks +if ! command_exists php; then error_exit "'php' command not found. Please install PHP."; fi +if ! command_exists mysql; then error_exit "'mysql' command (client) not found. Please install MySQL/MariaDB client."; fi +if ! command_exists curl; then error_exit "'curl' command not found. Please install curl."; fi +if ! command_exists openssl; then error_exit "'openssl' command not found. Please install openssl."; fi +if ! command_exists getopt; then error_exit "'getopt' command not found. Please install getopt."; fi +if ! command_exists hostname; then error_exit "'hostname' command not found. Please install hostname or provide --domain."; fi +if ! command_exists sed; then error_exit "'sed' command not found. Please install sed."; fi +# Sudo needed for WP-CLI install/move and potentially for DB reset/permissions +if ! command_exists sudo; then error_exit "'sudo' command not found. Sudo is required."; fi + +# Checks specific to DB reset path if [[ "$PERFORM_DB_ROOT_RESET" == "true" ]]; then + if ! command_exists systemctl; then error_exit "'systemctl' command not found, but required for --reset-db-root-pass."; fi if ! command_exists pkill; then error_exit "'pkill' command not found, but required for --reset-db-root-pass."; fi - if ! command_exists mysqld_safe; then error_exit "'mysqld_safe' command not found, but required for --reset-db-root-pass."; fi + # Check for mysqld_safe OR the actual daemon binary, as mysqld_safe might be deprecated + if ! command_exists mysqld_safe && ! command_exists mariadbd && ! command_exists mysqld; then + error_exit "'mysqld_safe' or 'mariadbd'/'mysqld' not found, required for --reset-db-root-pass."; + fi + # Check if we actually *can* use sudo for systemctl - requires root privileges + if [[ "$(id -u)" -ne 0 ]] && ! sudo -n systemctl is-active mariadb &>/dev/null; then + warning "Cannot run 'sudo systemctl' without password. DB reset requires script runner with root privileges or passwordless sudo for systemctl." + # Consider making this an error_exit depending on strictness + fi fi -success "All dependencies found." +success "All essential dependencies found." # --- WP-CLI Setup --- -WP_CLI_PATH="/usr/local/bin/wp" - -# Check if WP-CLI is installed and executable -if ! command_exists wp; then - info "WP-CLI not found. Installing WP-CLI..." - if ! curl -o wp-cli.phar https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar; then +# Check if WP-CLI executable exists at the defined path +if [[ ! -x "$WP_CLI_PATH" ]]; then + info "WP-CLI not found at $WP_CLI_PATH or not executable. Attempting installation..." + TEMP_WP_CLI="./wp-cli.phar" + if ! curl -o "$TEMP_WP_CLI" https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar; then error_exit "Failed to download WP-CLI." fi - chmod +x wp-cli.phar - if ! sudo mv wp-cli.phar "$WP_CLI_PATH"; then - error_exit "Failed to move WP-CLI to $WP_CLI_PATH. Check sudo permissions." + chmod +x "$TEMP_WP_CLI" + # Attempt to move using sudo. This requires sudo privileges. + if ! sudo mv "$TEMP_WP_CLI" "$WP_CLI_PATH"; then + # Cleanup downloaded file if move failed + rm -f "$TEMP_WP_CLI" + error_exit "Failed to move WP-CLI to $WP_CLI_PATH. Check sudo permissions for the move command." fi - # Verify installation - if ! command_exists wp; then - error_exit "WP-CLI installation failed unexpectedly." + # Verify installation by checking the target path again + if [[ ! -x "$WP_CLI_PATH" ]]; then + error_exit "WP-CLI installation failed unexpectedly. $WP_CLI_PATH not found or not executable after move." fi success "WP-CLI installed successfully to $WP_CLI_PATH" else - success "WP-CLI is already installed." + success "WP-CLI is already installed at $WP_CLI_PATH." fi -# Set up temporary WP-CLI config for HTTP_HOST if needed -export WP_CLI_CONFIG_PATH="/tmp/wp-cli-config-$RANDOM.yml" -cat > "$WP_CLI_CONFIG_PATH" </dev/null; then + info "Successfully verified ability to run WP-CLI as '$WEB_USER' using sudo." + SUDO_CMD="sudo -u $WEB_USER" + # Keep WP_EXECUTABLE as full path + else + # Cannot run as root, cannot run as web_user, cannot sudo -u web_user without password. + error_exit "Script lacks permissions. Run as root, as '$WEB_USER', or ensure user '$(id -un)' has passwordless sudo access to run '$WP_EXECUTABLE' as '$WEB_USER'." + fi + fi +fi +info "WP-CLI execution command prefix: [${SUDO_CMD:-direct}] using executable [$WP_EXECUTABLE]" +info "WP-CLI arguments: ${WP_RUN_ARGS[*]}" + +# --- Temporary WP-CLI Config for Domain --- +# Create config file for WP-CLI to potentially pick up domain context +# This helps if WP-CLI struggles to identify the site URL, especially with --allow-root +# Might not be strictly necessary if WP_HOME/WP_SITEURL are defined early enough +# but provides extra robustness. +info "Creating temporary WP-CLI config at $WP_CLI_CONFIG_PATH" +cat > "$WP_CLI_CONFIG_PATH" < /dev/null FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY '$new_root_password'; -ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '$new_root_password'; FLUSH PRIVILEGES; EXIT EOF then - warning "Failed initial root password reset attempt. Trying alternative command..." - # Fallback for older MySQL/MariaDB versions - if ! sudo mysql --protocol=socket -u root <<-EOF + warning "Failed 'ALTER USER' reset attempt (may be normal). Trying root@127.0.0.1..." + if ! sudo mysql --protocol=socket -u root <<-EOF &> /dev/null +FLUSH PRIVILEGES; +ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '$new_root_password'; +FLUSH PRIVILEGES; +EXIT +EOF + then + warning "Failed 'ALTER USER root@127.0.0.1' reset attempt. Trying legacy 'UPDATE' method..." + # Fallback for older MySQL/MariaDB versions + if ! sudo mysql --protocol=socket -u root <<-EOF &> /dev/null FLUSH PRIVILEGES; UPDATE mysql.user SET Password=PASSWORD('$new_root_password') WHERE User='root' AND Host='localhost'; UPDATE mysql.user SET Password=PASSWORD('$new_root_password') WHERE User='root' AND Host='127.0.0.1'; FLUSH PRIVILEGES; EXIT EOF - then - warning "Both root password reset methods failed. Trying grant table directly (less reliable)." - # This might be needed on some very old or strangely configured systems - if ! sudo mysql --protocol=socket -u mysql <<-EOF + then + warning "Failed legacy 'UPDATE' reset method. Trying direct 'authentication_string' update (MariaDB/newer MySQL)..." + if ! sudo mysql --protocol=socket -u root <<-EOF &> /dev/null FLUSH PRIVILEGES; UPDATE mysql.user SET authentication_string=PASSWORD('$new_root_password') WHERE User='root'; FLUSH PRIVILEGES; EXIT EOF - then - error_exit "All attempts to reset the root password failed. Check MySQL/MariaDB logs. Manual intervention required." - fi + then + error_exit "All attempts to reset the root password in safe mode failed. Check MySQL/MariaDB logs. Manual intervention required." + fi + fi fi fi success "Root password likely reset in safe mode." - info "Stopping MariaDB safe mode process..." - # Attempt to kill the background mysqld_safe process gracefully first, then force if needed - sudo kill $MYSQLD_SAFE_PID || true # Try killing the specific PID + info "Stopping MariaDB safe mode process (PID: $MYSQLD_SAFE_PID)..." + # Use sudo to kill processes started by root + sudo kill "$MYSQLD_SAFE_PID" || warning "Failed to kill specific PID $MYSQLD_SAFE_PID (maybe already stopped?)." sleep 2 - sudo pkill -f mysqld_safe || true # Broader attempt + info "Attempting broader pkill for lingering safe mode processes..." + sudo pkill -f mysqld_safe || sudo pkill -f mariadbd-safe || true # Ignore errors if not found sleep 2 - sudo pkill -f mariadbd || sudo pkill -f mysqld || true # Kill any lingering daemons + info "Attempting broader pkill for lingering database daemons..." + sudo pkill -f mariadbd || sudo pkill -f mysqld || true # Ignore errors if not found sleep 3 + success "Safe mode processes likely stopped." - info "Starting MariaDB service normally..." - if ! sudo systemctl start mariadb; then error_exit "Failed to start MariaDB service after password reset."; fi + info "Starting $DB_SERVICE_NAME service normally..." + if ! sudo systemctl start "$DB_SERVICE_NAME"; then error_exit "Failed to start $DB_SERVICE_NAME service after password reset. Check status/logs."; fi sleep 5 # Allow time for service to initialize - if ! sudo systemctl is-active --quiet mariadb; then error_exit "MariaDB service failed to start or become active. Check service status."; fi - success "MariaDB service started successfully with new root password." - DB_ROOT_PASS="$new_root_password" # Use the newly set password + info "Verifying $DB_SERVICE_NAME service status..." + if ! sudo systemctl is-active --quiet "$DB_SERVICE_NAME"; then error_exit "$DB_SERVICE_NAME service failed to start or become active. Check service status."; fi + success "$DB_SERVICE_NAME service started successfully with new root password." + DB_ROOT_PASS="$new_root_password" # Use the newly set password for subsequent operations else info "Using provided root password to create database and user." # Test connection with provided root password if ! mysql -u "$DB_ROOT_USER" -p"$DB_ROOT_PASS" -h "$DB_HOST" -e "SELECT 1;" &> /dev/null; then - error_exit "Failed to connect to database using provided root credentials. Check user, password, and host." + error_exit "Failed to connect to database using provided root credentials. Check user '$DB_ROOT_USER', password, and host '$DB_HOST'." fi success "Database root connection successful." fi @@ -300,141 +399,125 @@ fi # --- Create WordPress Database and User --- info "Creating WordPress database '$DB_NAME' and user '$DB_USER'..." # Use printf for safer password injection into the command -SQL_COMMAND=$(printf "CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS '%s'@'%s' IDENTIFIED BY '%s'; GRANT ALL PRIVILEGES ON %s.* TO '%s'@'%s'; FLUSH PRIVILEGES;" \ +SQL_COMMAND=$(printf "CREATE DATABASE IF NOT EXISTS \`%s\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS '%s'@'%s' IDENTIFIED BY '%s'; GRANT ALL PRIVILEGES ON \`%s\`.* TO '%s'@'%s'; FLUSH PRIVILEGES;" \ "$DB_NAME" "$DB_USER" "$DB_HOST" "$DB_PASSWORD" "$DB_NAME" "$DB_USER" "$DB_HOST") +# Use the DB_ROOT_PASS (either provided or newly generated) if ! mysql -u "$DB_ROOT_USER" -p"$DB_ROOT_PASS" -h "$DB_HOST" -e "$SQL_COMMAND"; then - error_exit "Failed to create WordPress database or user. Check MySQL/MariaDB logs and permissions." + error_exit "Failed to execute SQL command to create WordPress database/user. Check MySQL/MariaDB logs and permissions for '$DB_ROOT_USER'." fi -success "Database and user created successfully." +success "Database '$DB_NAME' and user '$DB_USER' created successfully." -# --- WordPress Installation --- -info "Navigating to WordPress root: $WP_ROOT" +# --- WordPress Core File Setup --- +info "Ensuring WordPress files are present in: $WP_ROOT" cd "$WP_ROOT" || error_exit "Failed to change directory to $WP_ROOT" # Backup existing wp-config.php if it exists if [[ -f "wp-config.php" ]]; then - BACKUP_NAME="wp-config.php.bak.$(date +%Y%m%d%H%M%S)" + BACKUP_NAME="wp-config.php.bak.$(date +%Y%m%d_%H%M%S)" # Use underscore in timestamp info "Backing up existing wp-config.php to $BACKUP_NAME" - cp wp-config.php "$BACKUP_NAME" + cp wp-config.php "$BACKUP_NAME" || warning "Failed to backup wp-config.php" fi -# Download WordPress core files if necessary (index.php is a good indicator) -# Run wp commands as the web user if possible, falling back to --allow-root if needed -WP_RUN_ARGS=("--path=$WP_ROOT") -# Determine if sudo is needed to run as web user -SUDO_CMD="" -if [[ "$(id -u)" -ne "$(id -u "$WEB_USER")" ]]; then - SUDO_CMD="sudo -u $WEB_USER" - # Check if we can sudo without password, otherwise need --allow-root - if ! sudo -n -u "$WEB_USER" true &>/dev/null; then - warning "Cannot sudo to '$WEB_USER' without a password. Using --allow-root for WP-CLI commands." - SUDO_CMD="" # Clear sudo command - WP_RUN_ARGS+=("--allow-root") - else - info "Running WP-CLI commands as user '$WEB_USER'." - fi -fi - - -if [[ ! -f "index.php" || ! -d "wp-admin" ]]; then - info "WordPress core files not found. Downloading..." - if ! $SUDO_CMD wp core download "${WP_RUN_ARGS[@]}" --skip-content --version=latest; then - error_exit "Failed to download WordPress core files." +# Download WordPress core files ONLY if key files/dirs are missing +if [[ ! -f "index.php" || ! -f "wp-includes/version.php" || ! -d "wp-admin" ]]; then + info "WordPress core files seem missing or incomplete. Downloading..." + # Use determined $SUDO_CMD, $WP_EXECUTABLE, and $WP_RUN_ARGS + if ! $SUDO_CMD $WP_EXECUTABLE core download "${WP_RUN_ARGS[@]}" --skip-content --version=latest; then + error_exit "Failed to download WordPress core files using WP-CLI." fi success "WordPress core downloaded." else - info "WordPress files already exist. Skipping download." + info "WordPress core files seem to exist. Skipping download." fi # --- Create wp-config.php --- -# Using wp cli command is generally preferred, but manual creation is more robust if WP-CLI has issues. -# We'll stick to the manual creation from your original script as it included specific fixes. info "Creating wp-config.php..." -# Generate Salts using WP-CLI if possible, otherwise fallback to openssl -SALTS=$($SUDO_CMD wp config salt generate --raw "${WP_RUN_ARGS[@]}" 2>/dev/null || { - warning "Could not generate salts using WP-CLI, falling back to openssl (less standard format)." - echo "define( 'AUTH_KEY', '$(generate_password)' );" - echo "define( 'SECURE_AUTH_KEY', '$(generate_password)' );" - echo "define( 'LOGGED_IN_KEY', '$(generate_password)' );" - echo "define( 'NONCE_KEY', '$(generate_password)' );" - echo "define( 'AUTH_SALT', '$(generate_password)' );" - echo "define( 'SECURE_AUTH_SALT', '$(generate_password)' );" - echo "define( 'LOGGED_IN_SALT', '$(generate_password)' );" - echo "define( 'NONCE_SALT', '$(generate_password)' );" -}) +# Generate Salts using WP-CLI +info "Generating WordPress salts using WP-CLI..." +# Use determined $SUDO_CMD, $WP_EXECUTABLE, and $WP_RUN_ARGS +SALTS=$($SUDO_CMD $WP_EXECUTABLE config salt generate --raw "${WP_RUN_ARGS[@]}" 2>/dev/null) || { + warning "Could not generate salts using WP-CLI. Falling back to openssl (less standard format)." + SALTS=$(cat < wp-config.php < wp-config.php <