Utility classes

2019
class AOC
{
    /**
     * Get the path for a given day
     *
     * @param string $file
     * @param int $year
     * @param int $day
     * @return string
     */
    public static function path($file, $year = null, $day = null)
    {
        return "./".($year?:static::year())."/".($day?:static::day())."/{$file}";
    }

    /**
     * Get all utilities
     *
     * @return array
     */
    public static function utilities($year = false)
    {
        if ($year) {
            $files = array_filter(scandir("$year"), fn ($f) => strpos($f, ".php"));
        } else {
            $files = array_diff(scandir("includes/utils"), [".", ".."]);
        }

        return array_map(fn ($f) => str_replace(".php", "", $f), $files);
    }

    /**
     * Load all utilities
     *
     * @return void
     */
    public static function loadUtilities()
    {
        // Global utilities
        foreach (static::utilities() as $class) {
            require_once("includes/utils/{$class}.php");
        }

        // Year-specific utilities
        foreach (static::utilities(static::year()) as $class) {
            require_once(static::year()."/{$class}.php");
        }
    }

    /**
     * Create a new directory with the base files for the given year/day
     *
     * @param int $year
     * @param int $day
     * @return void
     */
    public static function create($year, $day)
    {
        $path = static::path("", $year, $day);

        if (is_dir($path)) {
            echo "Directory already exists";
            exit;
        }

        // Create base directory
        mkdir($path, 0777, true);

        // Create each file with default content
        $files = [
            "README.md" => "# Day {$day}",
            "solution.php" => "\n\n",
            "input.txt" => "",
        ];
        foreach ($files as $file => $content) {
            file_put_contents($path . "/" . $file, $content);
        }
        exit;
    }

    /**
     * Load the right template/file based on the parameters
     *
     * @return void
     */
    public static function routing()
    {
        // Display list of utilities
        if (isset($_GET["utilities"])) {
            require_once("./includes/views/utilities.php");
            exit;
        }

        // Load and execute solution if requested
        if (AOC::direct() || AOC::ajax() || AOC::CLI()) {
            require_once("./includes/views/partial/solution.php");
        };

        // Display the homepage by default
        require_once("./includes/views/home.php");
    }

    /**
     * Link to a specific day
     *
     * @return string
     */
    public static function url($args)
    {
        return "?" . http_build_query(array_filter(array_merge($_GET, ["utilities" => 0], $args)));
    }

    // =============================================================================
    // > STATE VARIABLES
    // =============================================================================
    /**
     * The current state
     */
    public static $year;
    public static $day;
    public static $input;
    public static $question;
    public static $solution;
    public static $years;
    public static $days;
    public static $direct;
    public static $ajax;

    /**
     * The current input
     *
     * @return string
     */
    public static function input()
    {
        return static::$input ??= file_get_contents(static::path("input.txt"));
    }

    /**
     * The current question
     *
     * @return string
     */
    public static function question()
    {
        return static::$question ??= file_get_contents(static::path("README.md"));
    }

    /**
     * The current solution's code
     *
     * @return string
     */
    public static function solution()
    {
        $version = isset($_GET["version"]) ? "_" . $_GET["version"] : "";
        return static::$solution ??= file_get_contents(static::path("solution{$version}.php"));
    }

    /**
     * The visualisation link, if any
     *
     * @return void
     */
    public static function visualisation()
    {
        if (file_exists(static::path("visualisation.php"))) {
            return AOC::url(["visualisation" => 1, "direct" => 1]);
        }
        return false;
    }

    /**
     * The current year
     *
     * @return int
     */
    public static function year()
    {
        return static::$year ??= (int) ($_GET["year"] ?? static::years()->last());
    }

    /**
     * The available years
     *
     * @return Set
     */
    public static function years()
    {
        return static::$years ??= set(scandir("./"))->filter(fn ($dir) => intval($dir))->values();
    }

    /**
     * The current day
     *
     * @return int
     */
    public static function day()
    {
        return static::$day ??= (int) ($_GET["day"] ?? static::days()->last());
    }

    /**
     * The available days
     *
     * @return Set
     */
    public static function days($year = null)
    {
        return static::$days ??= set(scandir("./" . ($year ?: static::year()) . "/"))->filter(fn ($dir) => intval($dir))->values()->sort();
    }

    /**
     * The solution should be loaded directly and not via ajax
     *
     * @return bool
     */
    public static function direct()
    {
        return static::$direct ??= ($_GET["direct"] ?? false);
    }

    /**
     * The solution was loaded via ajax
     *
     * @return bool
     */
    public static function ajax()
    {
        return static::$ajax ??= ($_GET["ajax"] ?? false);
    }

    /**
     * The solution was loaded via the CLI
     *
     * @return bool
     */
    public static function CLI()
    {
        if (php_sapi_name() === "cli") {
            global $argv;

            [static::$year, static::$day] = explode("/", $argv[1]);

            if (static::$year[0] == "+") {
                static::create(trim(static::$year, "+"), static::$day);
            }

            return true;
        }
        return false;
    }
}