diff --git a/docs/PHP_METRICS_COMPREHENSIVE.md b/docs/PHP_METRICS_COMPREHENSIVE.md new file mode 100644 index 0000000..eb3a6ea --- /dev/null +++ b/docs/PHP_METRICS_COMPREHENSIVE.md @@ -0,0 +1,469 @@ +# Comprehensive PHP Metrics Tracking Guide + +## PHP Configuration Hierarchy & Detection + +### Configuration File Priority (Highest to Lowest) +Understanding which config takes effect is critical for accurate optimization. + +``` +1. .user.ini (per-directory, PHP-FPM only) + Location: /home/user/public_html/.user.ini + Scope: Specific directory and subdirectories + Reloads: Automatically every user_ini.cache_ttl seconds (default 300) + +2. .htaccess (Apache with mod_php only, NOT PHP-FPM!) + Location: /home/user/public_html/.htaccess + Scope: Directory-specific + Note: Does NOT work with PHP-FPM! + +3. php.ini (per-pool, cPanel EA-PHP) + Location: /opt/cpanel/ea-php*/root/etc/php.ini + Scope: All domains using that PHP version + +4. Additional .ini files (per-pool) + Location: /opt/cpanel/ea-php*/root/etc/php.d/*.ini + Scope: Per PHP version, loaded alphabetically + +5. Global php.ini + Location: /etc/php.ini (legacy) + Scope: System-wide fallback +``` + +### How to Determine Effective Settings + +**Method 1: Query via PHP (Most Accurate)** +```bash +# Get effective value for specific domain +echo '' | \ + su -s /bin/bash $username -c "php -q -d open_basedir=" + +# Get ALL effective settings +php -r 'print_r(ini_get_all());' > /tmp/php_all_settings.txt + +# Per-domain via web request (if domain is accessible) +curl -s "http://$domain/phpinfo.php" | grep -A1 "memory_limit" +``` + +**Method 2: Parse Configuration Files** +```bash +# Find ALL possible config files affecting a domain +find_php_configs() { + local domain="$1" + local user="$2" + local php_version="$3" # e.g., "ea-php82" + + # Priority order + echo "=== Config Hierarchy for $domain ===" + + # 1. .user.ini + local user_ini="/home/$user/public_html/.user.ini" + if [ -f "$user_ini" ]; then + echo "1. .user.ini: $user_ini (HIGHEST PRIORITY)" + grep -E "memory_limit|max_execution_time|upload_max_filesize" "$user_ini" + fi + + # 2. Pool-specific php.ini + local pool_ini="/opt/cpanel/$php_version/root/etc/php.ini" + if [ -f "$pool_ini" ]; then + echo "2. Pool php.ini: $pool_ini" + grep -E "memory_limit|max_execution_time|upload_max_filesize" "$pool_ini" + fi + + # 3. Additional .ini files + local ini_dir="/opt/cpanel/$php_version/root/etc/php.d" + if [ -d "$ini_dir" ]; then + echo "3. Additional .ini files: $ini_dir/*.ini" + grep -h -E "memory_limit|max_execution_time|upload_max_filesize" "$ini_dir"/*.ini 2>/dev/null + fi +} +``` + +## Complete PHP Metrics to Track + +### 1. **Memory Settings** (Critical for Performance) + +```ini +# Basic Memory +memory_limit = 256M # Per-script memory limit + # Track: Current value, recommended, % of total RAM + +# Upload Limits (Related to Memory) +upload_max_filesize = 64M # Max single file upload +post_max_size = 128M # Max POST data (should be >= upload_max_filesize) +max_input_vars = 1000 # Max input variables (forms with many fields) +max_input_nesting_level = 64 # Max array nesting depth +max_input_time = 60 # Max time parsing input data + +# Realpath Cache (Memory for path resolution) +realpath_cache_size = 4096K # Cache size for realpath() calls +realpath_cache_ttl = 120 # TTL in seconds +``` + +**Why Track:** +- `memory_limit` too low → "Allowed memory size exhausted" errors +- `post_max_size < upload_max_filesize` → Upload failures +- `realpath_cache_size` too small → File I/O slowdowns + +**Detection:** +```bash +# Find memory exhausted errors +grep -r "Allowed memory size.*exhausted" /home/$user/*/logs/error_log + +# Find upload failures +grep -r "POST Content-Length.*exceeds" /home/$user/*/logs/error_log +``` + +### 2. **Execution & Timeout Settings** + +```ini +# Script Execution +max_execution_time = 30 # Max script runtime (seconds) +max_input_time = 60 # Max time for input parsing +default_socket_timeout = 60 # Default socket timeout + +# CGI-specific +cgi.force_redirect = 1 +cgi.fix_pathinfo = 0 # Security: prevent path injection +``` + +**Why Track:** +- `max_execution_time` too low → Scripts timeout on slow operations +- Long-running cron jobs need higher limits + +**Detection:** +```bash +# Find timeout errors +grep -r "Maximum execution time.*exceeded" /home/$user/*/logs/error_log +``` + +### 3. **PHP-FPM Pool Settings** (Most Critical for Optimization!) + +```ini +# Process Manager Type +pm = dynamic # static | dynamic | ondemand + # static: Fixed number of children + # dynamic: Scales between min/max + # ondemand: Spawns on-demand (saves memory) + +# Process Limits (DYNAMIC mode) +pm.max_children = 50 # Max simultaneous processes +pm.start_servers = 5 # Processes started at boot +pm.min_spare_servers = 5 # Minimum idle processes +pm.max_spare_servers = 35 # Maximum idle processes + +# Process Limits (STATIC mode) +pm.max_children = 50 # Fixed number of processes + +# Process Limits (ONDEMAND mode) +pm.max_children = 50 # Max processes +pm.process_idle_timeout = 10s # Kill idle process after X seconds + +# Process Recycling +pm.max_requests = 500 # Respawn after X requests (prevent memory leaks) + +# Status & Monitoring +pm.status_path = /fpm-status # Status page URL +ping.path = /fpm-ping # Health check URL +ping.response = pong + +# Timeouts +request_terminate_timeout = 30s # Kill request after X seconds (0 = disabled) +request_slowlog_timeout = 5s # Log slow requests taking > X seconds + +# Logging +slowlog = /var/log/php-fpm/$pool-slow.log +catch_workers_output = yes # Capture stdout/stderr +php_admin_value[error_log] = /var/log/php-fpm/$pool-error.log +``` + +**Why Track (CRITICAL!):** +- `pm.max_children` too low → "server reached pm.max_children" errors → requests queue/fail +- `pm.max_children` too high → OOM kills, server crashes +- `pm = static` wastes memory on low-traffic sites +- `pm = ondemand` adds latency (process spawn time) +- `pm.max_requests = 0` → memory leaks never cleared + +**Detection:** +```bash +# Find max_children errors (CRITICAL) +grep "server reached pm.max_children" /opt/cpanel/ea-php*/root/usr/var/log/php-fpm/*error.log + +# Find slow requests +tail -100 /opt/cpanel/ea-php*/root/usr/var/log/php-fpm/*slow.log + +# Current process count vs limit +current=$(ps aux | grep "php-fpm: pool $domain" | grep -v grep | wc -l) +max=$(grep "pm.max_children" /opt/cpanel/ea-php*/root/etc/php-fpm.d/$user.conf | cut -d'=' -f2) +echo "Current: $current / Max: $max" +``` + +### 4. **OPcache Settings** (Massive Performance Impact!) + +```ini +[opcache] +; Enable/Disable +opcache.enable = 1 # Enable opcache +opcache.enable_cli = 0 # Disable for CLI (causes issues) + +; Memory Settings +opcache.memory_consumption = 128 # MB for opcache (CRITICAL!) +opcache.interned_strings_buffer = 8 # MB for string interning +opcache.max_accelerated_files = 10000 # Max cached files (set to > total PHP files) + +; Validation & Updates +opcache.revalidate_freq = 2 # Check file changes every X seconds (0 = always check) +opcache.validate_timestamps = 1 # Check if files changed (0 = never check, production) +opcache.fast_shutdown = 1 # Faster shutdown + +; Advanced +opcache.enable_file_override = 1 # Optimize file_exists(), is_file() +opcache.optimization_level = 0x7FFFBFFF +opcache.save_comments = 1 # Required for some frameworks (Doctrine, Symfony) +opcache.load_comments = 1 + +; JIT (PHP 8.0+) +opcache.jit = tracing # off | function | tracing +opcache.jit_buffer_size = 100M # JIT compilation buffer +``` + +**Why Track (HUGE PERFORMANCE!):** +- Opcache disabled → 40-70% slower, 300% more CPU +- `opcache.memory_consumption` too small → Cache thrashing +- `opcache.max_accelerated_files` too low → Not all files cached +- Hit rate < 90% → Increase memory or max files + +**Detection:** +```bash +# Get opcache status (MOST IMPORTANT METRICS!) +php -r "print_r(opcache_get_status());" | grep -E "opcache_enabled|memory_usage|opcache_statistics|num_cached_scripts|hits|misses|blacklist_misses" + +# Calculate hit rate +stats=$(php -r '$s=opcache_get_status(); echo $s["opcache_statistics"]["hits"].",".$s["opcache_statistics"]["misses"];') +hits=$(echo $stats | cut -d',' -f1) +misses=$(echo $stats | cut -d',' -f2) +total=$((hits + misses)) +hit_rate=$((hits * 100 / total)) +echo "Opcache Hit Rate: ${hit_rate}%" + +# If hit rate < 90% → Need more memory or max_files! +``` + +### 5. **Session Settings** + +```ini +session.save_handler = files # files | memcached | redis +session.save_path = "/var/lib/php/session" +session.gc_maxlifetime = 1440 # Session timeout (seconds) +session.gc_probability = 1 +session.gc_divisor = 1000 # GC runs 1/1000 requests +session.cookie_lifetime = 0 # Session cookie expires on browser close +``` + +**Why Track:** +- `session.save_path` full disk → Session writes fail +- Using `files` on high-traffic → I/O bottleneck (use Redis!) + +### 6. **Error Handling & Logging** + +```ini +error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT +display_errors = Off # CRITICAL: Must be Off in production! +display_startup_errors = Off +log_errors = On # Log to file +error_log = /home/$user/logs/php_error.log +ignore_repeated_errors = Off +ignore_repeated_source = Off +report_memleaks = On +``` + +**Why Track:** +- `display_errors = On` in production → Security risk (exposes paths) +- No `error_log` set → Errors go to Apache log (harder to track) + +### 7. **Security Settings** + +```ini +; Disable Dangerous Functions +disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source + +; Open Basedir (Restrict File Access) +open_basedir = /home/$user:/tmp # Prevent directory traversal + +; File Uploads +file_uploads = On +upload_tmp_dir = /tmp # Temp upload directory + +; Misc Security +expose_php = Off # Hide PHP version in headers +allow_url_fopen = On # Allow remote file access (needed for many apps) +allow_url_include = Off # CRITICAL: Prevent remote code execution +``` + +### 8. **APCu Cache** (User Cache, separate from OPcache) + +```ini +[apcu] +apc.enabled = 1 +apc.shm_size = 32M # Shared memory size +apc.ttl = 7200 # Time to live +apc.gc_ttl = 3600 # Garbage collection TTL +apc.enable_cli = 0 +``` + +**Why Track:** +- WordPress object cache, WooCommerce, etc. use APCu +- Low hit rate → Increase shm_size + +### 9. **MySQL/Database Settings** (php.ini side) + +```ini +mysqli.max_persistent = -1 # Max persistent connections (-1 = unlimited) +mysqli.max_links = -1 # Max total connections +mysqli.default_socket = /var/lib/mysql/mysql.sock +pdo_mysql.default_socket = /var/lib/mysql/mysql.sock +``` + +### 10. **Zend Extensions** + +```ini +zend_extension=opcache.so +zend_extension=ioncube_loader_lin_8.2.so # If using IonCube +``` + +## Complete Metrics Tracking List + +### Per-Domain Tracking Matrix + +```yaml +domain: example.com +user: examplec +php_version: ea-php82 + +config_hierarchy: + 1_user_ini: /home/examplec/public_html/.user.ini + 2_pool_ini: /opt/cpanel/ea-php82/root/etc/php.ini + 3_pool_d: /opt/cpanel/ea-php82/root/etc/php.d/ + 4_global: /etc/php.ini + +effective_settings: + # Memory + memory_limit: 256M + upload_max_filesize: 64M + post_max_size: 128M + max_input_vars: 1000 + realpath_cache_size: 4096K + + # Execution + max_execution_time: 30 + max_input_time: 60 + request_terminate_timeout: 30 + + # PHP-FPM Pool + pm: dynamic + pm.max_children: 50 + pm.start_servers: 5 + pm.min_spare_servers: 5 + pm.max_spare_servers: 35 + pm.max_requests: 500 + pm.process_idle_timeout: 10s + + # OPcache + opcache.enable: 1 + opcache.memory_consumption: 128M + opcache.max_accelerated_files: 10000 + opcache.jit: tracing + opcache.jit_buffer_size: 100M + + # Sessions + session.save_handler: redis + session.save_path: "tcp://127.0.0.1:6379" + + # Security + display_errors: Off + open_basedir: /home/examplec:/tmp + disable_functions: exec,passthru,shell_exec + +live_metrics: + # Process Stats + current_processes: 12 + avg_memory_per_process: 45MB + total_memory_usage: 540MB + cpu_usage: 15% + + # OPcache Stats + opcache_hit_rate: 95.3% + opcache_memory_used: 87MB / 128MB + opcache_cached_scripts: 2847 / 10000 + opcache_wasted_memory: 2.1MB + + # Traffic Stats (last 24h) + peak_concurrent_requests: 18 + avg_requests_per_minute: 45 + total_requests: 64,800 + + # Error Stats (last 7 days) + memory_exhausted: 0 + max_execution_time: 3 + max_children_reached: 47 # CRITICAL! + slow_requests: 12 + +issues_detected: + - type: CRITICAL + code: MAX_CHILDREN_REACHED + count: 47 + message: "pm.max_children limit hit 47 times in 7 days" + recommendation: "Increase from 50 to 75" + + - type: WARNING + code: SLOW_REQUESTS + count: 12 + message: "12 requests took > 5 seconds" + recommendation: "Review slow log, optimize code" + +recommendations: + - priority: HIGH + setting: pm.max_children + current: 50 + recommended: 75 + reason: "Peak concurrent (18) + buffer (50%) + safety margin" + impact: "Handle 75 concurrent PHP requests vs 50" + memory_impact: +1.1GB + + - priority: MEDIUM + setting: opcache.max_accelerated_files + current: 10000 + recommended: 15000 + reason: "Currently caching 2847 files, room for growth" + impact: "Better cache coverage as site grows" +``` + +## Detection Commands Cheat Sheet + +```bash +# Find ALL php.ini files affecting a domain +find /opt/cpanel/ea-php*/root/etc/ -name "php.ini" +find /home/$user/public_html -name ".user.ini" + +# Find FPM pool config +grep -r "pool.*$domain" /opt/cpanel/ea-php*/root/etc/php-fpm.d/ + +# Get effective settings for domain +su -s /bin/bash $user -c "php -r 'phpinfo();'" | grep -A1 "memory_limit" + +# Check opcache status +php -r "var_dump(opcache_get_status());" + +# Find max_children errors +grep -r "max_children" /opt/cpanel/ea-php*/root/usr/var/log/php-fpm/ + +# Find slow requests +find /opt/cpanel/ea-php*/root/usr/var/log/php-fpm/ -name "*slow.log" -exec tail -50 {} \; + +# Count current FPM processes +ps aux | grep "php-fpm: pool $domain" | wc -l + +# Memory per process +ps aux | grep "php-fpm: pool $domain" | awk '{sum+=$6} END {print sum/NR " KB avg per process"}' +``` + +This comprehensive tracking will allow us to build an intelligent optimizer that knows EXACTLY what to fix!