#!/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; } # 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}q${NC}) Quit" echo "" cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" } # ============================================================================ # DOMAIN SELECTION # ============================================================================ select_domain() { local action="${1:-analyze}" cecho "${WHITE}${BOLD}SELECT DOMAIN${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 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 # Display numbered list cecho "${CYAN}Available domains:${NC}" echo "" local index=1 for domain in "${domains[@]}"; do local username="${domain_to_user[$domain]}" local php_version php_version=$(detect_php_version_for_domain "$domain" 2>/dev/null || echo "unknown") printf " ${GREEN}%-3d${NC}) %-40s ${CYAN}[${username}]${NC} ${YELLOW}(${php_version})${NC}\n" "$index" "$domain" index=$((index + 1)) done echo "" 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 ${#domains[@]} ]; then cecho "${RED}Invalid selection${NC}" sleep 2 return 1 fi # Return selected domain and username local selected_domain="${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 analyze PHP configuration for ALL domains...${NC}" echo "" read -p "Continue? (y/n): " confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then return fi show_banner cecho "${WHITE}${BOLD}Analyzing all domains...${NC}" echo "" # Get all users local users users=$(list_all_users) local total_domains=0 local domains_with_issues=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)) cecho "${CYAN}[$total_domains] Analyzing: ${WHITE}$domain${NC}" # Detect issues local issues issues=$(detect_php_config_issues "$username" "$domain") # Count issues by severity local critical_count high_count medium_count low_count critical_count=$(echo "$issues" | grep -c "^[^|]*|CRITICAL|" || true) high_count=$(echo "$issues" | grep -c "^[^|]*|HIGH|" || true) medium_count=$(echo "$issues" | grep -c "^[^|]*|MEDIUM|" || true) low_count=$(echo "$issues" | grep -c "^[^|]*|LOW|" || true) # Default to 0 if empty critical_count=${critical_count:-0} high_count=${high_count:-0} medium_count=${medium_count:-0} low_count=${low_count:-0} local total_issues=$((critical_count + high_count + medium_count + low_count)) if [ "$total_issues" -gt 0 ]; then domains_with_issues=$((domains_with_issues + 1)) # Build issue summary local issue_summary="" [ "$critical_count" -gt 0 ] && issue_summary+="${RED}$critical_count CRITICAL${NC}, " [ "$high_count" -gt 0 ] && issue_summary+="${YELLOW}$high_count HIGH${NC}, " [ "$medium_count" -gt 0 ] && issue_summary+="$medium_count MEDIUM, " [ "$low_count" -gt 0 ] && issue_summary+="$low_count LOW, " issue_summary=${issue_summary%, } # Remove trailing comma cecho " ${RED}✗${NC} Issues found: $issue_summary" # Show critical/high issues while IFS='|' read -r issue_type severity message recommendation; do [ -z "$issue_type" ] && continue if [[ "$severity" == "CRITICAL" ]] || [[ "$severity" == "HIGH" ]]; then cecho " ${RED}[$severity]${NC} $issue_type: $message" fi done <<< "$issues" # Show what passed local checks_passed="" echo "$issues" | grep -q "max_children" || checks_passed+="max_children OK, " echo "$issues" | grep -q "memory_exhausted" || checks_passed+="memory OK, " echo "$issues" | grep -q "execution timeout" || checks_passed+="timeouts OK, " if [ -n "$checks_passed" ]; then checks_passed=${checks_passed%, } cecho " ${GREEN}✓${NC} Checks passed: $checks_passed" fi else cecho " ${GREEN}✓ All checks passed${NC} (max_children, memory, timeouts, config)" fi echo "" done <<< "$user_domains" done <<< "$users" # Summary cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" cecho "${WHITE}${BOLD}SUMMARY${NC}" cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" echo "" cecho " Total domains analyzed: ${WHITE}$total_domains${NC}" cecho " Domains with issues: ${RED}$domains_with_issues${NC}" cecho " Domains healthy: ${GREEN}$((total_domains - domains_with_issues))${NC}" echo "" cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" read -p "Press Enter to continue..." } # ============================================================================ # 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 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 # Get optimization recommendations cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" cecho "${WHITE}${BOLD}RECOMMENDED OPTIMIZATIONS${NC}" echo "" # Calculate optimal max_children local optimal_result optimal_result=$(calculate_optimal_max_children "$username" 1024) local recommended_max_children reason recommended_max_children=$(echo "$optimal_result" | cut -d'|' -f1) reason=$(echo "$optimal_result" | cut -d'|' -f2) # Get current max_children local pool_config pool_config=$(find_fpm_pool_config "$username") if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then local current_max_children current_max_children=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ') if [ -n "$current_max_children" ] && [ "$recommended_max_children" -ne "$current_max_children" ]; then cecho "${GREEN}1.${NC} Adjust ${BOLD}pm.max_children${NC} from ${RED}$current_max_children${NC} to ${GREEN}$recommended_max_children${NC}" cecho " Reason: $reason" echo "" fi fi # Check OPcache local opcache_status opcache_status=$(analyze_opcache_effectiveness "$username") local status hit_rate opcache_rec status=$(echo "$opcache_status" | cut -d'|' -f1) hit_rate=$(echo "$opcache_status" | cut -d'|' -f2) opcache_rec=$(echo "$opcache_status" | cut -d'|' -f5) if [ "$status" = "DISABLED" ]; then cecho "${GREEN}2.${NC} ${BOLD}Enable OPcache${NC} for 40-70% performance boost" echo "" elif (( $(echo "$hit_rate < 90" | bc -l 2>/dev/null || echo "0") )); then cecho "${GREEN}2.${NC} ${BOLD}Increase opcache.memory_consumption${NC} (current hit rate: ${hit_rate}%)" echo "" fi echo "" cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" echo "" # Ask if user wants to apply changes read -p "Apply these recommendations? (y/n): " apply_choice if [[ ! "$apply_choice" =~ ^[Yy]$ ]]; then cecho "${YELLOW}Optimization cancelled - no changes made${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 optimizations...${NC}" echo "" local changes_made=0 local changes_failed=0 # Find pool config local pool_config pool_config=$(find_fpm_pool_config "$username") if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then # Apply max_children change if recommended if [ -n "$recommended_max_children" ] && [ "$recommended_max_children" -ne "$current_max_children" ]; then if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended_max_children" >/dev/null 2>&1; then cecho " ${GREEN}✓${NC} Set pm.max_children = $recommended_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 echo "" cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" if [ "$changes_made" -gt 0 ]; then 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 "" read -p "Restart PHP-FPM now? (y/n): " restart_choice if [[ "$restart_choice" =~ ^[Yy]$ ]]; then # Detect PHP version local php_version php_version=$(detect_php_version_for_domain "$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 successfully${NC}" # Verify it's running sleep 1 if verify_php_fpm_running "$php_version" >/dev/null 2>&1; then cecho "${GREEN}✓ PHP-FPM is running${NC}" else cecho "${RED}✗ WARNING: PHP-FPM may not be running!${NC}" cecho "${YELLOW}Run: systemctl status ea-php${php_version#ea-php}-php-fpm${NC}" fi else cecho "${RED}✗ Failed to restart PHP-FPM${NC}" cecho "${YELLOW}You may need to restart manually:${NC}" cecho "${YELLOW}systemctl restart ea-php${php_version#ea-php}-php-fpm${NC}" 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..." } # ============================================================================ # 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 "" if (( $(echo "$hit_rate < 90" | bc -l 2>/dev/null || echo "0") )); then cecho " ${YELLOW}⚠ Hit rate below 90% - Consider increasing opcache.memory_consumption${NC}" else cecho " ${GREEN}✓ Hit rate is excellent${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 echo "" cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" cecho "${WHITE}${BOLD}RECOMMENDATION${NC}" cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" echo "" local optimal_result optimal_result=$(calculate_optimal_max_children "$username" 1024) local recommended reason recommended=$(echo "$optimal_result" | cut -d'|' -f1) reason=$(echo "$optimal_result" | cut -d'|' -f2) cecho " Optimal pm.max_children: ${GREEN}${BOLD}$recommended${NC}" cecho " Reason: $reason" 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>&1) # Parse result (main output is last line) local main_result main_result=$(echo "$result" | tail -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) # 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 read -p "Show detailed per-user breakdown? (y/n): " show_details if [[ "$show_details" =~ ^[Yy]$ ]]; then echo "" cecho "${WHITE}${BOLD}PER-USER BREAKDOWN${NC}" cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}" echo "" # Get details from stderr of previous call local details_output details_output=$(echo "$result" | head -n -1) printf "%-20s %12s %12s %12s\n" "USER" "MAX_CHILDREN" "AVG/PROCESS" "MAX_MEMORY" printf "%-20s %12s %12s %12s\n" "--------------------" "------------" "------------" "------------" while IFS='|' read -r username max_children avg_mb pool_max_mb; do [ -z "$username" ] && continue printf "%-20s %12s %12s %12s\n" "$username" "$max_children" "$avg_mb" "$pool_max_mb" done <<< "$details_output" echo "" fi # Ask if user wants balanced recommendations echo "" read -p "Calculate balanced memory allocation recommendations? (y/n): " show_recommendations if [[ "$show_recommendations" =~ ^[Yy]$ ]]; 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 local backup_array=() 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" backup_array+=("$backup_name") index=$((index + 1)) done # Store backup names in array for selection mapfile -t backup_array < <(echo "$backups" | tail -n +2 | cut -d'|' -f1) echo "" 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 cecho "${RED}Invalid selection${NC}" sleep 2 return fi local selected_backup="${backup_array[$((selection - 1))]}" # Confirm restoration echo "" cecho "${YELLOW}${BOLD}WARNING: This will overwrite current configurations!${NC}" echo "" read -p "Are you sure you want to restore from $selected_backup? (yes/no): " confirm if [[ "$confirm" != "yes" ]]; 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 read -p "Select option: " choice case "$choice" in 1) analyze_single_domain ;; 2) analyze_all_domains ;; 3) quick_health_check ;; 4) optimize_domain ;; 5) cecho "${YELLOW}Server-wide optimization not yet implemented${NC}" sleep 2 ;; 6) view_opcache_stats ;; 7) view_fpm_stats ;; 8) check_config_issues ;; 9) check_server_memory_capacity ;; b) backup_configurations ;; r) restore_configurations ;; q|Q) cecho "${GREEN}Exiting PHP Optimizer...${NC}" exit 0 ;; *) cecho "${RED}Invalid option${NC}" sleep 1 ;; esac done } # Run main main "$@"