#!/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 Recommendations${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 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 - use profile if available, otherwise use traffic-based local recommended_max local recommended_memory local recommended_requests if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then recommended_max=$(get_max_children_recommendation "$domain" "$username") recommended_memory=$(get_memory_limit_recommendation "$domain" "$username") recommended_requests=$(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=$((traffic_rpm > 5 ? traffic_rpm + 10 : 5)) recommended_memory=$(calculate_optimal_memory_limit "$username" "$domain" "$traffic_rpm") recommended_requests=$(calculate_optimal_max_requests "$traffic_rpm") 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 "$@"