PhpRuntimeConfig.java

package com.cloudforge.core.interfaces;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * PHP runtime configuration for CMS deployments.
 *
 * <p>This record encapsulates all PHP-related configuration including:</p>
 * <ul>
 *   <li>PHP version and extensions</li>
 *   <li>PHP-FPM pool settings</li>
 *   <li>OPcache configuration</li>
 *   <li>Memory and execution limits</li>
 *   <li>Upload size limits</li>
 * </ul>
 *
 * <h2>Usage Example:</h2>
 * <pre>{@code
 * PhpRuntimeConfig config = PhpRuntimeConfig.defaults()
 *     .withVersion("8.2")
 *     .withMemoryLimit(512)
 *     .withExtensions(List.of("mysqli", "gd", "redis"));
 * }</pre>
 *
 * @param version PHP version (e.g., "8.2", "8.1")
 * @param extensions List of required PHP extensions
 * @param phpIni Custom php.ini settings
 * @param fpmConfig PHP-FPM pool configuration
 * @param opcacheConfig OPcache settings
 * @param memoryLimit PHP memory_limit in MB
 * @param maxExecutionTime max_execution_time in seconds
 * @param uploadMaxFilesize upload_max_filesize in MB
 * @param postMaxSize post_max_size in MB
 *
 * @since 3.1.0
 */
