You are here

class XmlSitemapWriter in XML sitemap 2.x

Same name and namespace in other branches
  1. 8 src/XmlSitemapWriter.php \Drupal\xmlsitemap\XmlSitemapWriter

Extended class for writing XML sitemap files.

Hierarchy

Expanded class hierarchy of XmlSitemapWriter

1 file declares its use of XmlSitemapWriter
XmlSitemapWriterTest.php in tests/src/Kernel/XmlSitemapWriterTest.php

File

src/XmlSitemapWriter.php, line 12

Namespace

Drupal\xmlsitemap
View source
class XmlSitemapWriter extends \XMLWriter {

  /**
   * Document URI.
   *
   * @var string
   */
  protected $uri = NULL;

  /**
   * Counter for the sitemap elements.
   *
   * @var int
   */
  protected $sitemapElementCount = 0;

  /**
   * Flush counter for sitemap links.
   *
   * @var int
   */
  protected $linkCountFlush = 500;

  /**
   * Sitemap object to be written.
   *
   * @var \Drupal\xmlsitemap\XmlSitemapInterface
   */
  protected $sitemap;

  /**
   * Sitemap page to be written.
   *
   * @var int|string
   */
  protected $page;

  /**
   * Constructors and XmlSitemapWriter object.
   *
   * @param \Drupal\xmlsitemap\XmlSitemapInterface $sitemap
   *   The XML sitemap.
   * @param int|string $page
   *   The current page of the sitemap being generated.
   *
   * @throws \InvalidArgumentException
   *   If the page is invalid.
   * @throws \Drupal\xmlsitemap\XmlSitemapGenerationException
   *   If the file URI cannot be opened.
   */
  public function __construct(XmlSitemapInterface $sitemap, $page) {
    if ($page !== 'index' && !filter_var($page, FILTER_VALIDATE_INT)) {
      throw new \InvalidArgumentException("Invalid XML sitemap page {$page}.");
    }
    $this->sitemap = $sitemap;
    $this->page = $page;
    $this->uri = xmlsitemap_sitemap_get_file($sitemap, $page);
    $this
      ->openUri($this->uri);
  }

  /**
   * Opens and uri.
   *
   * @param string $uri
   *   Uri to be opened.
   *
   * @return bool
   *   Returns TRUE when uri was successfully opened.
   *
   * @throws XmlSitemapGenerationException
   *   If the file URI cannot be opened.
   */
  public function openUri($uri) {
    $return = parent::openUri($uri);
    if (!$return) {
      throw new XmlSitemapGenerationException("Could not open file {$uri} for writing.");
    }
    return $return;
  }

  /**
   * Starts an XML document.
   *
   * @param string $version
   *   The version number of the document.
   * @param string $encoding
   *   The encoding of the document.
   * @param string $standalone
   *   Yes or No.
   *
   * @throws XmlSitemapGenerationException
   *   Throws exception when document cannot be started.
   *
   * @return bool
   *   Returns TRUE on success.
   */
  public function startDocument($version = '1.0', $encoding = 'UTF-8', $standalone = NULL) {
    $this
      ->setIndent(FALSE);
    $result = parent::startDocument($version, $encoding);
    if (!$result) {
      throw new XmlSitemapGenerationException("Unknown error occurred while writing to file {$this->uri}.");
    }
    if (\Drupal::config('xmlsitemap.settings')
      ->get('xsl')) {
      $this
        ->writeXsl();
    }
    $this
      ->startElement($this
      ->isIndex() ? 'sitemapindex' : 'urlset', TRUE);
    return $result;
  }

  /**
   * Adds the XML stylesheet to the XML page.
   */
  public function writeXsl() {
    $xls_url = Url::fromRoute('xmlsitemap.sitemap_xsl')
      ->toString();
    $settings = \Drupal::config('language.negotiation');
    if ($settings) {
      $url_settings = $settings
        ->get('url');
      if (isset($url_settings['source']) && $url_settings['source'] == 'domain') {
        $scheme = \Drupal::request()
          ->getScheme();
        $context = $this->sitemap
          ->getContext();
        $base_url = $scheme . '://' . $url_settings['domains'][$context['language']];
        $xls_url = Url::fromRoute('xmlsitemap.sitemap_xsl');
        $xls_url = $base_url . '/' . $xls_url
          ->getInternalPath();
      }
    }
    $this
      ->writePi('xml-stylesheet', 'type="text/xsl" href="' . $xls_url . '"');
    $this
      ->writeRaw(PHP_EOL);
  }

