public static function createFromVariables(array $variables, $input, $url = null)
    {
        // Infer URL from REQUEST_URI unless explicitly provided
        if (!isset($url)) {
            // Remove query parameters (they're retained separately through $server['_GET']
            $url = parse_url($variables['_SERVER']['REQUEST_URI'], PHP_URL_PATH);

            // Remove base folders from the URL if webroot is hosted in a subfolder
            if (substr(strtolower($url), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) {
                $url = substr($url, strlen(BASE_URL));
            }
        }

        // Build request
        $request = new HTTPRequest(
            $variables['_SERVER']['REQUEST_METHOD'],
            $url,
            $variables['_GET'],
            $variables['_POST'],
            $input
        );

        // Set the scheme to HTTPS if needed
        if ((!empty($variables['_SERVER']['HTTPS']) && $variables['_SERVER']['HTTPS'] != 'off')
            || isset($variables['_SERVER']['SSL'])) {
            $request->setScheme('https');
        }

        // Set the client IP
        if (!empty($variables['_SERVER']['REMOTE_ADDR'])) {
            $request->setIP($variables['_SERVER']['REMOTE_ADDR']);
        }

        // Add headers
        $headers = static::extractRequestHeaders($variables['_SERVER']);
        foreach ($headers as $header => $value) {
            $request->addHeader($header, $value);
        }

        // Initiate an empty session - doesn't initialize an actual PHP session (see HTTPApplication)
        $session = new Session(isset($variables['_SESSION']) ? $variables['_SESSION'] : null);
        $request->setSession($session);

        return $request;
    }