public record PhpRuntimeConfig(
    String version,
    List<String> extensions,
    Map<String, String> phpIni,
    Map<String, String> fpmConfig,
    Map<String, String> opcacheConfig,
    int memoryLimit,
    int maxExecutionTime,
    int uploadMaxFilesize,
    int postMaxSize
) {

    /**
     * Common PHP extensions required by most CMS platforms.
     */
    public static final List<String> COMMON_EXTENSIONS = List.of(
        "mysqli",
        "pdo_mysql",
        "gd",
        "curl",
        "mbstring",
        "xml",
        "dom",
        "zip",
        "intl",
        "soap",
        "bcmath",
        "opcache",
        "json",
        "fileinfo",
        "exif"
    );

    /**
     * Extensions for image processing (beyond basic GD).
     */
    public static final List<String> IMAGE_EXTENSIONS = List.of(
        "gd",
        "imagick"
    );

    /**
     * Extensions for caching backends.
     */
    public static final List<String> CACHE_EXTENSIONS = List.of(
        "redis",
        "memcached",
        "apcu"
    );

    /**
     * Extensions for e-commerce platforms (encryption, math).
     */
    public static final List<String> ECOMMERCE_EXTENSIONS = List.of(
        "gmp",
        "sodium",
        "openssl"
    );

    /**
     * Creates a default PHP runtime configuration suitable for most CMS platforms.
     *
     * <p>Default values:</p>
     * <ul>
     *   <li>PHP 8.2</li>
     *   <li>Common extensions + redis</li>
     *   <li>256MB memory limit</li>
     *   <li>300 second max execution time</li>
     *   <li>64MB upload limit</li>
     * </ul>
     *
     * @return default PhpRuntimeConfig
     */
    public static PhpRuntimeConfig defaults() {
        List<String> extensions = new ArrayList<>(COMMON_EXTENSIONS);
        extensions.add("redis");

        return new PhpRuntimeConfig(
            "8.2",
            extensions,
            Map.of(),
            Map.of(
                "pm", "dynamic",
                "pm.max_children", "50",
                "pm.start_servers", "5",
                "pm.min_spare_servers", "5",
                "pm.max_spare_servers", "35",
                "pm.max_requests", "500"
            ),
            Map.of(
                "opcache.enable", "1",
                "opcache.memory_consumption", "128",
                "opcache.interned_strings_buffer", "16",
                "opcache.max_accelerated_files", "10000",
                "opcache.revalidate_freq", "60",
                "opcache.fast_shutdown", "1",
                "opcache.enable_cli", "0"
            ),
            256,  // memory_limit MB
            300,  // max_execution_time seconds
            64,   // upload_max_filesize MB
            64    // post_max_size MB
        );
    }

    /**
     * Creates a configuration optimized for WordPress.
     *
     * @return WordPress-optimized PhpRuntimeConfig
     */
    public static PhpRuntimeConfig forWordPress() {
        List<String> extensions = new ArrayList<>(COMMON_EXTENSIONS);
        extensions.addAll(List.of("redis", "imagick"));

        return defaults()
            .withExtensions(extensions)
            .withMemoryLimit(256)
            .withUploadMaxFilesize(64)
            .withPostMaxSize(64);
    }

    /**
     * Creates a configuration optimized for WooCommerce/e-commerce.
     *
     * @return E-commerce optimized PhpRuntimeConfig
     */
    public static PhpRuntimeConfig forEcommerce() {
        List<String> extensions = new ArrayList<>(COMMON_EXTENSIONS);
        extensions.addAll(CACHE_EXTENSIONS);
        extensions.addAll(ECOMMERCE_EXTENSIONS);
        extensions.addAll(IMAGE_EXTENSIONS);

        Map<String, String> fpm = new HashMap<>();
        fpm.put("pm", "dynamic");
        fpm.put("pm.max_children", "100");
        fpm.put("pm.start_servers", "10");
        fpm.put("pm.min_spare_servers", "10");
        fpm.put("pm.max_spare_servers", "50");
        fpm.put("pm.max_requests", "500");

        return new PhpRuntimeConfig(
            "8.2",
            extensions,
            Map.of(),
            fpm,
            defaults().opcacheConfig(),
            512,  // memory_limit MB (higher for e-commerce)
            600,  // max_execution_time seconds (longer for checkouts)
            128,  // upload_max_filesize MB (product images)
            128   // post_max_size MB
        );
    }

    /**
     * Creates a configuration optimized for Magento 2.
     *
     * @return Magento-optimized PhpRuntimeConfig
     */
    public static PhpRuntimeConfig forMagento() {
        List<String> extensions = new ArrayList<>(COMMON_EXTENSIONS);
        extensions.addAll(List.of(
            "redis", "imagick", "gmp", "sodium", "sockets",
            "xsl", "pdo_mysql", "openswoole"
        ));

        Map<String, String> fpm = new HashMap<>();
        fpm.put("pm", "dynamic");
        fpm.put("pm.max_children", "150");
        fpm.put("pm.start_servers", "20");
        fpm.put("pm.min_spare_servers", "10");
        fpm.put("pm.max_spare_servers", "75");
        fpm.put("pm.max_requests", "1000");

        Map<String, String> opcache = new HashMap<>();
        opcache.put("opcache.enable", "1");
        opcache.put("opcache.memory_consumption", "512");
        opcache.put("opcache.interned_strings_buffer", "32");
        opcache.put("opcache.max_accelerated_files", "60000");
        opcache.put("opcache.revalidate_freq", "0");  // Don't check for file changes
        opcache.put("opcache.validate_timestamps", "0");
        opcache.put("opcache.save_comments", "1");
        opcache.put("opcache.fast_shutdown", "1");

        return new PhpRuntimeConfig(
            "8.2",
            extensions,
            Map.of(
                "realpath_cache_size", "10M",
                "realpath_cache_ttl", "7200"
            ),
            fpm,
            opcache,
            2048,  // memory_limit MB (Magento is memory-hungry)
            900,   // max_execution_time seconds
            256,   // upload_max_filesize MB
            256    // post_max_size MB
        );
    }

    /**
     * Creates a configuration for Drupal.
     *
     * @return Drupal-optimized PhpRuntimeConfig
     */
    public static PhpRuntimeConfig forDrupal() {
        List<String> extensions = new ArrayList<>(COMMON_EXTENSIONS);
        extensions.addAll(List.of("redis", "imagick", "apcu", "pdo_pgsql"));

        return defaults()
            .withExtensions(extensions)
            .withMemoryLimit(256)
            .withMaxExecutionTime(300);
    }

    /**
     * Creates a configuration for social networking platforms (Dolphin/UNA, HumHub).
     *
     * @return Social platform optimized PhpRuntimeConfig
     */
    public static PhpRuntimeConfig forSocial() {
        List<String> extensions = new ArrayList<>(COMMON_EXTENSIONS);
        extensions.addAll(List.of(
            "redis", "imagick", "gd", "ffmpeg", "apcu"
        ));

        return defaults()
            .withExtensions(extensions)
            .withMemoryLimit(384)
            .withMaxExecutionTime(600)  // Video processing
            .withUploadMaxFilesize(256)  // Media uploads
            .withPostMaxSize(256);
    }

    // ========== Builder Methods ==========

    /**
     * Returns a new config with the specified PHP version.
     *
     * @param version PHP version (e.g., "8.2", "8.1")
     * @return new PhpRuntimeConfig with updated version
     */
    public PhpRuntimeConfig withVersion(String version) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    /**
     * Returns a new config with the specified extensions.
     *
     * @param extensions list of PHP extension names
     * @return new PhpRuntimeConfig with updated extensions
     */
    public PhpRuntimeConfig withExtensions(List<String> extensions) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    /**
     * Returns a new config with additional extensions added.
     *
     * @param additionalExtensions extensions to add
     * @return new PhpRuntimeConfig with extensions added
     */
    public PhpRuntimeConfig withAdditionalExtensions(List<String> additionalExtensions) {
        List<String> combined = new ArrayList<>(extensions);
        for (String ext : additionalExtensions) {
            if (!combined.contains(ext)) {
                combined.add(ext);
            }
        }
        return withExtensions(combined);
    }

    /**
     * Returns a new config with the specified php.ini settings.
     *
     * @param phpIni map of php.ini key-value pairs
     * @return new PhpRuntimeConfig with updated php.ini
     */
    public PhpRuntimeConfig withPhpIni(Map<String, String> phpIni) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    /**
     * Returns a new config with the specified PHP-FPM settings.
     *
     * @param fpmConfig map of PHP-FPM configuration
     * @return new PhpRuntimeConfig with updated FPM config
     */
    public PhpRuntimeConfig withFpmConfig(Map<String, String> fpmConfig) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    /**
     * Returns a new config with the specified OPcache settings.
     *
     * @param opcacheConfig map of OPcache configuration
     * @return new PhpRuntimeConfig with updated OPcache config
     */
    public PhpRuntimeConfig withOpcacheConfig(Map<String, String> opcacheConfig) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    /**
     * Returns a new config with the specified memory limit.
     *
     * @param memoryLimit memory_limit in MB
     * @return new PhpRuntimeConfig with updated memory limit
     */
    public PhpRuntimeConfig withMemoryLimit(int memoryLimit) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    /**
     * Returns a new config with the specified max execution time.
     *
     * @param maxExecutionTime max_execution_time in seconds
     * @return new PhpRuntimeConfig with updated execution time
     */
    public PhpRuntimeConfig withMaxExecutionTime(int maxExecutionTime) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    /**
     * Returns a new config with the specified upload max filesize.
     *
     * @param uploadMaxFilesize upload_max_filesize in MB
     * @return new PhpRuntimeConfig with updated upload size
     */
    public PhpRuntimeConfig withUploadMaxFilesize(int uploadMaxFilesize) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    /**
     * Returns a new config with the specified post max size.
     *
     * @param postMaxSize post_max_size in MB
     * @return new PhpRuntimeConfig with updated post size
     */
    public PhpRuntimeConfig withPostMaxSize(int postMaxSize) {
        return new PhpRuntimeConfig(
            version, extensions, phpIni, fpmConfig, opcacheConfig,
            memoryLimit, maxExecutionTime, uploadMaxFilesize, postMaxSize
        );
    }

    // ========== Utility Methods ==========

    /**
     * Returns the base Docker image for this PHP version.
     *
     * @return PHP-FPM Alpine image tag
     */
    public String getBaseImage() {
        return String.format("php:%s-fpm-alpine", version);
    }

    /**
     * Returns the PHP-FPM pool name.
     *
     * @return pool name (default: "www")
     */
    public String getPoolName() {
        return "www";
    }

    /**
     * Generates php.ini content from configuration.
     *
     * @return php.ini content as string
     */
    public String generatePhpIni() {
        StringBuilder ini = new StringBuilder();
        ini.append("; CloudForge generated php.ini\n\n");

        // Memory and execution
        ini.append(String.format("memory_limit = %dM\n", memoryLimit));
        ini.append(String.format("max_execution_time = %d\n", maxExecutionTime));
        ini.append(String.format("upload_max_filesize = %dM\n", uploadMaxFilesize));
        ini.append(String.format("post_max_size = %dM\n", postMaxSize));
        ini.append("max_input_vars = 10000\n");
        ini.append("max_input_time = 600\n\n");

        // OPcache
        ini.append("; OPcache configuration\n");
        for (Map.Entry<String, String> entry : opcacheConfig.entrySet()) {
            ini.append(String.format("%s = %s\n", entry.getKey(), entry.getValue()));
        }
        ini.append("\n");

        // Custom php.ini settings
        if (!phpIni.isEmpty()) {
            ini.append("; Custom settings\n");
            for (Map.Entry<String, String> entry : phpIni.entrySet()) {
                ini.append(String.format("%s = %s\n", entry.getKey(), entry.getValue()));
            }
        }

        return ini.toString();
    }

    /**
     * Generates PHP-FPM pool configuration content.
     *
     * @return PHP-FPM pool configuration as string
     */
    public String generateFpmPoolConfig() {
        StringBuilder fpm = new StringBuilder();
        fpm.append("; CloudForge generated PHP-FPM pool configuration\n\n");
        fpm.append(String.format("[%s]\n", getPoolName()));
        fpm.append("user = www-data\n");
        fpm.append("group = www-data\n");
        fpm.append("listen = 127.0.0.1:9000\n\n");

        for (Map.Entry<String, String> entry : fpmConfig.entrySet()) {
            fpm.append(String.format("%s = %s\n", entry.getKey(), entry.getValue()));
        }

        return fpm.toString();
    }
}