protected static function componentsFromMatches(array $matches): array
    {
        // http://tools.ietf.org/html/rfc3986#section-5.3
        // Note that we are careful to preserve the distinction between a
        // component that is undefined, meaning that its separator was not
        // present in the reference, and a component that is empty, meaning
        // that the separator was present and was immediately followed by the
        // next component separator or the end of the reference.
        if (isset($matches[2]) && $matches[2]) {
            $scheme = $matches[1] ?? '';
        } else {
            $scheme = null;
        }
        if (isset($matches[3]) && $matches[3]) {
            $authority = $matches[4] ?? '';
        } else {
            $authority = null;
        }
        $path = $matches[5] ?? '';
        if (isset($matches[6]) && $matches[6]) {
            $query = $matches[7] ?? '';
        } else {
            $query = null;
        }
        if (isset($matches[8]) && $matches[8]) {
            $fragment = $matches[9] ?? '';
        } else {
            $fragment = null;
        }

        return [
            'scheme'    => $scheme,
            'authority' => $authority,
            'path'      => $path,
            'query'     => $query,
            'fragment'  => $fragment
        ];
    }