public static function parseFilename($filename, &$bucket, &$object) {
    $bucket = null;
    $object = null;

    // $filename may contain nasty characters like # and ? that can throw off
    // parse_url(). It is best to do a manual parse here.
    $gs_prefix_len = strlen(self::GS_PREFIX);
    if (!StringUtil::startsWith($filename, self::GS_PREFIX)) {
      return false;
    }

    $first_slash_pos = strpos($filename, '/', $gs_prefix_len);
    if ($first_slash_pos === false) {
      $bucket = substr($filename, $gs_prefix_len);
    } else {
      $bucket = substr($filename, $gs_prefix_len,
          $first_slash_pos - $gs_prefix_len);
      // gs://bucket_name/ is treated the same as gs://bucket_name where
      // $object should be set to null.
      if ($first_slash_pos != strlen($filename) - 1) {
        $object = substr($filename, $first_slash_pos);
      }
    }

    if (strlen($bucket) == 0) {
      return false;
    }

    // Substitute default bucket name.
    if (ini_get('google_app_engine.gcs_default_keyword')) {
      if ($bucket === self::GS_DEFAULT_BUCKET_KEYWORD) {
        $bucket = self::getDefaultGoogleStorageBucketName();
        if (!$bucket) {
          throw new \InvalidArgumentException(
              'Application does not have a default Cloud Storage Bucket, ' .
              'must specify a bucket name');
        }
      }
    }

    // Validate bucket & object names.
    if (self::validateBucketName($bucket) === false) {
      trigger_error(sprintf('Invalid cloud storage bucket name \'%s\'',
          $bucket), E_USER_ERROR);
      return false;
    }

    if (isset($object) && self::validateObjectName($object) === false) {
      trigger_error(sprintf('Invalid cloud storage object name \'%s\'',
          $object), E_USER_ERROR);
      return false;
    }

    return true;
  }