protected function composeUri(array $components)
    {
        $uri = '';

        $scheme = empty($components['scheme']) ? 'http' : $components['scheme'];
        if ($scheme !== '') {
            $uri .= $scheme . ':';
        }

        $authority = $this->composeAuthority($components);

        if ($authority !== '' || $scheme === 'file') {
            // authority separator is added even when the authority is missing/empty for the "file" scheme
            // while `file:///myfile` and `file:/myfile` are equivalent according to RFC 3986, `file:///` is more common
            // PHP functions and Chrome, for example, use this format
            $uri .= '//' . $authority;
        }

        if (!empty($components['path'])) {
            $uri .= $components['path'];
        }

        if (!empty($components['query'])) {
            $uri .= '?' . $components['query'];
        }

        if (!empty($components['fragment'])) {
            $uri .= '#' . $components['fragment'];
        }

        return $uri;
    }