public static function parse($url) {
    $options = array(
      'path' => NULL,
      'query' => array(),
      'fragment' => '',
    );

    // External URLs: not using parse_url() here, so we do not have to rebuild
    // the scheme, host, and path without having any use for it.
    if (strpos($url, '://') !== FALSE) {
      // Split off everything before the query string into 'path'.
      $parts = explode('?', $url);

      // Don't support URLs without a path, like 'http://'.
      list(, $path) = explode('://', $parts[0], 2);
      if ($path != '') {
        $options['path'] = $parts[0];
      }
      // If there is a query string, transform it into keyed query parameters.
      if (isset($parts[1])) {
        $query_parts = explode('#', $parts[1]);
        parse_str($query_parts[0], $options['query']);
        // Take over the fragment, if there is any.
        if (isset($query_parts[1])) {
          $options['fragment'] = $query_parts[1];
        }
      }
    }
    // Internal URLs.
    else {
      // parse_url() does not support relative URLs, so make it absolute. E.g. the
      // relative URL "foo/bar:1" isn't properly parsed.
      $parts = parse_url('http://example.com/' . $url);
      // Strip the leading slash that was just added.
      $options['path'] = substr($parts['path'], 1);
      if (isset($parts['query'])) {
        parse_str($parts['query'], $options['query']);
      }
      if (isset($parts['fragment'])) {
        $options['fragment'] = $parts['fragment'];
      }
    }

    return $options;
  }