  /**
   * Return an array of attributes for the root element of the XML.
   *
   * @return array
   *   Returns root attributes.
   */
  public function getRootAttributes() {
    $attributes['xmlns'] = 'http://www.sitemaps.org/schemas/sitemap/0.9';

    // @todo Should content_moderation implement hook_xmlsitemap_root_attributes_alter() instead?
    $attributes['xmlns:xhtml'] = 'http://www.w3.org/1999/xhtml';
    if (\Drupal::state()
      ->get('xmlsitemap_developer_mode')) {
      $attributes['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance';
      $attributes['xsi:schemaLocation'] = 'http://www.sitemaps.org/schemas/sitemap/0.9';
      if ($this
        ->isIndex()) {
        $attributes['xsi:schemaLocation'] .= ' http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd';
      }
      else {
        $attributes['xsi:schemaLocation'] .= ' http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd';
      }
    }
    \Drupal::moduleHandler()
      ->alter('xmlsitemap_root_attributes', $attributes, $this->sitemap);
    return $attributes;
  }

  /**
   * Creates start element tag.
   *
   * @param string $name
   *   Element name.
   * @param bool $root
   *   Specify if it is root element or not.
   */
  public function startElement($name, $root = FALSE) {
    parent::startElement($name);
    if ($root) {
      foreach ($this
        ->getRootAttributes() as $key => $value) {
        $this
          ->writeAttribute($key, $value);
      }
      $this
        ->writeRaw(PHP_EOL);
    }
  }

  /**
   * Writes full element tag including support for nested elements.
   *
   * @param string $name
   *   The element name.
   * @param string|array $content
   *   The element contents or an array of the elements' sub-elements.
   */
  public function writeElement($name, $content = NULL) {
    if (is_array($content)) {
      $this
        ->startElement($name);
      $this
        ->writeRaw($this
        ->formatXmlElements($content));
      $this
        ->endElement();
    }
    else {
      parent::writeElement($name, Html::escape(static::toString($content)));
    }
    $this
      ->writeRaw(PHP_EOL);

    // After a certain number of elements have been added, flush the buffer
    // to the output file.
    $this->sitemapElementCount++;
    if ($this->sitemapElementCount % $this->linkCountFlush == 0) {
      $this
        ->flush();
    }
  }

  /**
   * Getter of the document uri.
   *
   * @return string
   *   Document uri.
   */
  public function getUri() {
    return $this->uri;
  }

  /**
   * Getter of the element count.
   *
   * @return int
   *   Element counters.
   */
  public function getSitemapElementCount() {
    return $this->sitemapElementCount;
  }

  /**
   * Ends an XML document.
   *
   * @throws XmlSitemapGenerationException
   *
   * @return bool
   *   Returns TRUE on success.
   */
  public function endDocument() {
    $return = parent::endDocument();
    if (!$return) {
      throw new XmlSitemapGenerationException("Unknown error occurred while writing to file {$this->uri}.");
    }
    if (xmlsitemap_var('gz')) {
      $file_gz = $this->uri . '.gz';
      file_put_contents($file_gz, gzencode(file_get_contents($this->uri), 9));
    }
    return $return;
  }

  /**
   * If the page being written is the index.
   *
   * @return bool
   *   TRUE if the sitemap index is being written, or FALSE otherwise.
   */
  protected function isIndex() {
    return $this->page === 'index';
  }

  /**
   * Copy of Drupal 7's format_xml_elements() function.
   *
   * The extra whitespace has been removed.
   *
   * @param array $array
   *   An array where each item represents an element and is either a:
   *   - (key => value) pair (<key>value</key>)
   *   - Associative array with fields:
   *     - 'key': element name
   *     - 'value': element contents
   *     - 'attributes': associative array of element attributes or an
   *       \Drupal\Core\Template\Attribute object
   *   In both cases, 'value' can be a simple string, or it can be another
   *   array with the same format as $array itself for nesting.
   *
   * @return string
   *   The XML output.
   */
  public static function formatXmlElements(array $array) {
    $output = '';
    foreach ($array as $key => $value) {
      if (is_numeric($key)) {
        if ($value['key']) {
          $output .= '<' . $value['key'];
          if (isset($value['attributes'])) {
            if (is_array($value['attributes'])) {
              $value['attributes'] = new Attribute($value['attributes']);
            }
            $output .= static::toString($value['attributes']);
          }
          if (isset($value['value']) && $value['value'] != '') {
            $output .= '>' . (is_array($value['value']) ? static::formatXmlElements($value['value']) : Html::escape(static::toString($value['value']))) . '</' . $value['key'] . '>';
          }
          else {
            $output .= ' />';
          }
        }
      }
      else {
        $output .= '<' . $key . '>' . (is_array($value) ? static::formatXmlElements($value) : Html::escape(static::toString($value))) . "</{$key}>";
      }
    }
    return $output;
  }

  /**
   * Convert translatable strings and URLs to strings.
   *
   * @param mixed $value
   *   The value to turn into a string.
   *
   * @return string
   *   The string value.
   */
  public static function toString($value) {
    if (is_object($value)) {
      if ($value instanceof Url) {
        return $value
          ->toString();
      }
    }
    return (string) $value;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
XmlSitemapWriter::$linkCountFlush protected property Flush counter for sitemap links.
XmlSitemapWriter::$page protected property Sitemap page to be written.
XmlSitemapWriter::$sitemap protected property Sitemap object to be written.
XmlSitemapWriter::$sitemapElementCount protected property Counter for the sitemap elements.
XmlSitemapWriter::$uri protected property Document URI.
XmlSitemapWriter::endDocument public function Ends an XML document.
XmlSitemapWriter::formatXmlElements public static function Copy of Drupal 7's format_xml_elements() function.
XmlSitemapWriter::getRootAttributes public function Return an array of attributes for the root element of the XML.
XmlSitemapWriter::getSitemapElementCount public function Getter of the element count.
XmlSitemapWriter::getUri public function Getter of the document uri.
XmlSitemapWriter::isIndex protected function If the page being written is the index.
XmlSitemapWriter::openUri public function Opens and uri.
XmlSitemapWriter::startDocument public function Starts an XML document.
XmlSitemapWriter::startElement public function Creates start element tag.
XmlSitemapWriter::toString public static function Convert translatable strings and URLs to strings.
XmlSitemapWriter::writeElement public function Writes full element tag including support for nested elements.
XmlSitemapWriter::writeXsl public function Adds the XML stylesheet to the XML page.
XmlSitemapWriter::__construct public function Constructors and XmlSitemapWriter object.