protected function parse($iri)
    {
        // Parse IRI by using the regular expression as specified by
        // http://tools.ietf.org/html/rfc3986#appendix-B
        $regex = '|^((?P<scheme>[^:/?#]+):)?' .
                    '((?P<doubleslash>//)(?P<authority>[^/?#]*))?(?P<path>[^?#]*)' .
                    '((?P<querydef>\?)(?P<query>[^#]*))?(#(?P<fragment>.*))?|';
        preg_match($regex, $iri, $match);

        // Extract scheme
        if (false === empty($match['scheme'])) {
            $this->scheme = $match['scheme'];
        }

        // Parse authority (http://tools.ietf.org/html/rfc3986#section-3.2)
        if ('//' === $match['doubleslash']) {
            if (0 === strlen($match['authority'])) {
                $this->host = '';
            } else {
                $authority = $match['authority'];

                // Split authority into userinfo and host
                // (use last @ to ignore unescaped @ symbols)
                if (false !== ($pos = strrpos($authority, '@'))) {
                    $this->userinfo = substr($authority, 0, $pos);
                    $authority = substr($authority, $pos + 1);
                }

                // Split authority into host and port
                $hostEnd = 0;
                if ((strlen($authority) > 0) &&
                    ('[' === $authority[0]) &&
                    (false !== ($pos = strpos($authority, ']')))) {
                    $hostEnd = $pos;
                }

                if ((false !== ($pos = strrpos($authority, ':'))) && ($pos > $hostEnd)) {
                    $this->host = substr($authority, 0, $pos);
                    $this->port = substr($authority, $pos + 1);
                } else {
                    $this->host = $authority;
                }
            }
        }

        // Extract path (http://tools.ietf.org/html/rfc3986#section-3.3)
        // The path is always present but might be empty
        $this->path = $match['path'];

        // Extract query (http://tools.ietf.org/html/rfc3986#section-3.4)
        if (false === empty($match['querydef'])) {
            $this->query = $match['query'];
        }

        // Extract fragment (http://tools.ietf.org/html/rfc3986#section-3.5)
        if (isset($match['fragment'])) {
            $this->fragment = $match['fragment'];
        }
    }