ce65004c79
The optimizer now uses the same intelligent three-constraint model as the batch analyzer for consistent recommendations: - Calculates server capacity upfront - Uses three-constraint intelligent function - Falls back to profiles if available (for advanced users) - Shows limiting factor for each recommendation - Ensures fair distribution across all domains This brings the optimizer in line with the batch analyzer and provides the most intelligent, fair, and safe recommendations possible.
3424 lines
127 KiB
Bash
Executable File
3424 lines
127 KiB
Bash
Executable File
#!/bin/bash
|
|
# PHP & Server Performance Optimizer
|
|
# Interactive tool for analyzing and optimizing PHP-FPM configurations
|
|
# Part of Server Toolkit - Phase 3: Main Interactive Script
|
|
|
|
# Source required libraries
|
|
PHP_TOOLKIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../.. && pwd)"
|
|
source "$PHP_TOOLKIT_DIR/lib/common-functions.sh" || { echo "ERROR: common-functions.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/system-detect.sh" || { echo "ERROR: system-detect.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/user-manager.sh" || { echo "ERROR: user-manager.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/php-detector.sh" || { echo "ERROR: php-detector.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/php-analyzer.sh" || { echo "ERROR: php-analyzer.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/php-config-manager.sh" || { echo "ERROR: php-config-manager.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/php-calculator-improved.sh" || { echo "ERROR: php-calculator-improved.sh not found"; exit 1; }
|
|
|
|
# Phase 3 Modular Architecture - NEW (optional but recommended for batch operations)
|
|
source "$PHP_TOOLKIT_DIR/lib/php-ui.sh" 2>/dev/null || true
|
|
source "$PHP_TOOLKIT_DIR/lib/php-scanner.sh" 2>/dev/null || true
|
|
source "$PHP_TOOLKIT_DIR/lib/php-action-executor.sh" 2>/dev/null || true
|
|
source "$PHP_TOOLKIT_DIR/lib/php-server-manager.sh" 2>/dev/null || true
|
|
|
|
# True Optimization - Analytics Library for real data-driven decisions
|
|
source "$PHP_TOOLKIT_DIR/lib/php-analytics.sh" 2>/dev/null || true
|
|
|
|
# Color codes (using safe echo -e)
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
MAGENTA='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
WHITE='\033[1;37m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Safe color echo function
|
|
cecho() {
|
|
echo -e "$@"
|
|
}
|
|
|
|
# ============================================================================
|
|
# BANNER & DISPLAY FUNCTIONS
|
|
# ============================================================================
|
|
|
|
show_banner() {
|
|
clear
|
|
cecho "${CYAN}╔══════════════════════════════════════════════════════════════════════╗${NC}"
|
|
cecho "${CYAN}║${WHITE} PHP & SERVER PERFORMANCE OPTIMIZER ${CYAN}║${NC}"
|
|
cecho "${CYAN}╚══════════════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
}
|
|
|
|
show_main_menu() {
|
|
cecho "${WHITE}${BOLD}MAIN MENU${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
cecho " ${GREEN}1${NC}) Analyze Single Domain"
|
|
cecho " ${GREEN}2${NC}) Analyze All Domains (Server-Wide)"
|
|
cecho " ${GREEN}3${NC}) Quick Health Check (All Domains)"
|
|
cecho " ${GREEN}4${NC}) Optimize Domain PHP Settings"
|
|
cecho " ${GREEN}5${NC}) Optimize Server-Wide PHP Settings"
|
|
cecho " ${GREEN}6${NC}) View OPcache Statistics"
|
|
cecho " ${GREEN}7${NC}) View PHP-FPM Process Stats"
|
|
cecho " ${GREEN}8${NC}) Check for Configuration Issues"
|
|
cecho " ${GREEN}9${NC}) Check Server Memory Capacity (OOM Risk)"
|
|
echo ""
|
|
cecho " ${YELLOW}b${NC}) Backup Current Configurations"
|
|
cecho " ${YELLOW}r${NC}) Restore from Backup"
|
|
echo ""
|
|
cecho " ${RED}0${NC}) Exit"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
}
|
|
|
|
# ============================================================================
|
|
# DOMAIN SELECTION
|
|
# ============================================================================
|
|
|
|
select_domain() {
|
|
local action="${1:-analyze}"
|
|
|
|
cecho "${WHITE}${BOLD}SELECT DOMAIN${NC}"
|
|
echo ""
|
|
|
|
# Ask about filtering
|
|
cecho "${CYAN}Filter options:${NC}"
|
|
cecho " ${GREEN}n${NC}) No filter (show all)"
|
|
cecho " ${GREEN}s${NC}) Search by name"
|
|
cecho " ${GREEN}t${NC}) Show high-traffic domains"
|
|
cecho " ${GREEN}o${NC}) Show domains needing optimization"
|
|
cecho " ${RED}q${NC}) Cancel"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
read -p "Select filter: " filter_choice
|
|
filter_choice=$(echo "${filter_choice,,}")
|
|
|
|
case "$filter_choice" in
|
|
q)
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
# Get all users with domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
if [ -z "$users" ]; then
|
|
cecho "${RED}ERROR: No users found on system${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return 1
|
|
fi
|
|
|
|
# Build domain list
|
|
declare -a domains
|
|
declare -A domain_to_user
|
|
|
|
while IFS= read -r username; do
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
domains+=("$domain")
|
|
domain_to_user["$domain"]="$username"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
if [ ${#domains[@]} -eq 0 ]; then
|
|
cecho "${RED}ERROR: No domains found on system${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return 1
|
|
fi
|
|
|
|
# Apply filters
|
|
declare -a filtered_domains
|
|
|
|
case "$filter_choice" in
|
|
s)
|
|
# Search by name
|
|
echo ""
|
|
read -p "Search pattern: " search_pattern
|
|
for domain in "${domains[@]}"; do
|
|
if [[ "$domain" =~ $search_pattern ]]; then
|
|
filtered_domains+=("$domain")
|
|
fi
|
|
done
|
|
;;
|
|
t)
|
|
# High-traffic domains
|
|
for domain in "${domains[@]}"; do
|
|
local peak=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0")
|
|
if [[ "$peak" =~ ^[0-9]+$ ]] && [ "$peak" -ge 10 ]; then
|
|
filtered_domains+=("$domain")
|
|
fi
|
|
done
|
|
# Sort by peak traffic (highest first)
|
|
;;
|
|
o)
|
|
# Domains needing optimization
|
|
for domain in "${domains[@]}"; do
|
|
local username="${domain_to_user[$domain]}"
|
|
local issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "NONE|NONE|None")
|
|
local has_high_issues=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
|
|
if [ "$has_high_issues" -gt 0 ]; then
|
|
filtered_domains+=("$domain")
|
|
fi
|
|
done
|
|
;;
|
|
*)
|
|
# No filter
|
|
filtered_domains=("${domains[@]}")
|
|
;;
|
|
esac
|
|
|
|
if [ ${#filtered_domains[@]} -eq 0 ]; then
|
|
cecho "${YELLOW}No domains match filter criteria${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return 1
|
|
fi
|
|
|
|
# Display numbered list with optimization status
|
|
cecho "${CYAN}Available domains:${NC}"
|
|
echo ""
|
|
|
|
local index=1
|
|
for domain in "${filtered_domains[@]}"; do
|
|
local username="${domain_to_user[$domain]}"
|
|
local php_version
|
|
php_version=$(detect_php_version_for_domain "$username" "$domain" 2>/dev/null || echo "unknown")
|
|
|
|
# Check optimization status (only for optimize action to reduce noise)
|
|
local status_indicator=""
|
|
if [[ "$action" == "optimize" ]]; then
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "NONE|NONE|None")
|
|
local has_high_issues
|
|
has_high_issues=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
|
|
|
|
if [ "$has_high_issues" -gt 0 ]; then
|
|
status_indicator="${YELLOW}[NEEDS OPTIMIZATION]${NC}"
|
|
else
|
|
status_indicator="${GREEN}[OK]${NC}"
|
|
fi
|
|
fi
|
|
|
|
# Show peak traffic if available
|
|
local traffic_info=""
|
|
local peak=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "")
|
|
if [ -n "$peak" ] && [ "$peak" != "?" ]; then
|
|
traffic_info="${CYAN}(peak: ${peak})${NC} "
|
|
fi
|
|
|
|
printf " ${GREEN}%-3d${NC}) %-40s ${CYAN}[${username}]${NC} ${YELLOW}(${php_version})${NC} %s%s\n" "$index" "$domain" "$(echo -e "$traffic_info")" "$(echo -e "$status_indicator")"
|
|
index=$((index + 1))
|
|
done
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
# Validate domain selection with retry loop
|
|
while true; do
|
|
read -p "Select domain number (or 'q' to cancel): " selection
|
|
|
|
if [[ "$selection" == "q" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
if ! [[ "$selection" =~ ^[0-9]+$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt ${#filtered_domains[@]} ]; then
|
|
echo ""
|
|
cecho "${RED}Invalid selection. Please enter a number 1-${#filtered_domains[@]}${NC}"
|
|
echo ""
|
|
continue
|
|
fi
|
|
|
|
break
|
|
done
|
|
|
|
# Return selected domain and username
|
|
local selected_domain="${filtered_domains[$((selection - 1))]}"
|
|
local selected_user="${domain_to_user[$selected_domain]}"
|
|
|
|
echo "$selected_domain|$selected_user"
|
|
return 0
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 1: ANALYZE SINGLE DOMAIN
|
|
# ============================================================================
|
|
|
|
analyze_single_domain() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "analyze")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Analyzing: ${GREEN}$domain${WHITE} (user: ${CYAN}$username${WHITE})${NC}"
|
|
echo ""
|
|
|
|
# Run comprehensive analysis
|
|
analyze_domain_php "$username" "$domain"
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 2: ANALYZE ALL DOMAINS
|
|
# ============================================================================
|
|
|
|
analyze_all_domains() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}SERVER-WIDE ANALYSIS${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will show domain-by-domain PHP-FPM configuration analysis${NC}"
|
|
cecho "${YELLOW}including memory impact and combined server load...${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
return
|
|
fi
|
|
|
|
# Call the dedicated batch analyzer script
|
|
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
local batch_analyzer="$script_dir/php-fpm-batch-analyzer.sh"
|
|
|
|
if [ -f "$batch_analyzer" ]; then
|
|
bash "$batch_analyzer"
|
|
else
|
|
cecho "${RED}ERROR: Batch analyzer script not found at $batch_analyzer${NC}"
|
|
read -p "Press Enter to continue..."
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 3: QUICK HEALTH CHECK
|
|
# ============================================================================
|
|
|
|
quick_health_check() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}QUICK HEALTH CHECK${NC}"
|
|
echo ""
|
|
|
|
# Get all users
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
declare -A issue_counts
|
|
issue_counts["CRITICAL"]=0
|
|
issue_counts["HIGH"]=0
|
|
issue_counts["MEDIUM"]=0
|
|
issue_counts["LOW"]=0
|
|
|
|
local total_domains=0
|
|
|
|
while IFS= read -r username; do
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
total_domains=$((total_domains + 1))
|
|
|
|
# Detect issues
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain")
|
|
|
|
# Count by severity
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ -z "$severity" ] && continue
|
|
|
|
issue_counts["$severity"]=$((issue_counts["$severity"] + 1))
|
|
done <<< "$issues"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Display results
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}HEALTH CHECK RESULTS${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Total Domains: ${WHITE}$total_domains${NC}"
|
|
echo ""
|
|
cecho " ${RED}CRITICAL${NC} issues: ${issue_counts["CRITICAL"]}"
|
|
cecho " ${YELLOW}HIGH${NC} issues: ${issue_counts["HIGH"]}"
|
|
cecho " ${BLUE}MEDIUM${NC} issues: ${issue_counts["MEDIUM"]}"
|
|
cecho " ${GREEN}LOW${NC} issues: ${issue_counts["LOW"]}"
|
|
echo ""
|
|
|
|
# Overall health score
|
|
local health_score
|
|
health_score=$((100 - (issue_counts["CRITICAL"] * 20) - (issue_counts["HIGH"] * 10) - (issue_counts["MEDIUM"] * 5) - (issue_counts["LOW"] * 2)))
|
|
[ "$health_score" -lt 0 ] && health_score=0
|
|
|
|
if [ "$health_score" -ge 90 ]; then
|
|
cecho " Overall Health: ${GREEN}${BOLD}$health_score/100 - EXCELLENT${NC}"
|
|
elif [ "$health_score" -ge 70 ]; then
|
|
cecho " Overall Health: ${YELLOW}${BOLD}$health_score/100 - GOOD${NC}"
|
|
elif [ "$health_score" -ge 50 ]; then
|
|
cecho " Overall Health: ${YELLOW}${BOLD}$health_score/100 - FAIR${NC}"
|
|
else
|
|
cecho " Overall Health: ${RED}${BOLD}$health_score/100 - POOR${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 4: OPTIMIZE DOMAIN
|
|
# ============================================================================
|
|
|
|
optimize_domain() {
|
|
show_banner
|
|
|
|
cecho "${WHITE}${BOLD}DOMAIN OPTIMIZATION${NC}"
|
|
echo ""
|
|
cecho " ${GREEN}s${NC}) Optimize single domain"
|
|
cecho " ${GREEN}m${NC}) Optimize multiple domains (batch)"
|
|
cecho " ${RED}q${NC}) Cancel"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
read -p "Select option: " batch_choice
|
|
|
|
case "${batch_choice,,}" in
|
|
s)
|
|
optimize_single_domain_wrapper
|
|
;;
|
|
m)
|
|
optimize_multiple_domains_wrapper
|
|
;;
|
|
q)
|
|
return
|
|
;;
|
|
*)
|
|
cecho "${RED}Invalid selection${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
;;
|
|
esac
|
|
}
|
|
|
|
optimize_single_domain_wrapper() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "optimize")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}OPTIMIZE: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Detect issues
|
|
cecho "${YELLOW}Detecting issues...${NC}"
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain")
|
|
|
|
# Display issues
|
|
local has_issues=false
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ -z "$issue_type" ] && continue
|
|
|
|
if [ "$issue_type" != "NONE" ]; then
|
|
has_issues=true
|
|
|
|
case "$severity" in
|
|
CRITICAL)
|
|
cecho "${RED}[CRITICAL]${NC} $message"
|
|
;;
|
|
HIGH)
|
|
cecho "${YELLOW}[HIGH]${NC} $message"
|
|
;;
|
|
MEDIUM)
|
|
cecho "${BLUE}[MEDIUM]${NC} $message"
|
|
;;
|
|
LOW)
|
|
cecho "${GREEN}[LOW]${NC} $message"
|
|
;;
|
|
esac
|
|
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
fi
|
|
done <<< "$issues"
|
|
|
|
if [ "$has_issues" = false ]; then
|
|
cecho "${GREEN}${BOLD}✓ No issues detected - configuration is optimal!${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check server-wide memory capacity first
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho "${WHITE}${BOLD}SERVER MEMORY ANALYSIS${NC}"
|
|
echo ""
|
|
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
cecho " Total Server RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current FPM Capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}% of RAM)"
|
|
|
|
case "$status" in
|
|
CRITICAL)
|
|
cecho " Server Status: ${RED}${BOLD}CRITICAL${NC} - Server at risk of OOM!"
|
|
;;
|
|
WARNING)
|
|
cecho " Server Status: ${YELLOW}${BOLD}WARNING${NC} - High memory pressure"
|
|
;;
|
|
CAUTION)
|
|
cecho " Server Status: ${YELLOW}CAUTION${NC} - Approaching limits"
|
|
;;
|
|
HEALTHY)
|
|
cecho " Server Status: ${GREEN}HEALTHY${NC} - Sufficient headroom"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
|
|
# Check for max_children errors
|
|
cecho "${WHITE}${BOLD}MAX_CHILDREN ERROR ANALYSIS${NC}"
|
|
echo ""
|
|
|
|
local max_children_errors
|
|
max_children_errors=$(analyze_max_children_errors "$username" 7)
|
|
local error_count last_error_time
|
|
error_count=$(echo "$max_children_errors" | cut -d'|' -f1)
|
|
last_error_time=$(echo "$max_children_errors" | cut -d'|' -f2)
|
|
|
|
if [ "$error_count" -gt 0 ]; then
|
|
cecho " ${RED}✗${NC} Found ${RED}${BOLD}$error_count${NC} max_children errors in last 7 days"
|
|
cecho " ${YELLOW}Last error: $last_error_time${NC}"
|
|
cecho " ${CYAN}→${NC} This domain is hitting process limits and rejecting requests!"
|
|
else
|
|
cecho " ${GREEN}✓${NC} No max_children errors in last 7 days"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Get optimization recommendations
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho "${WHITE}${BOLD}RECOMMENDED OPTIMIZATIONS${NC}"
|
|
echo ""
|
|
|
|
# Get total system memory for improved calculation
|
|
local total_ram_mb
|
|
total_ram_mb=$(free -m | awk '/^Mem:/ {print $2}')
|
|
|
|
# IMPROVED: Calculate using new algorithm
|
|
local improved_result
|
|
improved_result=$(calculate_optimal_php_settings "$username" "$total_ram_mb")
|
|
local improved_max_children improved_pm_mode improved_min_spare improved_max_spare improved_reason
|
|
improved_max_children=$(get_field "$improved_result" 1)
|
|
improved_pm_mode=$(get_field "$improved_result" 2)
|
|
improved_min_spare=$(get_field "$improved_result" 3)
|
|
improved_max_spare=$(get_field "$improved_result" 4)
|
|
improved_reason=$(get_field "$improved_result" 5)
|
|
|
|
# OLD: Calculate using legacy algorithm (for comparison)
|
|
local optimal_result
|
|
optimal_result=$(calculate_optimal_max_children "$username" 1024)
|
|
local legacy_max_children legacy_reason
|
|
legacy_max_children=$(echo "$optimal_result" | cut -d'|' -f1)
|
|
legacy_reason=$(echo "$optimal_result" | cut -d'|' -f2)
|
|
|
|
# Get current max_children
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain")
|
|
|
|
# Track available optimizations
|
|
declare -A opt_available
|
|
declare -A opt_description
|
|
local opt_count=0
|
|
|
|
local current_max_children current_pm_mode
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max_children=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
current_pm_mode=$(grep "^pm =" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ -n "$current_max_children" ] && [ "$improved_max_children" -ne "$current_max_children" ]; then
|
|
opt_count=$((opt_count + 1))
|
|
opt_available["max_children"]="true"
|
|
opt_description["max_children"]="Adjust pm.max_children from $current_max_children to $improved_max_children"
|
|
|
|
# Display comprehensive recommendation
|
|
cecho "${GREEN}$opt_count.${NC} Adjust ${BOLD}pm.max_children${NC} from ${RED}$current_max_children${NC} to ${GREEN}$improved_max_children${NC}"
|
|
cecho " ${CYAN}Improved Algorithm:${NC} $improved_max_children (${improved_reason})"
|
|
cecho " ${YELLOW}Legacy Algorithm:${NC} $legacy_max_children (${legacy_reason})"
|
|
|
|
# Show comparison if different
|
|
if [ "$improved_max_children" -ne "$legacy_max_children" ]; then
|
|
local diff=$((improved_max_children - legacy_max_children))
|
|
if [ "$diff" -gt 0 ]; then
|
|
cecho " ${GREEN}✓ Improved: +$diff processes (safer)${NC}"
|
|
else
|
|
cecho " ${GREEN}✓ Improved: $diff processes (more efficient)${NC}"
|
|
fi
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
# Recommend PM mode if different
|
|
if [ -n "$current_pm_mode" ] && [ "$current_pm_mode" != "$improved_pm_mode" ]; then
|
|
opt_count=$((opt_count + 1))
|
|
opt_available["pm_mode"]="true"
|
|
opt_description["pm_mode"]="Change pm mode from $current_pm_mode to $improved_pm_mode"
|
|
cecho "${GREEN}$opt_count.${NC} Change ${BOLD}pm${NC} mode from ${RED}$current_pm_mode${NC} to ${GREEN}$improved_pm_mode${NC}"
|
|
cecho " Recommended: $improved_pm_mode with min_spare=$improved_min_spare, max_spare=$improved_max_spare"
|
|
echo ""
|
|
fi
|
|
fi
|
|
|
|
# Check OPcache
|
|
local opcache_status
|
|
opcache_status=$(analyze_opcache_effectiveness "$username")
|
|
local opcache_state hit_rate opcache_rec
|
|
opcache_state=$(echo "$opcache_status" | cut -d'|' -f1)
|
|
hit_rate=$(echo "$opcache_status" | cut -d'|' -f2)
|
|
opcache_rec=$(echo "$opcache_status" | cut -d'|' -f5)
|
|
|
|
if [ "$opcache_state" = "DISABLED" ]; then
|
|
opt_count=$((opt_count + 1))
|
|
opt_available["opcache"]="true"
|
|
opt_description["opcache"]="Enable OPcache for 40-70% performance boost"
|
|
cecho "${GREEN}$opt_count.${NC} ${BOLD}Enable OPcache${NC} for 40-70% performance boost"
|
|
echo ""
|
|
else
|
|
# Check if hit_rate is numeric before using awk
|
|
if [ -n "$hit_rate" ] && [[ "$hit_rate" =~ ^[0-9]+$ ]]; then
|
|
local hit_rate_low=$(awk "BEGIN {print ($hit_rate < 90 ? 1 : 0)}" 2>/dev/null || echo 0)
|
|
if [ "$hit_rate_low" -eq 1 ]; then
|
|
opt_count=$((opt_count + 1))
|
|
opt_available["opcache"]="true"
|
|
opt_description["opcache"]="Increase opcache.memory_consumption (current hit rate: ${hit_rate}%)"
|
|
cecho "${GREEN}$opt_count.${NC} ${BOLD}Increase opcache.memory_consumption${NC} (current hit rate: ${hit_rate}%)"
|
|
echo ""
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Check if there are any optimizations to apply
|
|
if [ "$opt_count" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ All settings are optimal - no changes needed!${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
|
|
# Ask user which optimizations to apply
|
|
cecho "${WHITE}${BOLD}SELECT OPTIMIZATIONS TO APPLY${NC}"
|
|
echo ""
|
|
cecho " ${GREEN}a${NC}) Apply all $opt_count recommendations"
|
|
[ "${opt_available[max_children]}" = "true" ] && cecho " ${GREEN}1${NC}) Apply max_children optimization only"
|
|
[ "${opt_available[pm_mode]}" = "true" ] && cecho " ${GREEN}2${NC}) Apply PM mode optimization only"
|
|
[ "${opt_available[opcache]}" = "true" ] && cecho " ${GREEN}3${NC}) Apply OPcache optimization only"
|
|
cecho " ${RED}n${NC}) Cancel - don't apply any changes"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
# Validate optimization selection with retry loop
|
|
while true; do
|
|
read -p "Select option: " apply_choice
|
|
|
|
if ! [[ "$apply_choice" =~ ^[a123nNA]$ ]]; then
|
|
echo ""
|
|
cecho "${RED}Invalid selection. Please enter a, 1, 2, 3, or n${NC}"
|
|
echo ""
|
|
continue
|
|
fi
|
|
break
|
|
done
|
|
|
|
# Determine which optimizations to apply
|
|
local apply_max_children=false
|
|
local apply_pm_mode=false
|
|
local apply_opcache=false
|
|
|
|
# Convert to lowercase for consistent case matching
|
|
apply_choice=${apply_choice,,}
|
|
|
|
case "$apply_choice" in
|
|
a)
|
|
apply_max_children=${opt_available[max_children]:-false}
|
|
apply_pm_mode=${opt_available[pm_mode]:-false}
|
|
apply_opcache=${opt_available[opcache]:-false}
|
|
;;
|
|
1)
|
|
apply_max_children=${opt_available[max_children]:-false}
|
|
;;
|
|
2)
|
|
apply_pm_mode=${opt_available[pm_mode]:-false}
|
|
;;
|
|
3)
|
|
apply_opcache=${opt_available[opcache]:-false}
|
|
;;
|
|
n)
|
|
cecho "${YELLOW}Optimization cancelled - no changes made${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
;;
|
|
esac
|
|
|
|
# Check if nothing was selected
|
|
if [ "$apply_max_children" = "false" ] && [ "$apply_pm_mode" = "false" ] && [ "$apply_opcache" = "false" ]; then
|
|
cecho "${YELLOW}No optimizations selected${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Create backup first
|
|
cecho "${CYAN}Creating backup before making changes...${NC}"
|
|
local backup_dir
|
|
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
|
|
|
|
if [ $? -ne 0 ]; then
|
|
cecho "${RED}${BOLD}✗ Backup failed - aborting changes${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho "${GREEN}✓ Backup created: $(basename "$backup_dir")${NC}"
|
|
echo ""
|
|
|
|
# Apply changes
|
|
cecho "${CYAN}Applying selected optimizations...${NC}"
|
|
echo ""
|
|
|
|
local changes_made=0
|
|
local changes_failed=0
|
|
|
|
# Apply max_children if selected (uses improved algorithm)
|
|
if [ "$apply_max_children" = "true" ]; then
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
if [ -n "$improved_max_children" ] && [ -n "$current_max_children" ]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$improved_max_children" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Set pm.max_children = $improved_max_children"
|
|
changes_made=$((changes_made + 1))
|
|
else
|
|
cecho " ${RED}✗${NC} Failed to set pm.max_children"
|
|
changes_failed=$((changes_failed + 1))
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Apply PM mode if selected (STATIC/DYNAMIC/ONDEMAND)
|
|
if [ "$apply_pm_mode" = "true" ]; then
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
if [ -n "$improved_pm_mode" ]; then
|
|
# Apply pm mode
|
|
if modify_fpm_pool_setting "$pool_config" "pm" "$improved_pm_mode" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Set pm = $improved_pm_mode"
|
|
|
|
# For DYNAMIC and ONDEMAND modes, also set min/max spare servers
|
|
if [[ "$improved_pm_mode" == "dynamic" ]] || [[ "$improved_pm_mode" == "ondemand" ]]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.min_spare_servers" "$improved_min_spare" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Set pm.min_spare_servers = $improved_min_spare"
|
|
fi
|
|
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_spare_servers" "$improved_max_spare" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Set pm.max_spare_servers = $improved_max_spare"
|
|
fi
|
|
fi
|
|
|
|
changes_made=$((changes_made + 1))
|
|
else
|
|
cecho " ${RED}✗${NC} Failed to set pm mode"
|
|
changes_failed=$((changes_failed + 1))
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Apply OPcache if selected
|
|
if [ "$apply_opcache" = "true" ]; then
|
|
# Note: OPcache settings are in php.ini, not FPM pool config
|
|
# This would require php.ini modification (not implemented yet)
|
|
cecho " ${YELLOW}⚠${NC} OPcache optimization requires php.ini modification (not yet implemented)"
|
|
# TODO: Implement php.ini modification for OPcache
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
if [ "$changes_made" -gt 0 ]; then
|
|
# Validate the pool config before reloading
|
|
cecho "${CYAN}Validating configuration...${NC}"
|
|
local config_valid=true
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
if ! validate_pool_config "$pool_config" >/dev/null 2>&1; then
|
|
cecho "${RED}✗ Configuration validation failed!${NC}"
|
|
cecho "${YELLOW}The modified configuration has syntax errors.${NC}"
|
|
config_valid=false
|
|
|
|
# Offer to rollback
|
|
if confirm "Rollback changes?"; then
|
|
cecho "${CYAN}Rolling back configuration...${NC}"
|
|
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ Configuration restored from backup${NC}"
|
|
else
|
|
cecho "${RED}✗ Rollback failed - manual recovery may be needed${NC}"
|
|
cecho "${YELLOW}Backup location: $backup_dir${NC}"
|
|
fi
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
else
|
|
cecho "${GREEN}✓ Configuration is valid${NC}"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${GREEN}${BOLD}✓ Applied $changes_made optimization(s)${NC}"
|
|
|
|
if [ "$changes_failed" -gt 0 ]; then
|
|
cecho "${YELLOW}⚠ Failed to apply $changes_failed optimization(s)${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${YELLOW}Changes have been applied. Restart PHP-FPM for changes to take effect.${NC}"
|
|
echo ""
|
|
|
|
if confirm "Restart PHP-FPM now?"; then
|
|
# Detect PHP version
|
|
local php_version
|
|
php_version=$(detect_php_version_for_domain "$username" "$domain")
|
|
|
|
cecho "${CYAN}Restarting PHP-FPM ($php_version)...${NC}"
|
|
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ PHP-FPM reloaded${NC}"
|
|
|
|
# Verify it's running
|
|
sleep 2
|
|
if verify_php_fpm_running "$php_version" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ PHP-FPM is running and accepting connections${NC}"
|
|
|
|
# Verify applied changes - check if new config loaded
|
|
local verify_result
|
|
verify_result=$(verify_applied_changes "$username" "$domain" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$verify_result" ]; then
|
|
cecho "${GREEN}✓ New configuration loaded successfully${NC}"
|
|
fi
|
|
else
|
|
cecho "${RED}✗ WARNING: PHP-FPM is not responding!${NC}"
|
|
cecho "${YELLOW}Service may have crashed due to invalid configuration.${NC}"
|
|
cecho "${YELLOW}Attempting rollback...${NC}"
|
|
|
|
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ Configuration rolled back${NC}"
|
|
cecho "${YELLOW}Restart PHP-FPM manually once this script completes${NC}"
|
|
else
|
|
cecho "${RED}✗ Rollback failed - manual recovery needed${NC}"
|
|
cecho "${YELLOW}Backup location: $backup_dir${NC}"
|
|
fi
|
|
fi
|
|
else
|
|
cecho "${RED}✗ Failed to restart PHP-FPM${NC}"
|
|
cecho "${YELLOW}Attempting rollback...${NC}"
|
|
|
|
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ Configuration rolled back to previous state${NC}"
|
|
else
|
|
cecho "${RED}✗ Rollback failed - manual recovery may be needed${NC}"
|
|
cecho "${YELLOW}Backup location: $backup_dir${NC}"
|
|
fi
|
|
fi
|
|
else
|
|
cecho "${YELLOW}Skipping restart - remember to restart PHP-FPM manually!${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}Backup location: ${WHITE}$(basename "$backup_dir")${NC}"
|
|
cecho "${CYAN}To rollback: Use Option 'r' (Restore from Backup)${NC}"
|
|
else
|
|
cecho "${YELLOW}No changes were applied${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
optimize_multiple_domains_wrapper() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}SELECT DOMAINS TO OPTIMIZE${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Batch operation: Optimize multiple domains in sequence${NC}"
|
|
echo ""
|
|
|
|
# Get all users with domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
if [ -z "$users" ]; then
|
|
cecho "${RED}ERROR: No users found on system${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Build domain list with optimization status
|
|
declare -a domains
|
|
declare -A domain_to_user
|
|
|
|
while IFS= read -r username; do
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
domains+=("$domain")
|
|
domain_to_user["$domain"]="$username"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
if [ ${#domains[@]} -eq 0 ]; then
|
|
cecho "${RED}ERROR: No domains found on system${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Display numbered list with optimization status
|
|
cecho "${CYAN}Available domains (select ones to optimize):${NC}"
|
|
echo ""
|
|
|
|
local index=1
|
|
for domain in "${domains[@]}"; do
|
|
local username="${domain_to_user[$domain]}"
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "NONE|NONE|None")
|
|
local has_high_issues
|
|
has_high_issues=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
|
|
|
|
local status_indicator=""
|
|
if [ "$has_high_issues" -gt 0 ]; then
|
|
status_indicator="${YELLOW}[NEEDS OPTIMIZATION]${NC}"
|
|
else
|
|
status_indicator="${GREEN}[OK]${NC}"
|
|
fi
|
|
|
|
printf " ${GREEN}%-3d${NC}) %-40s ${CYAN}[${username}]${NC} %s\n" "$index" "$domain" "$(echo -e "$status_indicator")"
|
|
index=$((index + 1))
|
|
done
|
|
|
|
echo ""
|
|
cecho "${CYAN}Enter numbers separated by spaces (e.g., '1 3 5') or 'all' for all domains, 'none' to cancel${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
read -p "Selection: " user_selection
|
|
|
|
# Parse selection
|
|
user_selection=$(echo "$user_selection" | tr '[:upper:]' '[:lower:]')
|
|
|
|
declare -a selected_domains
|
|
|
|
if [[ "$user_selection" == "all" ]]; then
|
|
selected_domains=("${domains[@]}")
|
|
elif [[ "$user_selection" =~ ^(none|n)$ ]]; then
|
|
return
|
|
else
|
|
# Parse individual numbers
|
|
for num in $user_selection; do
|
|
if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#domains[@]} ]; then
|
|
selected_domains+=("${domains[$((num - 1))]}")
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ ${#selected_domains[@]} -eq 0 ]; then
|
|
cecho "${YELLOW}No domains selected${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Confirm
|
|
echo ""
|
|
cecho "${CYAN}Selected ${#selected_domains[@]} domain(s) for optimization${NC}"
|
|
if ! confirm "Continue?"; then
|
|
return
|
|
fi
|
|
|
|
# Optimize each selected domain
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}BATCH OPTIMIZATION IN PROGRESS${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
|
|
for domain in "${selected_domains[@]}"; do
|
|
local username="${domain_to_user[$domain]}"
|
|
|
|
cecho "${CYAN}Optimizing: ${WHITE}$domain${NC} ${CYAN}[$username]${NC}"
|
|
|
|
# Run single domain optimization (simplified)
|
|
optimize_domain_direct "$domain" "$username"
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
optimized=$((optimized + 1))
|
|
else
|
|
failed=$((failed + 1))
|
|
fi
|
|
|
|
echo ""
|
|
done
|
|
|
|
# Display summary
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}BATCH OPTIMIZATION COMPLETE${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho " Successfully optimized: ${GREEN}$optimized${NC}"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}$failed${NC}"
|
|
fi
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
optimize_domain_direct() {
|
|
local domain="$1"
|
|
local username="$2"
|
|
|
|
# Detect issues
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "")
|
|
|
|
# Check if there are issues
|
|
local critical_high_count
|
|
critical_high_count=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
|
|
|
|
if [ "$critical_high_count" -eq 0 ]; then
|
|
cecho " ${GREEN}✓ No optimization needed${NC}"
|
|
return 0
|
|
fi
|
|
|
|
# Get pool config
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
|
cecho " ${YELLOW}⊘ No pool config found - skipping${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Get recommendations
|
|
local total_ram_mb
|
|
total_ram_mb=$(free -m | awk '/^Mem:/ {print $2}')
|
|
|
|
local improved_result
|
|
improved_result=$(calculate_optimal_php_settings "$username" "$total_ram_mb" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$improved_result" ]; then
|
|
cecho " ${YELLOW}⊘ Could not calculate recommendations - skipping${NC}"
|
|
return 1
|
|
fi
|
|
|
|
local improved_max=$(get_field "$improved_result" 1)
|
|
local current_max=$(grep "^pm.max_children" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ -z "$current_max" ] || [ -z "$improved_max" ] || [ "$current_max" = "$improved_max" ]; then
|
|
cecho " ${GREEN}✓ Already optimized${NC}"
|
|
return 0
|
|
fi
|
|
|
|
# Create backup
|
|
local backup_dir
|
|
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
|
|
|
|
if [ $? -ne 0 ]; then
|
|
cecho " ${RED}✗ Backup failed - skipping${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Apply optimization
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$improved_max" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓ Updated max_children: $current_max → $improved_max${NC}"
|
|
return 0
|
|
else
|
|
cecho " ${RED}✗ Optimization failed${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTIONS FOR TIERED OPTIMIZATION
|
|
# ============================================================================
|
|
|
|
# Calculate optimal memory_limit based on domain traffic and type
|
|
calculate_optimal_memory_limit() {
|
|
local username="$1"
|
|
local domain="$2"
|
|
local avg_rpm="$3"
|
|
|
|
# Get current memory_limit
|
|
local current_memory
|
|
current_memory=$(get_effective_php_setting "$username" "memory_limit")
|
|
|
|
# Default recommendation based on traffic
|
|
local recommended="128M"
|
|
|
|
if [ "$avg_rpm" -ge 100 ]; then
|
|
recommended="256M"
|
|
elif [ "$avg_rpm" -ge 50 ]; then
|
|
recommended="192M"
|
|
elif [ "$avg_rpm" -ge 20 ]; then
|
|
recommended="128M"
|
|
else
|
|
recommended="64M"
|
|
fi
|
|
|
|
echo "$recommended"
|
|
}
|
|
|
|
# Find all php.ini files for a domain
|
|
find_php_ini_files() {
|
|
local username="$1"
|
|
local domain="$2"
|
|
|
|
local ini_files=""
|
|
|
|
# cPanel locations
|
|
if [ -d "/home/$username/public_html" ]; then
|
|
# Check for .user.ini in document root
|
|
if [ -f "/home/$username/public_html/.user.ini" ]; then
|
|
ini_files+="/home/$username/public_html/.user.ini "
|
|
fi
|
|
fi
|
|
|
|
# Check all PHP versions' pool configs
|
|
for php_conf in /opt/cpanel/ea-php*/root/etc/php.ini; do
|
|
if [ -f "$php_conf" ]; then
|
|
ini_files+="$php_conf "
|
|
fi
|
|
done
|
|
|
|
# Check /etc/php.ini
|
|
if [ -f "/etc/php.ini" ]; then
|
|
ini_files+="/etc/php.ini "
|
|
fi
|
|
|
|
echo "$ini_files" | tr -s ' '
|
|
}
|
|
|
|
# Modify php.ini setting safely
|
|
modify_php_ini_setting() {
|
|
local ini_file="$1"
|
|
local setting="$2"
|
|
local value="$3"
|
|
|
|
if [ ! -f "$ini_file" ]; then
|
|
return 1
|
|
fi
|
|
|
|
# Backup before modifying
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null || return 1
|
|
|
|
# Check if setting exists
|
|
if grep -q "^$setting" "$ini_file"; then
|
|
# Replace existing setting
|
|
sed -i "s/^$setting.*/$setting = $value/" "$ini_file" 2>/dev/null || {
|
|
mv "$ini_file.backup.$$" "$ini_file"
|
|
return 1
|
|
}
|
|
else
|
|
# Add new setting (find appropriate section)
|
|
if grep -q "^\[PHP\]" "$ini_file"; then
|
|
sed -i "/^\[PHP\]/a $setting = $value" "$ini_file" 2>/dev/null || {
|
|
mv "$ini_file.backup.$$" "$ini_file"
|
|
return 1
|
|
}
|
|
else
|
|
# Just append to end
|
|
echo "$setting = $value" >> "$ini_file" 2>/dev/null || {
|
|
mv "$ini_file.backup.$$" "$ini_file"
|
|
return 1
|
|
}
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Validate php.ini syntax
|
|
validate_php_ini() {
|
|
local ini_file="$1"
|
|
|
|
if [ ! -f "$ini_file" ]; then
|
|
return 1
|
|
fi
|
|
|
|
# Use php -i to check for syntax errors (basic validation)
|
|
php -d "display_errors=0" -r "return 0;" 2>&1 | grep -q "Parse error\|Fatal error" && return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
# Calculate optimal pm.max_requests
|
|
calculate_optimal_max_requests() {
|
|
local avg_rpm="$1"
|
|
|
|
# Base recommendation: every 500 requests = 1 process recycle
|
|
# This prevents memory leaks from accumulating
|
|
local max_requests=500
|
|
|
|
if [ "$avg_rpm" -ge 100 ]; then
|
|
max_requests=1000
|
|
elif [ "$avg_rpm" -ge 50 ]; then
|
|
max_requests=750
|
|
elif [ "$avg_rpm" -ge 20 ]; then
|
|
max_requests=500
|
|
else
|
|
max_requests=300
|
|
fi
|
|
|
|
echo "$max_requests"
|
|
}
|
|
|
|
# Check if OPcache is enabled
|
|
is_opcache_enabled() {
|
|
local username="$1"
|
|
|
|
local result=$(check_opcache_enabled "$username" 2>/dev/null)
|
|
[ "$result" = "1" ] && return 0 || return 1
|
|
}
|
|
|
|
# Calculate optimal OPcache memory
|
|
calculate_optimal_opcache_memory() {
|
|
local avg_rpm="$1"
|
|
|
|
# Base recommendation in MB
|
|
local memory="64"
|
|
|
|
if [ "$avg_rpm" -ge 100 ]; then
|
|
memory="256"
|
|
elif [ "$avg_rpm" -ge 50 ]; then
|
|
memory="192"
|
|
elif [ "$avg_rpm" -ge 20 ]; then
|
|
memory="128"
|
|
else
|
|
memory="64"
|
|
fi
|
|
|
|
echo "${memory}M"
|
|
}
|
|
|
|
# Enable OPcache in php.ini
|
|
enable_opcache() {
|
|
local ini_file="$1"
|
|
|
|
# Check current status
|
|
if grep -q "^opcache.enable.*=.*0" "$ini_file" 2>/dev/null; then
|
|
modify_php_ini_setting "$ini_file" "opcache.enable" "1" || return 1
|
|
elif ! grep -q "^opcache.enable" "$ini_file" 2>/dev/null; then
|
|
echo "opcache.enable = 1" >> "$ini_file" || return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Rollback php.ini to backup
|
|
rollback_php_ini() {
|
|
local ini_file="$1"
|
|
local backup_file="$2"
|
|
|
|
if [ -f "$backup_file" ]; then
|
|
cp "$backup_file" "$ini_file" 2>/dev/null && return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# ============================================================================
|
|
# RUN DOMAIN ANALYZER - Collect real usage data
|
|
# ============================================================================
|
|
|
|
run_domain_analyzer() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}DOMAIN PRE-ANALYSIS${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will analyze real usage data from your domains.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}This process will:${NC}"
|
|
cecho " • Scan error logs for memory exhaustion patterns"
|
|
cecho " • Analyze current process memory usage"
|
|
cecho " • Parse traffic logs for peak concurrent requests"
|
|
cecho " • Detect potential memory leaks"
|
|
cecho " • Build accurate domain profiles"
|
|
echo ""
|
|
|
|
if ! confirm "Continue with domain analysis?"; then
|
|
cecho "${YELLOW}Analysis cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check if php-domain-analyzer.sh exists
|
|
local analyzer_script="/root/server-toolkit/modules/performance/php-domain-analyzer.sh"
|
|
if [ ! -f "$analyzer_script" ]; then
|
|
cecho "${RED}ERROR: Domain analyzer script not found${NC}"
|
|
cecho "${YELLOW}Expected: $analyzer_script${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Run the analyzer
|
|
bash "$analyzer_script"
|
|
|
|
echo ""
|
|
cecho "${GREEN}${BOLD}✓ Domain analysis complete!${NC}"
|
|
cecho "${YELLOW}Profiles are ready for optimization.${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# PROFILE-BASED OPTIMIZATION FUNCTIONS (True Optimization)
|
|
# ============================================================================
|
|
|
|
# Get recommendation for memory_limit using actual usage data
|
|
get_memory_limit_recommendation() {
|
|
local domain="$1"
|
|
local username="$2"
|
|
|
|
# Try to get from stored profile first (real data)
|
|
if [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
local profile
|
|
profile=$(cat "/tmp/php-domain-profiles/$domain.profile")
|
|
|
|
# Calculate based on ACTUAL memory seen in logs
|
|
local peak_mem_seen=$(echo "$profile" | cut -d'|' -f11)
|
|
|
|
if [ "$peak_mem_seen" -gt 0 ]; then
|
|
# Use observed peak + 20% buffer
|
|
local recommended=$((peak_mem_seen + (peak_mem_seen / 5)))
|
|
[ "$recommended" -gt 1024 ] && recommended=1024
|
|
[ "$recommended" -lt 64 ] && recommended=64
|
|
echo "${recommended}M"
|
|
return 0
|
|
else
|
|
# No memory data detected - use safe default
|
|
echo "128M"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Fallback to old method if no profile
|
|
calculate_optimal_memory_limit "$username" "$domain" "50"
|
|
}
|
|
|
|
# Get recommendation for max_children using actual traffic data
|
|
get_max_children_recommendation() {
|
|
local domain="$1"
|
|
local username="$2"
|
|
|
|
# Try to get from stored profile first (real traffic data)
|
|
if [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
local profile
|
|
profile=$(cat "/tmp/php-domain-profiles/$domain.profile")
|
|
|
|
# Use ACTUAL peak concurrent from logs
|
|
local peak_concurrent=$(echo "$profile" | cut -d'|' -f3)
|
|
|
|
if [ "$peak_concurrent" -gt 0 ]; then
|
|
# Add 30% safety margin for spikes
|
|
local recommended=$((peak_concurrent + (peak_concurrent / 3)))
|
|
[ "$recommended" -gt 100 ] && recommended=100
|
|
[ "$recommended" -lt 5 ] && recommended=5
|
|
echo "$recommended"
|
|
return 0
|
|
else
|
|
# No traffic detected - use safe minimum
|
|
echo "5"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Fallback to old method if no profile exists
|
|
local traffic_rpm
|
|
traffic_rpm=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0")
|
|
[ "$traffic_rpm" = "?" ] && traffic_rpm="0"
|
|
local recommended=$((traffic_rpm > 5 ? traffic_rpm + 10 : 5))
|
|
[ "$recommended" -gt 100 ] && recommended=100
|
|
[ "$recommended" -lt 5 ] && recommended=5
|
|
echo "$recommended"
|
|
}
|
|
|
|
# Get recommendation for max_requests using memory leak analysis
|
|
get_max_requests_recommendation() {
|
|
local domain="$1"
|
|
|
|
# Try to get from stored profile first
|
|
if [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
local profile
|
|
profile=$(cat "/tmp/php-domain-profiles/$domain.profile")
|
|
|
|
# Check for memory leak pattern
|
|
local leak_type=$(echo "$profile" | cut -d'|' -f12)
|
|
|
|
if [ "$leak_type" = "LIKELY_LEAK" ]; then
|
|
echo "250" # Recycle frequently for leak-prone domains
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Default
|
|
echo "500"
|
|
}
|
|
|
|
# Display profile data if available
|
|
show_profile_analysis() {
|
|
local domain="$1"
|
|
|
|
[ ! -f "/tmp/php-domain-profiles/$domain.profile" ] && return
|
|
|
|
local profile
|
|
profile=$(cat "/tmp/php-domain-profiles/$domain.profile")
|
|
|
|
local peak_concurrent=$(echo "$profile" | cut -d'|' -f3)
|
|
local avg_concurrent=$(echo "$profile" | cut -d'|' -f4)
|
|
local min_mem=$(echo "$profile" | cut -d'|' -f6)
|
|
local max_mem=$(echo "$profile" | cut -d'|' -f7)
|
|
local avg_mem=$(echo "$profile" | cut -d'|' -f8)
|
|
local mem_exhausted=$(echo "$profile" | cut -d'|' -f10)
|
|
local peak_mem=$(echo "$profile" | cut -d'|' -f11)
|
|
local leak_status=$(echo "$profile" | cut -d'|' -f12)
|
|
|
|
cecho "${CYAN}REAL USAGE DATA (from profile analysis):${NC}"
|
|
cecho " Peak Concurrent: ${WHITE}${peak_concurrent}${NC} | Avg: ${avg_concurrent}"
|
|
cecho " Process Memory: ${WHITE}${min_mem}MB${NC}-${max_mem}MB (avg: ${avg_mem}MB)"
|
|
if [ "$mem_exhausted" -gt 0 ]; then
|
|
cecho " Memory Exhaustion Errors: ${RED}${mem_exhausted}${NC}"
|
|
fi
|
|
cecho " Peak Memory Seen: ${WHITE}${peak_mem}MB${NC}"
|
|
cecho " Memory Status: ${leak_status}"
|
|
echo ""
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZE ALL DOMAINS (SERVER-WIDE) - TIERED OPTIMIZATION
|
|
# ============================================================================
|
|
|
|
optimize_all_domains() {
|
|
show_banner
|
|
|
|
cecho "${WHITE}${BOLD}SERVER-WIDE OPTIMIZATION MENU${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Choose optimization level for all domains on the server${NC}"
|
|
echo ""
|
|
|
|
cecho "${CYAN}PRE-ANALYSIS (Recommended for accurate optimization):${NC}"
|
|
cecho " ${GREEN}0${NC}) Pre-analyze domains"
|
|
cecho " └─ Collect real usage data for data-driven optimization"
|
|
echo ""
|
|
|
|
cecho "${CYAN}OPTIMIZATION LEVELS:${NC}"
|
|
cecho " ${GREEN}1${NC}) Optimize pm.max_children only"
|
|
cecho " └─ Adjust process limits based on traffic"
|
|
echo ""
|
|
cecho " ${GREEN}2${NC}) Optimize pm.max_children + memory_limit"
|
|
cecho " └─ Add PHP memory settings for each domain"
|
|
echo ""
|
|
cecho " ${GREEN}3${NC}) Optimize max_children + memory_limit + pm.max_requests"
|
|
cecho " └─ Add process recycling to prevent memory leaks"
|
|
echo ""
|
|
cecho " ${GREEN}4${NC}) Optimize OPcache only"
|
|
cecho " └─ Enable/increase OPcache for performance"
|
|
echo ""
|
|
cecho " ${GREEN}5${NC}) Optimize EVERYTHING"
|
|
cecho " └─ All of the above (safest, most thorough)"
|
|
echo ""
|
|
cecho " ${RED}b${NC}) Back to main menu"
|
|
cecho " ${RED}q${NC}) Cancel"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
while true; do
|
|
read -p "Select option (0-5, b, or q): " opt_choice
|
|
opt_choice=${opt_choice,,}
|
|
|
|
if [[ "$opt_choice" =~ ^[0-5bq]$ ]]; then
|
|
break
|
|
fi
|
|
echo ""
|
|
cecho "${RED}Invalid choice. Please enter 0-5, b, or q${NC}"
|
|
echo ""
|
|
done
|
|
|
|
case "$opt_choice" in
|
|
0)
|
|
run_domain_analyzer
|
|
;;
|
|
1)
|
|
optimize_level_1_max_children
|
|
;;
|
|
2)
|
|
optimize_level_2_memory
|
|
;;
|
|
3)
|
|
optimize_level_3_advanced
|
|
;;
|
|
4)
|
|
optimize_level_4_opcache
|
|
;;
|
|
5)
|
|
optimize_level_5_everything
|
|
;;
|
|
b)
|
|
return
|
|
;;
|
|
q)
|
|
cecho "${YELLOW}Optimization cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 1: max_children ONLY
|
|
# ============================================================================
|
|
|
|
optimize_level_1_max_children() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 1: Optimize pm.max_children (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will adjust pm.max_children based on available memory and traffic.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will happen:${NC}"
|
|
cecho " • Analyze memory capacity across all domains"
|
|
cecho " • Calculate optimal max_children per domain"
|
|
cecho " • Apply changes and validate configuration"
|
|
cecho " • Restart PHP-FPM if changes needed"
|
|
echo ""
|
|
cecho "${RED}${BOLD}WARNING:${NC} ${RED}This will modify PHP-FPM pool configurations!${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check server capacity
|
|
show_banner
|
|
cecho "${CYAN}Step 1: Checking server memory capacity...${NC}"
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
echo ""
|
|
cecho " Total RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}%)"
|
|
cecho " Status: ${WHITE}${status}${NC}"
|
|
echo ""
|
|
|
|
# Check if profiles exist
|
|
local profiles_exist=0
|
|
if [ -d "/tmp/php-domain-profiles" ] && [ "$(ls -1 /tmp/php-domain-profiles/*.profile 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
profiles_exist=1
|
|
cecho "${GREEN}${BOLD}✓ Domain profiles detected!${NC}"
|
|
cecho "${YELLOW}Using REAL usage data from domain analysis${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
# Get recommendations
|
|
cecho "${CYAN}Step 2: Calculating optimal settings...${NC}"
|
|
echo ""
|
|
|
|
declare -A recommended_values
|
|
declare -A domain_to_username
|
|
local changes_needed=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local current_max
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
[ -z "$current_max" ] && current_max="?"
|
|
else
|
|
current_max="?"
|
|
fi
|
|
|
|
# Get recommendations - use profile if available, otherwise use traffic-based
|
|
local recommended_max
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
recommended_max=$(get_max_children_recommendation "$domain" "$username")
|
|
else
|
|
# Fallback to traffic-based (old method)
|
|
local traffic_rpm
|
|
traffic_rpm=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0")
|
|
[ "$traffic_rpm" = "?" ] && traffic_rpm="0"
|
|
recommended_max=$((traffic_rpm > 5 ? traffic_rpm + 10 : 5))
|
|
fi
|
|
|
|
recommended_values["$domain"]="$recommended_max"
|
|
domain_to_username["$domain"]="$username"
|
|
|
|
if [ "$current_max" != "?" ] && [ "$current_max" != "$recommended_max" ]; then
|
|
changes_needed=$((changes_needed + 1))
|
|
cecho " ${GREEN}✓${NC} $domain: $current_max → ${GREEN}$recommended_max${NC}"
|
|
|
|
# Show profile data if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
show_profile_analysis "$domain"
|
|
fi
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
echo ""
|
|
if [ "$changes_needed" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ All domains already optimized - no changes needed${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho " Total changes needed: ${YELLOW}${BOLD}$changes_needed${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Apply these $changes_needed changes?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Apply changes
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Applying optimizations...${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local recommended="${recommended_values[$domain]}"
|
|
[ -z "$recommended" ] && continue
|
|
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
|
continue
|
|
fi
|
|
|
|
local current
|
|
current=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ "$current" != "$recommended" ]; then
|
|
# Create backup
|
|
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
|
|
|
|
# Apply change
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: $current → $recommended"
|
|
optimized=$((optimized + 1))
|
|
else
|
|
cecho " ${RED}✗${NC} $domain: Failed to apply"
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
echo ""
|
|
cecho "${CYAN}Restarting PHP-FPM...${NC}"
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}SUMMARY${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Applied: ${GREEN}${optimized}${NC} changes"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}${failed}${NC} changes"
|
|
fi
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 2: max_children + memory_limit
|
|
# ============================================================================
|
|
|
|
optimize_level_2_memory() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 2: Optimize pm.max_children + memory_limit (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will adjust both process limits and PHP memory settings.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will be optimized:${NC}"
|
|
cecho " • pm.max_children (based on real traffic data if available)"
|
|
cecho " • memory_limit (PHP memory per domain)"
|
|
echo ""
|
|
cecho "${RED}${BOLD}WARNING:${NC} ${RED}This will modify PHP-FPM and php.ini configurations!${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check server capacity
|
|
show_banner
|
|
cecho "${CYAN}Step 1: Checking server memory capacity...${NC}"
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
echo ""
|
|
cecho " Total RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current FPM capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}%)"
|
|
cecho " Status: ${WHITE}${status}${NC}"
|
|
echo ""
|
|
|
|
# Check if profiles exist
|
|
local profiles_exist=0
|
|
if [ -d "/tmp/php-domain-profiles" ] && [ "$(ls -1 /tmp/php-domain-profiles/*.profile 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
profiles_exist=1
|
|
cecho "${GREEN}${BOLD}✓ Domain profiles detected!${NC}"
|
|
cecho "${YELLOW}Using REAL usage data from domain analysis${NC}"
|
|
echo ""
|
|
else
|
|
cecho "${YELLOW}${BOLD}⚠ No domain profiles found${NC}"
|
|
cecho "${CYAN}For more accurate optimization, run pre-analysis first:${NC}"
|
|
cecho " ${WHITE}php-optimizer.sh${NC} → Option 3 (Pre-analyze domains)"
|
|
echo ""
|
|
fi
|
|
|
|
# Get recommendations
|
|
cecho "${CYAN}Step 2: Calculating optimal settings...${NC}"
|
|
echo ""
|
|
|
|
declare -A recommended_max_children
|
|
declare -A recommended_memory_limit
|
|
declare -A domain_to_username
|
|
local changes_needed=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local current_max
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
[ -z "$current_max" ] && current_max="?"
|
|
else
|
|
current_max="?"
|
|
fi
|
|
|
|
# Get recommendations - use profile if available, otherwise use traffic-based
|
|
local recommended_max
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
recommended_max=$(get_max_children_recommendation "$domain" "$username")
|
|
else
|
|
# Fallback to traffic-based (old method)
|
|
local traffic_rpm
|
|
traffic_rpm=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0")
|
|
[ "$traffic_rpm" = "?" ] && traffic_rpm="0"
|
|
recommended_max=$((traffic_rpm > 5 ? traffic_rpm + 10 : 5))
|
|
fi
|
|
|
|
local recommended_memory
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
recommended_memory=$(get_memory_limit_recommendation "$domain" "$username")
|
|
else
|
|
# Fallback to traffic-based (old method)
|
|
local traffic_rpm
|
|
traffic_rpm=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0")
|
|
[ "$traffic_rpm" = "?" ] && traffic_rpm="0"
|
|
recommended_memory=$(calculate_optimal_memory_limit "$username" "$domain" "$traffic_rpm")
|
|
fi
|
|
|
|
recommended_max_children["$domain"]="$recommended_max"
|
|
recommended_memory_limit["$domain"]="$recommended_memory"
|
|
domain_to_username["$domain"]="$username"
|
|
|
|
if [ "$current_max" != "?" ] && [ "$current_max" != "$recommended_max" ]; then
|
|
changes_needed=$((changes_needed + 1))
|
|
cecho " ${GREEN}✓${NC} $domain:"
|
|
cecho " pm.max_children: $current_max → ${GREEN}$recommended_max${NC}"
|
|
cecho " memory_limit: → ${GREEN}$recommended_memory${NC}"
|
|
|
|
# Show profile data if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
show_profile_analysis "$domain"
|
|
fi
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
echo ""
|
|
if [ "$changes_needed" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ All domains already optimized${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho " Total domains to optimize: ${YELLOW}${BOLD}$changes_needed${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Apply these changes?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Apply changes
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Applying Level 2 Optimizations...${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
declare -a changes_log
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local recommended_max="${recommended_max_children[$domain]}"
|
|
local recommended_mem="${recommended_memory_limit[$domain]}"
|
|
|
|
[ -z "$recommended_max" ] && continue
|
|
|
|
# 1. Optimize pm.max_children (FPM pool config)
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
local current_max
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ "$current_max" != "$recommended_max" ]; then
|
|
# Backup FPM config
|
|
cp "$pool_config" "$pool_config.backup.$$" 2>/dev/null
|
|
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended_max" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: pm.max_children → $recommended_max"
|
|
changes_log+=("$domain: pm.max_children $current_max→$recommended_max")
|
|
optimized=$((optimized + 1))
|
|
else
|
|
cecho " ${RED}✗${NC} $domain: Failed to update pm.max_children"
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# 2. Optimize memory_limit (php.ini files)
|
|
local ini_files
|
|
ini_files=$(find_php_ini_files "$username" "$domain")
|
|
|
|
while IFS= read -r ini_file; do
|
|
[ -z "$ini_file" ] && continue
|
|
[ ! -f "$ini_file" ] && continue
|
|
|
|
# Backup ini file
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null
|
|
|
|
if modify_php_ini_setting "$ini_file" "memory_limit" "$recommended_mem" >/dev/null 2>&1; then
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: memory_limit → $recommended_mem ($ini_file)"
|
|
changes_log+=("$domain: memory_limit→$recommended_mem")
|
|
else
|
|
cecho " ${YELLOW}⚠${NC} $domain: PHP syntax error in $ini_file, rolling back"
|
|
rollback_php_ini "$ini_file" "$ini_file.backup.$$"
|
|
fi
|
|
fi
|
|
done <<< "$ini_files"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Restart PHP-FPM
|
|
echo ""
|
|
cecho "${CYAN}Restarting PHP-FPM services...${NC}"
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
# Summary
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}LEVEL 2 OPTIMIZATION SUMMARY${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Optimizations applied: ${GREEN}${optimized}${NC}"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}${failed}${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
if [ "${#changes_log[@]}" -gt 0 ]; then
|
|
cecho "${WHITE}${BOLD}Changes Applied:${NC}"
|
|
for change in "${changes_log[@]}"; do
|
|
cecho " • $change"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 3: max_children + max_requests + memory_limit
|
|
# ============================================================================
|
|
|
|
optimize_level_3_advanced() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 3: Advanced Optimization (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will optimize multiple PHP-FPM settings for comprehensive improvement.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will be optimized:${NC}"
|
|
cecho " • pm.max_children (process limits)"
|
|
cecho " • pm.max_requests (memory leak prevention)"
|
|
cecho " • memory_limit (PHP memory allocation)"
|
|
echo ""
|
|
cecho "${RED}${BOLD}WARNING:${NC} ${RED}This will modify PHP-FPM and php.ini configurations!${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check server capacity
|
|
show_banner
|
|
cecho "${CYAN}Step 1: Checking server memory capacity...${NC}"
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
echo ""
|
|
cecho " Total RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current FPM capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}%)"
|
|
cecho " Status: ${WHITE}${status}${NC}"
|
|
echo ""
|
|
|
|
# Check if profiles exist
|
|
local profiles_exist=0
|
|
if [ -d "/tmp/php-domain-profiles" ] && [ "$(ls -1 /tmp/php-domain-profiles/*.profile 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
profiles_exist=1
|
|
cecho "${GREEN}${BOLD}✓ Domain profiles detected!${NC}"
|
|
cecho "${YELLOW}Using REAL usage data and memory leak detection${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
# Get recommendations
|
|
cecho "${CYAN}Step 2: Calculating optimal settings...${NC}"
|
|
echo ""
|
|
|
|
declare -A recommended_max_children
|
|
declare -A recommended_memory_limit
|
|
declare -A recommended_max_requests
|
|
declare -A domain_to_username
|
|
local changes_needed=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
recommended_max_children["$domain"]=0
|
|
recommended_memory_limit["$domain"]=0
|
|
recommended_max_requests["$domain"]=0
|
|
domain_to_username["$domain"]="$username"
|
|
|
|
# Use profile-based if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
recommended_max_children["$domain"]=$(get_max_children_recommendation "$domain" "$username")
|
|
recommended_memory_limit["$domain"]=$(get_memory_limit_recommendation "$domain" "$username")
|
|
recommended_max_requests["$domain"]=$(get_max_requests_recommendation "$domain")
|
|
else
|
|
# Fallback to traffic-based (old method)
|
|
local traffic_rpm
|
|
traffic_rpm=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0")
|
|
[ "$traffic_rpm" = "?" ] && traffic_rpm="0"
|
|
|
|
recommended_max_children["$domain"]=$((traffic_rpm > 5 ? traffic_rpm + 10 : 5))
|
|
recommended_memory_limit["$domain"]=$(calculate_optimal_memory_limit "$username" "$domain" "$traffic_rpm")
|
|
recommended_max_requests["$domain"]=$(calculate_optimal_max_requests "$traffic_rpm")
|
|
fi
|
|
|
|
local current_max
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
[ -z "$current_max" ] && current_max="?"
|
|
else
|
|
current_max="?"
|
|
fi
|
|
|
|
local rec_max_children=${recommended_max_children["$domain"]}
|
|
local rec_memory=${recommended_memory_limit["$domain"]}
|
|
local rec_requests=${recommended_max_requests["$domain"]}
|
|
|
|
if [ "$current_max" != "?" ] && [ "$current_max" != "$rec_max_children" ]; then
|
|
changes_needed=$((changes_needed + 1))
|
|
cecho " ${GREEN}✓${NC} $domain:"
|
|
cecho " pm.max_children: $current_max → ${GREEN}$rec_max_children${NC}"
|
|
cecho " memory_limit: → ${GREEN}$rec_memory${NC}"
|
|
cecho " pm.max_requests: → ${GREEN}$rec_requests${NC} (prevent memory leaks)"
|
|
|
|
# Show profile data if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
show_profile_analysis "$domain"
|
|
fi
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
echo ""
|
|
if [ "$changes_needed" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ All domains already optimized${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho " Total domains to optimize: ${YELLOW}${BOLD}$changes_needed${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Apply these changes?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Apply changes
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Applying Level 3 Optimizations (Advanced)...${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
declare -a changes_log
|
|
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local recommended_max="${recommended_max_children[$domain]}"
|
|
local recommended_mem="${recommended_memory_limit[$domain]}"
|
|
local recommended_requests="${recommended_max_requests[$domain]}"
|
|
|
|
[ -z "$recommended_max" ] && continue
|
|
|
|
# 1. Optimize pm.max_children
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
local current_max
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
# Backup
|
|
cp "$pool_config" "$pool_config.backup.$$" 2>/dev/null
|
|
|
|
local pool_updated=0
|
|
|
|
# Update pm.max_children
|
|
if [ "$current_max" != "$recommended_max" ]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended_max" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: pm.max_children → $recommended_max"
|
|
changes_log+=("$domain: pm.max_children $current_max→$recommended_max")
|
|
pool_updated=$((pool_updated + 1))
|
|
fi
|
|
fi
|
|
|
|
# Update pm.max_requests
|
|
if [ -n "$recommended_requests" ]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_requests" "$recommended_requests" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: pm.max_requests → $recommended_requests"
|
|
changes_log+=("$domain: pm.max_requests→$recommended_requests")
|
|
pool_updated=$((pool_updated + 1))
|
|
fi
|
|
fi
|
|
|
|
if [ "$pool_updated" -gt 0 ]; then
|
|
optimized=$((optimized + 1))
|
|
fi
|
|
fi
|
|
|
|
# 2. Optimize memory_limit
|
|
local ini_files
|
|
ini_files=$(find_php_ini_files "$username" "$domain")
|
|
|
|
while IFS= read -r ini_file; do
|
|
[ -z "$ini_file" ] && continue
|
|
[ ! -f "$ini_file" ] && continue
|
|
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null
|
|
|
|
if modify_php_ini_setting "$ini_file" "memory_limit" "$recommended_mem" >/dev/null 2>&1; then
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: memory_limit → $recommended_mem"
|
|
changes_log+=("$domain: memory_limit→$recommended_mem")
|
|
else
|
|
cecho " ${YELLOW}⚠${NC} Rolling back $ini_file"
|
|
rollback_php_ini "$ini_file" "$ini_file.backup.$$"
|
|
fi
|
|
fi
|
|
done <<< "$ini_files"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Restart PHP-FPM
|
|
echo ""
|
|
cecho "${CYAN}Restarting PHP-FPM services...${NC}"
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
# Summary
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}LEVEL 3 OPTIMIZATION SUMMARY (Advanced)${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Optimizations applied: ${GREEN}${optimized}${NC}"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}${failed}${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
if [ "${#changes_log[@]}" -gt 0 ]; then
|
|
cecho "${WHITE}${BOLD}Changes Applied:${NC}"
|
|
for change in "${changes_log[@]}"; do
|
|
cecho " • $change"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 4: OPcache ONLY
|
|
# ============================================================================
|
|
|
|
optimize_level_4_opcache() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 4: Optimize OPcache (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will enable/optimize OPcache for all domains.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will happen:${NC}"
|
|
cecho " • Enable OPcache if disabled"
|
|
cecho " • Adjust memory_consumption based on needs"
|
|
cecho " • Optimize hit rate and performance"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Analyze current OPcache status
|
|
show_banner
|
|
cecho "${CYAN}Step 1: Analyzing current OPcache status...${NC}"
|
|
echo ""
|
|
|
|
declare -A opcache_enabled
|
|
declare -A opcache_needs_enable
|
|
local needs_enable_count=0
|
|
local already_enabled=0
|
|
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
if is_opcache_enabled "$username"; then
|
|
opcache_enabled["$domain"]="1"
|
|
already_enabled=$((already_enabled + 1))
|
|
else
|
|
opcache_needs_enable["$domain"]="1"
|
|
needs_enable_count=$((needs_enable_count + 1))
|
|
cecho " ${YELLOW}⚠${NC} $domain: OPcache is disabled"
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
echo ""
|
|
cecho " Domains with OPcache enabled: ${GREEN}${already_enabled}${NC}"
|
|
cecho " Domains needing OPcache: ${YELLOW}${needs_enable_count}${NC}"
|
|
echo ""
|
|
|
|
if [ "$needs_enable_count" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ OPcache is already enabled on all domains${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
if ! confirm "Enable OPcache on $needs_enable_count domain(s)?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Enable OPcache
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Enabling OPcache...${NC}"
|
|
echo ""
|
|
|
|
local enabled=0
|
|
local failed=0
|
|
declare -a changes_log
|
|
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
[ "${opcache_needs_enable[$domain]}" != "1" ] && continue
|
|
|
|
# Find php.ini files for this domain
|
|
local ini_files
|
|
ini_files=$(find_php_ini_files "$username" "$domain")
|
|
|
|
while IFS= read -r ini_file; do
|
|
[ -z "$ini_file" ] && continue
|
|
[ ! -f "$ini_file" ] && continue
|
|
|
|
# Backup
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null
|
|
|
|
# Enable OPcache
|
|
if enable_opcache "$ini_file" >/dev/null 2>&1; then
|
|
# Calculate optimal memory for OPcache
|
|
local avg_rpm
|
|
avg_rpm=$(calculate_avg_requests_per_minute "$username" 24 | cut -d'|' -f1)
|
|
local optimal_memory
|
|
optimal_memory=$(calculate_optimal_opcache_memory "$avg_rpm")
|
|
|
|
# Set memory_consumption
|
|
if modify_php_ini_setting "$ini_file" "opcache.memory_consumption" "$optimal_memory" >/dev/null 2>&1; then
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: OPcache enabled (memory: $optimal_memory)"
|
|
changes_log+=("$domain: OPcache enabled with $optimal_memory")
|
|
enabled=$((enabled + 1))
|
|
else
|
|
cecho " ${YELLOW}⚠${NC} OPcache PHP error in $ini_file, rolling back"
|
|
rollback_php_ini "$ini_file" "$ini_file.backup.$$"
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
else
|
|
cecho " ${RED}✗${NC} $domain: Failed to enable OPcache"
|
|
failed=$((failed + 1))
|
|
fi
|
|
done <<< "$ini_files"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Restart PHP-FPM
|
|
echo ""
|
|
cecho "${CYAN}Restarting PHP-FPM services...${NC}"
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
# Summary
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}LEVEL 4 OPTIMIZATION SUMMARY (OPcache)${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " OPcache enabled: ${GREEN}${enabled}${NC}"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}${failed}${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
if [ "${#changes_log[@]}" -gt 0 ]; then
|
|
cecho "${WHITE}${BOLD}Changes Applied:${NC}"
|
|
for change in "${changes_log[@]}"; do
|
|
cecho " • $change"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 5: EVERYTHING
|
|
# ============================================================================
|
|
|
|
optimize_level_5_everything() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 5: OPTIMIZE EVERYTHING (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will apply all optimizations comprehensively.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will be optimized:${NC}"
|
|
cecho " • pm.max_children (process limits)"
|
|
cecho " • pm.mode (STATIC/DYNAMIC/ONDEMAND)"
|
|
cecho " • pm.min_spare_servers & pm.max_spare_servers"
|
|
cecho " • pm.max_requests (memory leak prevention)"
|
|
cecho " • memory_limit (PHP memory allocation)"
|
|
cecho " • OPcache (enable & optimize)"
|
|
echo ""
|
|
cecho "${RED}${BOLD}WARNING:${NC} ${RED}This is the most comprehensive optimization!${NC}"
|
|
cecho "${YELLOW}This will take several minutes to complete.${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue with FULL optimization?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Run all levels in sequence
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 5: Complete Optimization Starting...${NC}"
|
|
echo ""
|
|
|
|
# Note: We'll run the core logic here instead of calling the functions
|
|
# to provide unified progress display
|
|
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}STEP 1: Analyzing Server Capacity${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
cecho " Total RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current FPM capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}%)"
|
|
cecho " Status: ${WHITE}${status}${NC}"
|
|
echo ""
|
|
|
|
# CRITICAL SAFETY CHECK: If current usage is already > 80%, warn user
|
|
if [ "$percentage" -gt 80 ]; then
|
|
cecho "${RED}${BOLD}⚠ CRITICAL: Server is running with insufficient PHP-FPM headroom!${NC}"
|
|
cecho "${RED}Current allocation is ${percentage}% of RAM.${NC}"
|
|
cecho "${RED}Optimization may not be sufficient - consider:${NC}"
|
|
cecho "${RED} 1. Upgrading server RAM${NC}"
|
|
cecho "${RED} 2. Disabling problematic domains${NC}"
|
|
cecho "${RED} 3. Migrating heavy sites to dedicated servers${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
cecho "${CYAN}STEP 2: Calculating Intelligent Recommendations${NC}"
|
|
echo ""
|
|
|
|
# Calculate server capacity for fair share allocation
|
|
local server_capacity_result
|
|
server_capacity_result=$(calculate_server_capacity "$TOTAL_RAM_MB")
|
|
local server_capacity
|
|
server_capacity=$(echo "$server_capacity_result" | cut -d'|' -f1)
|
|
cecho " Server PHP-FPM capacity: ${WHITE}${server_capacity}${NC} max_children"
|
|
cecho " Using three-constraint model: Memory + Traffic + Fair Share"
|
|
echo ""
|
|
|
|
# Check if profiles exist
|
|
local profiles_exist=0
|
|
if [ -d "/tmp/php-domain-profiles" ] && [ "$(ls -1 /tmp/php-domain-profiles/*.profile 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
profiles_exist=1
|
|
cecho "${GREEN}${BOLD}✓ Domain profiles detected!${NC}"
|
|
cecho "${YELLOW}Using REAL usage data for comprehensive optimization${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
declare -A recommended_max_children
|
|
declare -A recommended_memory_limit
|
|
declare -A recommended_max_requests
|
|
declare -A domain_to_username
|
|
declare -A opcache_needs_enable
|
|
local changes_count=0
|
|
local opcache_count=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local current_max
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
[ -z "$current_max" ] && current_max="?"
|
|
else
|
|
current_max="?"
|
|
fi
|
|
|
|
# Get recommendations using THREE-CONSTRAINT INTELLIGENT MODEL
|
|
local recommended_max
|
|
local recommended_memory
|
|
local recommended_requests
|
|
local traffic_pct=50 # Default if no data
|
|
|
|
# Get traffic percentage for this domain
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
# Use profile data if available
|
|
recommended_max=$(get_max_children_recommendation "$domain" "$username")
|
|
recommended_memory=$(get_memory_limit_recommendation "$domain" "$username")
|
|
recommended_requests=$(get_max_requests_recommendation "$domain")
|
|
else
|
|
# Use intelligent three-constraint model
|
|
traffic_pct=$(get_domain_traffic_percentage "$username" "$domain" "$user_domains" 2>/dev/null | cut -d'|' -f1)
|
|
traffic_pct=${traffic_pct:-50}
|
|
|
|
# Call intelligent three-constraint function
|
|
local intel_result
|
|
intel_result=$(calculate_optimal_php_settings_intelligent "$username" "$TOTAL_RAM_MB" "$server_capacity" "$traffic_pct" 2>/dev/null || echo "20|dynamic|1|5|ERROR|Failed")
|
|
|
|
recommended_max=$(echo "$intel_result" | cut -d'|' -f1)
|
|
recommended_memory=$(calculate_optimal_memory_limit "$username" "$domain" "$recommended_max" 2>/dev/null || echo "128M")
|
|
recommended_requests=$(calculate_optimal_max_requests "$recommended_max" 2>/dev/null || echo "0")
|
|
fi
|
|
|
|
recommended_max_children["$domain"]="$recommended_max"
|
|
recommended_memory_limit["$domain"]="$recommended_memory"
|
|
recommended_max_requests["$domain"]="$recommended_requests"
|
|
domain_to_username["$domain"]="$username"
|
|
|
|
if [ "$current_max" != "?" ] && [ "$current_max" != "$recommended_max" ]; then
|
|
changes_count=$((changes_count + 1))
|
|
fi
|
|
|
|
if ! is_opcache_enabled "$username"; then
|
|
opcache_needs_enable["$domain"]="1"
|
|
opcache_count=$((opcache_count + 1))
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
cecho " Domains needing updates: ${YELLOW}${changes_count}${NC}"
|
|
cecho " Domains needing OPcache: ${YELLOW}${opcache_count}${NC}"
|
|
echo ""
|
|
|
|
# CRITICAL VALIDATION: Check if combined recommendations exceed safe limits
|
|
cecho "${CYAN}STEP 2b: Validating Combined Capacity${NC}"
|
|
echo ""
|
|
|
|
local total_recommended_max_children=0
|
|
local avg_memory_per_process=20 # Conservative 20MB per process
|
|
local total_recommended_memory=0
|
|
|
|
for domain in "${!recommended_max_children[@]}"; do
|
|
local rec_max="${recommended_max_children[$domain]}"
|
|
[ -z "$rec_max" ] && continue
|
|
total_recommended_max_children=$((total_recommended_max_children + rec_max))
|
|
total_recommended_memory=$((total_recommended_memory + (rec_max * avg_memory_per_process)))
|
|
done
|
|
|
|
# Determine if recommendations are safe
|
|
local max_safe_php_fpm=$((total_ram_mb * 60 / 100)) # 60% of RAM for PHP-FPM
|
|
|
|
if [ "$total_recommended_memory" -gt "$max_safe_php_fpm" ]; then
|
|
cecho "${RED}${BOLD}⚠ WARNING: Combined recommendations exceed safe limits!${NC}"
|
|
cecho "${RED}Recommended total: ${total_recommended_memory}MB (${total_recommended_max_children} max_children combined)${NC}"
|
|
cecho "${RED}Safe maximum: ${max_safe_php_fpm}MB${NC}"
|
|
cecho "${YELLOW}Applying safety caps to prevent OOM crashes...${NC}"
|
|
|
|
# Scale down all recommendations proportionally
|
|
local scale_factor=$((max_safe_php_fpm * 100 / total_recommended_memory))
|
|
scale_factor=$((scale_factor / 100)) # Convert to percentage
|
|
|
|
for domain in "${!recommended_max_children[@]}"; do
|
|
local old_max="${recommended_max_children[$domain]}"
|
|
[ -z "$old_max" ] && continue
|
|
local new_max=$((old_max * scale_factor / 100))
|
|
[ "$new_max" -lt 5 ] && new_max=5
|
|
[ "$new_max" -gt 150 ] && new_max=150 # Hard cap for shared hosting
|
|
recommended_max_children["$domain"]="$new_max"
|
|
done
|
|
|
|
echo ""
|
|
cecho "${GREEN}✓ Safety caps applied${NC}"
|
|
else
|
|
cecho "${GREEN}✓ Combined capacity is safe: ${total_recommended_memory}MB (${total_recommended_max_children} total max_children)${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
cecho "${CYAN}STEP 3: Applying All Optimizations${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
local opcache_enabled=0
|
|
declare -a changes_log
|
|
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local recommended_max="${recommended_max_children[$domain]}"
|
|
local recommended_mem="${recommended_memory_limit[$domain]}"
|
|
local recommended_requests="${recommended_max_requests[$domain]}"
|
|
|
|
[ -z "$recommended_max" ] && continue
|
|
|
|
# Get pool config
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
cp "$pool_config" "$pool_config.backup.$$" 2>/dev/null
|
|
|
|
local pool_updated=0
|
|
|
|
# Apply FPM settings
|
|
local current_max
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ "$current_max" != "$recommended_max" ]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended_max" >/dev/null 2>&1; then
|
|
pool_updated=$((pool_updated + 1))
|
|
fi
|
|
fi
|
|
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_requests" "$recommended_requests" >/dev/null 2>&1; then
|
|
pool_updated=$((pool_updated + 1))
|
|
fi
|
|
|
|
if [ "$pool_updated" -gt 0 ]; then
|
|
cecho " ${GREEN}✓${NC} $domain: FPM settings optimized"
|
|
cecho " pm.max_children: ${recommended_max} | pm.max_requests: ${recommended_requests}"
|
|
optimized=$((optimized + 1))
|
|
|
|
# Show profile data if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
show_profile_analysis "$domain"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Apply memory_limit
|
|
local ini_files
|
|
ini_files=$(find_php_ini_files "$username" "$domain")
|
|
|
|
while IFS= read -r ini_file; do
|
|
[ -z "$ini_file" ] && continue
|
|
[ ! -f "$ini_file" ] && continue
|
|
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null
|
|
|
|
if modify_php_ini_setting "$ini_file" "memory_limit" "$recommended_mem" >/dev/null 2>&1; then
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
changes_log+=("$domain: memory_limit→$recommended_mem")
|
|
fi
|
|
fi
|
|
|
|
# Enable OPcache if needed
|
|
if [ "${opcache_needs_enable[$domain]}" = "1" ]; then
|
|
if enable_opcache "$ini_file" >/dev/null 2>&1; then
|
|
local avg_rpm
|
|
avg_rpm=$(calculate_avg_requests_per_minute "$username" 24 | cut -d'|' -f1)
|
|
local optimal_opcache_mem
|
|
optimal_opcache_mem=$(calculate_optimal_opcache_memory "$avg_rpm")
|
|
|
|
modify_php_ini_setting "$ini_file" "opcache.memory_consumption" "$optimal_opcache_mem" >/dev/null 2>&1
|
|
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: OPcache enabled (${optimal_opcache_mem})"
|
|
opcache_enabled=$((opcache_enabled + 1))
|
|
fi
|
|
fi
|
|
fi
|
|
done <<< "$ini_files"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Restart PHP-FPM
|
|
echo ""
|
|
cecho "${CYAN}STEP 4: Restarting PHP-FPM${NC}"
|
|
echo ""
|
|
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
# Final Summary
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}LEVEL 5 OPTIMIZATION COMPLETE - COMPREHENSIVE SUMMARY${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " ${GREEN}Domains Optimized: ${optimized}${NC}"
|
|
cecho " ${GREEN}OPcache Enabled: ${opcache_enabled}${NC}"
|
|
cecho " ${GREEN}Changes Applied: ${#changes_log[@]}${NC}"
|
|
echo ""
|
|
cecho "${GREEN}${BOLD}✓ OPTIMIZATION COMPLETE - Server is now fully optimized!${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Key Improvements:${NC}"
|
|
cecho " • PHP-FPM process limits optimized for traffic"
|
|
cecho " • Memory limits set per domain"
|
|
cecho " • Process recycling enabled (pm.max_requests)"
|
|
cecho " • OPcache enabled for performance boost"
|
|
echo ""
|
|
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# OPTION 6: VIEW OPCACHE STATISTICS
|
|
# ============================================================================
|
|
|
|
view_opcache_stats() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "opcache")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}OPCACHE STATISTICS: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Get OPcache stats
|
|
local stats
|
|
stats=$(get_opcache_stats "$username")
|
|
|
|
if [ -z "$stats" ]; then
|
|
cecho "${RED}Unable to retrieve OPcache statistics${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Parse and display
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}OPCACHE STATUS${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
local enabled
|
|
enabled=$(check_opcache_enabled "$username")
|
|
|
|
if [ "$enabled" = "1" ]; then
|
|
cecho " Status: ${GREEN}ENABLED${NC}"
|
|
else
|
|
cecho " Status: ${RED}DISABLED${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Display stats
|
|
while IFS='=' read -r key value; do
|
|
[ -z "$key" ] && continue
|
|
|
|
case "$key" in
|
|
memory_usage_mb)
|
|
cecho " Memory Used: ${WHITE}${value}MB${NC}"
|
|
;;
|
|
hits)
|
|
cecho " Cache Hits: ${GREEN}$value${NC}"
|
|
;;
|
|
misses)
|
|
cecho " Cache Misses: ${YELLOW}$value${NC}"
|
|
;;
|
|
num_cached_scripts)
|
|
cecho " Cached Scripts: ${WHITE}$value${NC}"
|
|
;;
|
|
max_cached_keys)
|
|
cecho " Max Cached Keys: ${WHITE}$value${NC}"
|
|
;;
|
|
wasted_memory_mb)
|
|
cecho " Wasted Memory: ${RED}${value}MB${NC}"
|
|
;;
|
|
esac
|
|
done <<< "$stats"
|
|
|
|
# Calculate and display hit rate
|
|
local hit_rate
|
|
hit_rate=$(calculate_opcache_hit_rate "$username")
|
|
echo ""
|
|
cecho " Hit Rate: ${GREEN}${BOLD}${hit_rate}%${NC}"
|
|
|
|
# Recommendation
|
|
echo ""
|
|
# Check if hit_rate is numeric before using awk
|
|
if [ -n "$hit_rate" ] && [[ "$hit_rate" =~ ^[0-9]+$ ]]; then
|
|
local hit_rate_low=$(awk "BEGIN {print ($hit_rate < 90 ? 1 : 0)}" 2>/dev/null || echo 0)
|
|
if [ "$hit_rate_low" -eq 1 ]; then
|
|
cecho " ${YELLOW}⚠ Hit rate below 90% - Consider increasing opcache.memory_consumption${NC}"
|
|
else
|
|
cecho " ${GREEN}✓ Hit rate is excellent${NC}"
|
|
fi
|
|
else
|
|
cecho " ${YELLOW}⚠ Unable to determine OPcache hit rate${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 7: VIEW PHP-FPM PROCESS STATS
|
|
# ============================================================================
|
|
|
|
view_fpm_stats() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "fpm")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}PHP-FPM PROCESS STATISTICS: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Get process stats
|
|
local memory_stats
|
|
memory_stats=$(calculate_memory_per_process "$username")
|
|
|
|
local avg_kb process_count total_mb
|
|
avg_kb=$(echo "$memory_stats" | cut -d'|' -f1)
|
|
process_count=$(echo "$memory_stats" | cut -d'|' -f2)
|
|
total_mb=$(echo "$memory_stats" | cut -d'|' -f3)
|
|
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}CURRENT RESOURCE USAGE${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
if [ "$process_count" -eq 0 ]; then
|
|
cecho "${YELLOW}No active PHP-FPM processes found${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho " Active Processes: ${WHITE}$process_count${NC}"
|
|
cecho " Avg Memory/Process: ${WHITE}$((avg_kb / 1024))MB${NC} (${avg_kb}KB)"
|
|
cecho " Total Memory: ${WHITE}${total_mb}MB${NC}"
|
|
echo ""
|
|
|
|
# Get pool config
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username")
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho "${WHITE}${BOLD}POOL CONFIGURATION${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
|
|
local pool_settings
|
|
pool_settings=$(parse_fpm_pool_config "$pool_config")
|
|
|
|
# Display key settings
|
|
while IFS='=' read -r key value; do
|
|
[ -z "$key" ] && continue
|
|
cecho " $key: ${WHITE}$value${NC}"
|
|
done <<< "$pool_settings"
|
|
fi
|
|
|
|
# Calculate optimal max_children using improved algorithm
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho "${WHITE}${BOLD}RECOMMENDATION${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
|
|
# Get total system memory for improved calculation
|
|
local total_sys_ram
|
|
total_sys_ram=$(free -m | awk '/^Mem:/ {print $2}')
|
|
|
|
# NEW: Improved algorithm
|
|
local improved_opt
|
|
improved_opt=$(calculate_optimal_php_settings "$username" "$total_sys_ram")
|
|
local improved_max improved_pm improved_min improved_max_spare improved_opt_reason
|
|
improved_max=$(get_field "$improved_opt" 1)
|
|
improved_pm=$(get_field "$improved_opt" 2)
|
|
improved_min=$(get_field "$improved_opt" 3)
|
|
improved_max_spare=$(get_field "$improved_opt" 4)
|
|
improved_opt_reason=$(get_field "$improved_opt" 5)
|
|
|
|
# OLD: Legacy algorithm (for comparison)
|
|
local legacy_result
|
|
legacy_result=$(calculate_optimal_max_children "$username" 1024)
|
|
local legacy_recommended legacy_reason
|
|
legacy_recommended=$(echo "$legacy_result" | cut -d'|' -f1)
|
|
legacy_reason=$(echo "$legacy_result" | cut -d'|' -f2)
|
|
|
|
# Display comparison
|
|
cecho " ${GREEN}${BOLD}Improved Recommendation:${NC}"
|
|
cecho " pm.max_children: ${GREEN}$improved_max${NC}"
|
|
cecho " pm mode: ${GREEN}$improved_pm${NC}"
|
|
cecho " min_spare_servers: ${GREEN}$improved_min${NC}"
|
|
cecho " max_spare_servers: ${GREEN}$improved_max_spare${NC}"
|
|
cecho " Reason: $improved_opt_reason"
|
|
echo ""
|
|
|
|
if [ "$improved_max" -ne "$legacy_recommended" ]; then
|
|
cecho " ${YELLOW}Legacy Recommendation (for reference):${NC}"
|
|
cecho " pm.max_children: ${YELLOW}$legacy_recommended${NC} ($legacy_reason)"
|
|
echo ""
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 8: CHECK FOR ISSUES
|
|
# ============================================================================
|
|
|
|
check_config_issues() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "check")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}CONFIGURATION ISSUES: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Detect issues
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain")
|
|
|
|
# Display by severity
|
|
local has_critical=false
|
|
local has_high=false
|
|
local has_medium=false
|
|
local has_low=false
|
|
|
|
# Check for each severity level
|
|
echo "$issues" | grep -q "CRITICAL" && has_critical=true
|
|
echo "$issues" | grep -q "HIGH" && has_high=true
|
|
echo "$issues" | grep -q "MEDIUM" && has_medium=true
|
|
echo "$issues" | grep -q "LOW" && has_low=true
|
|
|
|
# Display CRITICAL
|
|
if [ "$has_critical" = true ]; then
|
|
cecho "${RED}${BOLD}CRITICAL ISSUES:${NC}"
|
|
echo ""
|
|
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ "$severity" != "CRITICAL" ] && continue
|
|
|
|
cecho " ${RED}●${NC} $message"
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# Display HIGH
|
|
if [ "$has_high" = true ]; then
|
|
cecho "${YELLOW}${BOLD}HIGH PRIORITY ISSUES:${NC}"
|
|
echo ""
|
|
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ "$severity" != "HIGH" ] && continue
|
|
|
|
cecho " ${YELLOW}●${NC} $message"
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# Display MEDIUM
|
|
if [ "$has_medium" = true ]; then
|
|
cecho "${BLUE}${BOLD}MEDIUM PRIORITY ISSUES:${NC}"
|
|
echo ""
|
|
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ "$severity" != "MEDIUM" ] && continue
|
|
|
|
cecho " ${BLUE}●${NC} $message"
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# Display LOW
|
|
if [ "$has_low" = true ]; then
|
|
cecho "${GREEN}${BOLD}LOW PRIORITY ISSUES:${NC}"
|
|
echo ""
|
|
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ "$severity" != "LOW" ] && continue
|
|
|
|
cecho " ${GREEN}●${NC} $message"
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# No issues
|
|
if [ "$has_critical" = false ] && [ "$has_high" = false ] && [ "$has_medium" = false ] && [ "$has_low" = false ]; then
|
|
cecho "${GREEN}${BOLD}✓ No configuration issues detected!${NC}"
|
|
cecho "${GREEN}Configuration appears to be optimal.${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 9: CHECK SERVER MEMORY CAPACITY
|
|
# ============================================================================
|
|
|
|
check_server_memory_capacity() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}SERVER MEMORY CAPACITY CHECK${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This checks if all PHP-FPM pools hitting max_children would cause OOM...${NC}"
|
|
echo ""
|
|
|
|
# Run capacity analysis
|
|
cecho "${CYAN}Analyzing PHP-FPM memory requirements...${NC}"
|
|
echo ""
|
|
|
|
local result
|
|
result=$(calculate_server_memory_capacity 2>/dev/null)
|
|
|
|
# Parse result - first line is summary, remaining lines are details
|
|
local main_result
|
|
main_result=$(echo "$result" | head -1)
|
|
|
|
local total_required total_ram percentage status details
|
|
total_required=$(echo "$main_result" | cut -d'|' -f1)
|
|
total_ram=$(echo "$main_result" | cut -d'|' -f2)
|
|
percentage=$(echo "$main_result" | cut -d'|' -f3)
|
|
status=$(echo "$main_result" | cut -d'|' -f4)
|
|
|
|
# Get details (all lines after first)
|
|
details=$(echo "$result" | tail -n +2)
|
|
|
|
# Display summary
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}MEMORY CAPACITY ANALYSIS${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Total Server RAM: ${WHITE}${total_ram}MB${NC}"
|
|
cecho " Required if ALL pools at max_children: ${WHITE}${total_required}MB${NC}"
|
|
cecho " Percentage of RAM: ${WHITE}${percentage}%${NC}"
|
|
echo ""
|
|
|
|
# Display status with color
|
|
case "$status" in
|
|
CRITICAL)
|
|
cecho " Status: ${RED}${BOLD}CRITICAL - HIGH OOM RISK!${NC}"
|
|
cecho ""
|
|
cecho " ${RED}WARNING: If all PHP-FPM pools hit their max_children limit,${NC}"
|
|
cecho " ${RED}the server will likely run out of memory and kill processes!${NC}"
|
|
;;
|
|
WARNING)
|
|
cecho " Status: ${YELLOW}${BOLD}WARNING - MODERATE OOM RISK${NC}"
|
|
cecho ""
|
|
cecho " ${YELLOW}CAUTION: Memory usage is high. Some pools may need reduction.${NC}"
|
|
;;
|
|
CAUTION)
|
|
cecho " Status: ${YELLOW}${BOLD}CAUTION - WATCH MEMORY USAGE${NC}"
|
|
cecho ""
|
|
cecho " ${YELLOW}Memory usage is elevated but manageable.${NC}"
|
|
;;
|
|
HEALTHY)
|
|
cecho " Status: ${GREEN}${BOLD}HEALTHY - LOW OOM RISK${NC}"
|
|
cecho ""
|
|
cecho " ${GREEN}Memory allocation appears safe.${NC}"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
# Ask if user wants detailed breakdown
|
|
if confirm "Show detailed per-user breakdown?"; then
|
|
echo ""
|
|
cecho "${WHITE}${BOLD}PER-USER BREAKDOWN${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
|
|
printf "%-30s %-20s %12s %12s %12s\n" "DOMAIN" "USER" "MAX_CHILDREN" "AVG/PROCESS" "MAX_MEMORY"
|
|
printf "%-30s %-20s %12s %12s %12s\n" "------------------------------" "--------------------" "------------" "------------" "------------"
|
|
|
|
while IFS='|' read -r domain username max_children avg_mb pool_max_mb; do
|
|
[ -z "$domain" ] && continue
|
|
printf "%-30s %-20s %12s %12s %12s\n" "$domain" "$username" "$max_children" "$avg_mb" "$pool_max_mb"
|
|
done <<< "$details"
|
|
|
|
echo ""
|
|
fi
|
|
|
|
# Ask if user wants balanced recommendations
|
|
echo ""
|
|
|
|
if confirm "Calculate balanced memory allocation recommendations?"; then
|
|
echo ""
|
|
cecho "${WHITE}${BOLD}BALANCED MEMORY ALLOCATION RECOMMENDATIONS${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Calculating optimal max_children based on traffic...${NC}"
|
|
echo ""
|
|
|
|
local recommendations
|
|
recommendations=$(calculate_balanced_memory_allocation 2>/dev/null)
|
|
|
|
# Display header
|
|
local header
|
|
header=$(echo "$recommendations" | head -1)
|
|
echo "$header" | awk -F'|' '{printf "%-15s %8s %10s %12s %12s %15s %20s\n", $1, $2, $3, $4, $5, $6, $7}'
|
|
echo "────────────────────────────────────────────────────────────────────────────────────────────────────────"
|
|
|
|
# Display recommendations
|
|
echo "$recommendations" | tail -n +2 | while IFS='|' read -r user current_max avg_mb traffic_rpm recommended_max allocated_mb reason; do
|
|
[ -z "$user" ] && continue
|
|
|
|
# Color code reason
|
|
if [[ "$reason" == *"REDUCE"* ]]; then
|
|
color="${RED}"
|
|
elif [[ "$reason" == *"INCREASE"* ]]; then
|
|
color="${GREEN}"
|
|
else
|
|
color="${WHITE}"
|
|
fi
|
|
|
|
printf "%-15s %8s %10s %12s ${color}%12s${NC} %15s %20s\n" "$user" "$current_max" "$avg_mb" "$traffic_rpm" "$recommended_max" "$allocated_mb" "$reason"
|
|
done
|
|
|
|
echo ""
|
|
cecho "${YELLOW}NOTE: These are recommendations based on proportional traffic.${NC}"
|
|
cecho "${YELLOW}Actual needs may vary. Always test changes carefully.${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION B: BACKUP CONFIGURATIONS
|
|
# ============================================================================
|
|
|
|
backup_configurations() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}BACKUP PHP CONFIGURATIONS${NC}"
|
|
echo ""
|
|
|
|
# Select domain
|
|
local selection
|
|
selection=$(select_domain "backup")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}BACKUP: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Initialize backup system
|
|
initialize_backup_system
|
|
|
|
# Create backup
|
|
cecho "${YELLOW}Creating backup of PHP configurations...${NC}"
|
|
echo ""
|
|
|
|
local backup_dir
|
|
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
|
|
|
|
if [ $? -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ Backup created successfully!${NC}"
|
|
echo ""
|
|
cecho " Backup location: ${WHITE}$backup_dir${NC}"
|
|
echo ""
|
|
|
|
# Show what was backed up
|
|
if [ -f "$backup_dir/metadata.txt" ]; then
|
|
cecho "${CYAN}Files backed up:${NC}"
|
|
grep "^ /" "$backup_dir/metadata.txt" | while IFS= read -r line; do
|
|
local file=$(echo "$line" | awk '{print $1}')
|
|
cecho " ${GREEN}✓${NC} $file"
|
|
done
|
|
fi
|
|
else
|
|
cecho "${RED}${BOLD}✗ Backup failed${NC}"
|
|
echo ""
|
|
echo "$backup_dir"
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION R: RESTORE FROM BACKUP
|
|
# ============================================================================
|
|
|
|
restore_configurations() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}RESTORE FROM BACKUP${NC}"
|
|
echo ""
|
|
|
|
# List available backups
|
|
cecho "${CYAN}Available backups:${NC}"
|
|
echo ""
|
|
|
|
local backups
|
|
backups=$(list_backups)
|
|
|
|
if [ $? -ne 0 ]; then
|
|
cecho "${YELLOW}No backups found${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Display backups and store backup names in array for selection
|
|
local backup_array=()
|
|
mapfile -t backup_array < <(echo "$backups" | tail -n +2 | cut -d'|' -f1)
|
|
|
|
local index=1
|
|
echo "$backups" | tail -n +2 | while IFS='|' read -r backup_name created username domain file_count; do
|
|
printf "${GREEN}%3d${NC}) %-20s %s ${CYAN}[%s]${NC} ${YELLOW}(%s)${NC} %s files\n" \
|
|
"$index" "$backup_name" "$created" "$username" "$domain" "$file_count"
|
|
index=$((index + 1))
|
|
done
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
# Validate backup selection with retry loop
|
|
while true; do
|
|
read -p "Select backup number to restore (or 'q' to cancel): " selection
|
|
|
|
if [[ "$selection" == "q" ]]; then
|
|
return
|
|
fi
|
|
|
|
if ! [[ "$selection" =~ ^[0-9]+$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt ${#backup_array[@]} ]; then
|
|
echo ""
|
|
cecho "${RED}Invalid selection. Please enter a number 1-${#backup_array[@]}${NC}"
|
|
echo ""
|
|
continue
|
|
fi
|
|
|
|
break
|
|
done
|
|
|
|
local selected_backup="${backup_array[$((selection - 1))]}"
|
|
|
|
# Confirm restoration
|
|
echo ""
|
|
cecho "${YELLOW}${BOLD}WARNING: This will overwrite current configurations!${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Are you sure you want to restore from $selected_backup?"; then
|
|
cecho "${YELLOW}Restore cancelled${NC}"
|
|
sleep 2
|
|
return
|
|
fi
|
|
|
|
# Perform restore
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}RESTORING FROM BACKUP${NC}"
|
|
echo ""
|
|
|
|
if restore_from_backup "$selected_backup"; then
|
|
echo ""
|
|
cecho "${GREEN}${BOLD}✓ Restore completed successfully!${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Don't forget to restart PHP-FPM for changes to take effect!${NC}"
|
|
else
|
|
echo ""
|
|
cecho "${RED}${BOLD}✗ Restore failed${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# MAIN LOOP
|
|
# ============================================================================
|
|
|
|
main() {
|
|
# Detect system
|
|
initialize_system_detection
|
|
|
|
# Check if running as root
|
|
if [ "$EUID" -ne 0 ]; then
|
|
cecho "${RED}ERROR: This script must be run as root${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Check for PHP-FPM
|
|
if ! is_using_php_fpm; then
|
|
cecho "${YELLOW}WARNING: PHP-FPM not detected. Some features may not work.${NC}"
|
|
read -p "Press Enter to continue anyway..."
|
|
fi
|
|
|
|
# Main loop
|
|
while true; do
|
|
show_banner
|
|
show_main_menu
|
|
|
|
# Validate choice input with retry loop
|
|
while true; do
|
|
read -p "Select option (0-9, b, r): " choice
|
|
|
|
if ! [[ "$choice" =~ ^([0-9]|[bBrR])$ ]]; then
|
|
echo ""
|
|
cecho "${RED}Invalid choice. Please enter 0-9, b, or r${NC}"
|
|
echo ""
|
|
continue
|
|
fi
|
|
break
|
|
done
|
|
|
|
# Convert uppercase to lowercase for case statement
|
|
choice=${choice,,}
|
|
|
|
case "$choice" in
|
|
1)
|
|
analyze_single_domain
|
|
;;
|
|
2)
|
|
analyze_all_domains
|
|
;;
|
|
3)
|
|
quick_health_check
|
|
;;
|
|
4)
|
|
optimize_domain
|
|
;;
|
|
5)
|
|
optimize_all_domains
|
|
;;
|
|
6)
|
|
view_opcache_stats
|
|
;;
|
|
7)
|
|
view_fpm_stats
|
|
;;
|
|
8)
|
|
check_config_issues
|
|
;;
|
|
9)
|
|
check_server_memory_capacity
|
|
;;
|
|
b)
|
|
backup_configurations
|
|
;;
|
|
r)
|
|
restore_configurations
|
|
;;
|
|
0)
|
|
cecho "${GREEN}Exiting PHP Optimizer...${NC}"
|
|
exit 0
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Run main
|
|
main "$@"
|