src/includes/classes/Core/Utils/Route.php
public function resolve(string $path = '', string $dir = '', array $args = []): array
    {
        $default_args = [
            'rewrites'                => [],
            'protocol'                => 'http',
            'extension'               => 'php',
            'redirect_trailing_slash' => true,
        ];
        $args += $default_args; // Merge defaults.

        $rewrites                = (array) $args['rewrites'];
        $protocol                = (string) $args['protocol'];
        $extension               = (string) $args['extension'];
        $redirect_trailing_slash = (bool) $args['redirect_trailing_slash'];

        if (!$protocol || !$extension) {
            return []; // Missing components.
        }
        if (!isset($path[0])) { // If no specific route; use current URL path.
            $path = $this->c::currentPath(); // Use current URL path.

            if ($redirect_trailing_slash && mb_substr($path, -1) === '/' && $path !== '/') {
                $current_url         = $this->c::parseUrl($this->c::currentUrl());
                $current_url['path'] = $this->c::mbRTrim($current_url['path'], '/');
                $current_url         = $this->c::unparseUrl($current_url);

                $this->c::redirect($current_url, [
                    'cacheable' => true,
                    'status'    => 301,
                ]); // Without trailing slash.
            }
        } // Trim leading/trailing slashes.
        $path               = $this->c::mbTrim($path, '/');
        $path               = isset($path[0]) ? $path : 'index';
        $rewrite_query_vars = []; // Initialize query vars.

        foreach ($rewrites as $_rewrite_pattern => $_rewrite_path) {
            if (!preg_match($_rewrite_pattern, $path, $_m)) {
                continue; // Only if pattern matches.
            } elseif (!$_rewrite_path) {
                continue; // Must have.
            }
            $_rewrite_path_contains_rcs = mb_strpos($_rewrite_path, '$') !== false
                || mb_strpos($_rewrite_path, '%%') !== false;

            foreach ($_m as $_key => $_value) {
                if ($_rewrite_path_contains_rcs) {
                    $_rewrite_path = str_replace(['${'.$_key.'}', '%%'.$_key.'%%'], $_value, $_rewrite_path);
                    $_rewrite_path = is_int($_key) ? str_replace('$'.$_key, $_value, $_rewrite_path) : $_rewrite_path;
                } // This allows replacement codes in rewritten paths.

                if ($_key && is_string($_key)) {
                    $rewrite_query_vars[urldecode($_key)] = urldecode($_value);
                } // Only string keys w/ a value.
            } // unset($_key, $_value); // Housekeeping.

            if ($_rewrite_path_contains_rcs) {
                $_rewrite_path = preg_replace(['/\$[0-9]+/u', '/\$\{[^{}]*\}/u', '/%%[^%]*%%/u'], '', $_rewrite_path);
                $_rewrite_path = preg_replace('/\/{2,}/u', '/', $_rewrite_path);
            }
            $path = $_rewrite_path;
            $path = $this->c::mbTrim($path, '/');
            $path = isset($path[0]) ? $path : 'index';
            break; // Only one pattern can match; i.e., the first match wins.
        } // unset($_rewrite_pattern, $_rewrite_path, $_rewrite_path_contains_rcs, $_m);

        $location        = $this->locate($protocol.'/'.$path.'.'.$extension, $dir);
        return $resolved = $location ? array_merge($location, compact('path', 'rewrite_query_vars')) : [];
    }
public function resolve(string $path = '', string $dir = '', array $args = []): array
    {
        $default_args = [
            'rewrites'                => [],
            'protocol'                => 'http',
            'extension'               => 'php',
            'redirect_trailing_slash' => true,
        ];
        $args += $default_args; // Merge defaults.

        $rewrites                = (array) $args['rewrites'];
        $protocol                = (string) $args['protocol'];
        $extension               = (string) $args['extension'];
        $redirect_trailing_slash = (bool) $args['redirect_trailing_slash'];

        if (!$protocol || !$extension) {
            return []; // Missing components.
        }
        if (!isset($path[0])) { // If no specific route; use current URL path.
            $path = $this->c::currentPath(); // Use current URL path.

            if ($redirect_trailing_slash && mb_substr($path, -1) === '/' && $path !== '/') {
                $current_url         = $this->c::parseUrl($this->c::currentUrl());
                $current_url['path'] = $this->c::mbRTrim($current_url['path'], '/');
                $current_url         = $this->c::unparseUrl($current_url);

                $this->c::redirect($current_url, [
                    'cacheable' => true,
                    'status'    => 301,
                ]); // Without trailing slash.
            }
        } // Trim leading/trailing slashes.
        $path               = $this->c::mbTrim($path, '/');
        $path               = isset($path[0]) ? $path : 'index';
        $rewrite_query_vars = []; // Initialize query vars.

        foreach ($rewrites as $_rewrite_pattern => $_rewrite_path) {
            if (!preg_match($_rewrite_pattern, $path, $_m)) {
                continue; // Only if pattern matches.
            } elseif (!$_rewrite_path) {
                continue; // Must have.
            }
            $_rewrite_path_contains_rcs = mb_strpos($_rewrite_path, '$') !== false
                || mb_strpos($_rewrite_path, '%%') !== false;

            foreach ($_m as $_key => $_value) {
                if ($_rewrite_path_contains_rcs) {
                    $_rewrite_path = str_replace(['${'.$_key.'}', '%%'.$_key.'%%'], $_value, $_rewrite_path);
                    $_rewrite_path = is_int($_key) ? str_replace('$'.$_key, $_value, $_rewrite_path) : $_rewrite_path;
                } // This allows replacement codes in rewritten paths.

                if ($_key && is_string($_key)) {
                    $rewrite_query_vars[urldecode($_key)] = urldecode($_value);
                } // Only string keys w/ a value.
            } // unset($_key, $_value); // Housekeeping.

            if ($_rewrite_path_contains_rcs) {
                $_rewrite_path = preg_replace(['/\$[0-9]+/u', '/\$\{[^{}]*\}/u', '/%%[^%]*%%/u'], '', $_rewrite_path);
                $_rewrite_path = preg_replace('/\/{2,}/u', '/', $_rewrite_path);
            }
            $path = $_rewrite_path;
            $path = $this->c::mbTrim($path, '/');
            $path = isset($path[0]) ? $path : 'index';
            break; // Only one pattern can match; i.e., the first match wins.
        } // unset($_rewrite_pattern, $_rewrite_path, $_rewrite_path_contains_rcs, $_m);

        $location        = $this->locate($protocol.'/'.$path.'.'.$extension, $dir);
        return $resolved = $location ? array_merge($location, compact('path', 'rewrite_query_vars')) : [];
    }
Resolves a route.

@since 161008 Route utilities.

@param string $path A URL path/route.
@param string $dir  From a specific directory?
@param array  $args Any additional behavioral args.

@return array `[dir, file, ext, path, rewrite_query_vars]`.