tag * * @var string */ const SITEMAP_NS = 'http://www.sitemaps.org/schemas/sitemap/0.9'; /** * Schema URL * * @var string */ const SITEMAP_XSD = 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd'; /** * Whether XML output should be formatted * * @var bool */ protected $_formatOutput = false; /** * Whether the XML declaration should be included in XML output * * @var bool */ protected $_useXmlDeclaration = true; /** * Whether sitemap should be validated using Zend_Validate_Sitemap_* * * @var bool */ protected $_useSitemapValidators = true; /** * Whether sitemap should be schema validated when generated * * @var bool */ protected $_useSchemaValidation = false; /** * Server url * * @var string */ protected $_serverUrl; /** * View helper entry point: * Retrieves helper and optionally sets container to operate on * * @param Zend_Navigation_Container $container [optional] container to * operate on * @return Zend_View_Helper_Navigation_Sitemap fluent interface, returns * self */ public function sitemap(Zend_Navigation_Container $container = null) { if (null !== $container) { $this->setContainer($container); } return $this; } // Accessors: /** * Sets whether XML output should be formatted * * @param bool $formatOutput [optional] whether output * should be formatted. Default * is true. * @return Zend_View_Helper_Navigation_Sitemap fluent interface, returns * self */ public function setFormatOutput($formatOutput = true) { $this->_formatOutput = (bool) $formatOutput; return $this; } /** * Returns whether XML output should be formatted * * @return bool whether XML output should be formatted */ public function getFormatOutput() { return $this->_formatOutput; } /** * Sets whether the XML declaration should be used in output * * @param bool $useXmlDecl whether XML delcaration * should be rendered * @return Zend_View_Helper_Navigation_Sitemap fluent interface, returns * self */ public function setUseXmlDeclaration($useXmlDecl) { $this->_useXmlDeclaration = (bool) $useXmlDecl; return $this; } /** * Returns whether the XML declaration should be used in output * * @return bool whether the XML declaration should be used in output */ public function getUseXmlDeclaration() { return $this->_useXmlDeclaration; } /** * Sets whether sitemap should be validated using Zend_Validate_Sitemap_* * * @param bool $useSitemapValidators whether sitemap validators * should be used * @return Zend_View_Helper_Navigation_Sitemap fluent interface, returns * self */ public function setUseSitemapValidators($useSitemapValidators) { $this->_useSitemapValidators = (bool) $useSitemapValidators; return $this; } /** * Returns whether sitemap should be validated using Zend_Validate_Sitemap_* * * @return bool whether sitemap should be validated using validators */ public function getUseSitemapValidators() { return $this->_useSitemapValidators; } /** * Sets whether sitemap should be schema validated when generated * * @param bool $schemaValidation whether sitemap should * validated using XSD Schema * @return Zend_View_Helper_Navigation_Sitemap fluent interface, returns * self */ public function setUseSchemaValidation($schemaValidation) { $this->_useSchemaValidation = (bool) $schemaValidation; return $this; } /** * Returns true if sitemap should be schema validated when generated * * @return bool */ public function getUseSchemaValidation() { return $this->_useSchemaValidation; } /** * Sets server url (scheme and host-related stuff without request URI) * * E.g. http://www.example.com * * @param string $serverUrl server URL to set (only * scheme and host) * @throws Zend_Uri_Exception if invalid server URL * @return Zend_View_Helper_Navigation_Sitemap fluent interface, returns * self */ public function setServerUrl($serverUrl) { require_once 'Zend/Uri.php'; $uri = Zend_Uri::factory($serverUrl); $uri->setFragment(''); $uri->setPath(''); $uri->setQuery(''); if ($uri->valid()) { $this->_serverUrl = $uri->getUri(); } else { require_once 'Zend/Uri/Exception.php'; $e = new Zend_Uri_Exception(sprintf( 'Invalid server URL: "%s"', $serverUrl)); $e->setView($this->view); throw $e; } return $this; } /** * Returns server URL * * @return string server URL */ public function getServerUrl() { if (!isset($this->_serverUrl)) { $this->_serverUrl = $this->view->serverUrl(); } return $this->_serverUrl; } // Helper methods: /** * Escapes string for XML usage * * @param string $string string to escape * @return string escaped string */ protected function _xmlEscape($string) { $enc = 'UTF-8'; if ($this->view instanceof Zend_View_Interface && method_exists($this->view, 'getEncoding') ) { $enc = $this->view->getEncoding(); } // TODO: remove check when minimum PHP version is >= 5.2.3 if (version_compare(PHP_VERSION, '5.2.3', '>=')) { // do not encode existing HTML entities return htmlspecialchars($string, ENT_QUOTES, $enc, false); } else { $string = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&', $string); $string = str_replace(array('<', '>', '\'', '"'), array('<', '>', ''', '"'), $string); return $string; } } // Public methods: /** * Returns an escaped absolute URL for the given page * * @param Zend_Navigation_Page $page page to get URL from * @return string */ public function url(Zend_Navigation_Page $page) { $href = $page->getHref(); if (!isset($href{0})) { // no href return ''; } elseif ($href{0} == '/') { // href is relative to root; use serverUrl helper $url = $this->getServerUrl() . $href; } elseif (preg_match('/^[a-z]+:/im', (string) $href)) { // scheme is given in href; assume absolute URL already $url = (string) $href; } else { // href is relative to current document; use url helpers $url = $this->getServerUrl() . rtrim($this->view->url(), '/') . '/' . $href; } return $this->_xmlEscape($url); } /** * Returns a DOMDocument containing the Sitemap XML for the given container * * @param Zend_Navigation_Container $container [optional] container to get * breadcrumbs from, defaults * to what is registered in the * helper * @return DOMDocument DOM representation of the * container * @throws Zend_View_Exception if schema validation is on * and the sitemap is invalid * according to the sitemap * schema, or if sitemap * validators are used and the * loc element fails validation */ public function getDomSitemap(Zend_Navigation_Container $container = null) { if (null === $container) { $container = $this->getContainer(); } // check if we should validate using our own validators if ($this->getUseSitemapValidators()) { require_once 'Zend/Validate/Sitemap/Changefreq.php'; require_once 'Zend/Validate/Sitemap/Lastmod.php'; require_once 'Zend/Validate/Sitemap/Loc.php'; require_once 'Zend/Validate/Sitemap/Priority.php'; // create validators $locValidator = new Zend_Validate_Sitemap_Loc(); $lastmodValidator = new Zend_Validate_Sitemap_Lastmod(); $changefreqValidator = new Zend_Validate_Sitemap_Changefreq(); $priorityValidator = new Zend_Validate_Sitemap_Priority(); } // create document $dom = new DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = $this->getFormatOutput(); // ...and urlset (root) element $urlSet = $dom->createElementNS(self::SITEMAP_NS, 'urlset'); $dom->appendChild($urlSet); // create iterator $iterator = new RecursiveIteratorIterator($container, RecursiveIteratorIterator::SELF_FIRST); $maxDepth = $this->getMaxDepth(); if (is_int($maxDepth)) { $iterator->setMaxDepth($maxDepth); } $minDepth = $this->getMinDepth(); if (!is_int($minDepth) || $minDepth < 0) { $minDepth = 0; } // iterate container foreach ($iterator as $page) { if ($iterator->getDepth() < $minDepth || !$this->accept($page)) { // page should not be included continue; } // get absolute url from page if (!$url = $this->url($page)) { // skip page if it has no url (rare case) continue; } // create url node for this page $urlNode = $dom->createElementNS(self::SITEMAP_NS, 'url'); $urlSet->appendChild($urlNode); if ($this->getUseSitemapValidators() && !$locValidator->isValid($url)) { require_once 'Zend/View/Exception.php'; $e = new Zend_View_Exception(sprintf( 'Encountered an invalid URL for Sitemap XML: "%s"', $url)); $e->setView($this->view); throw $e; } // put url in 'loc' element $urlNode->appendChild($dom->createElementNS(self::SITEMAP_NS, 'loc', $url)); // add 'lastmod' element if a valid lastmod is set in page if (isset($page->lastmod)) { $lastmod = strtotime((string) $page->lastmod); // prevent 1970-01-01... if ($lastmod !== false) { $lastmod = date('c', $lastmod); } if (!$this->getUseSitemapValidators() || $lastmodValidator->isValid($lastmod)) { $urlNode->appendChild( $dom->createElementNS(self::SITEMAP_NS, 'lastmod', $lastmod) ); } } // add 'changefreq' element if a valid changefreq is set in page if (isset($page->changefreq)) { $changefreq = $page->changefreq; if (!$this->getUseSitemapValidators() || $changefreqValidator->isValid($changefreq)) { $urlNode->appendChild( $dom->createElementNS(self::SITEMAP_NS, 'changefreq', $changefreq) ); } } // add 'priority' element if a valid priority is set in page if (isset($page->priority)) { $priority = $page->priority; if (!$this->getUseSitemapValidators() || $priorityValidator->isValid($priority)) { $urlNode->appendChild( $dom->createElementNS(self::SITEMAP_NS, 'priority', $priority) ); } } } // validate using schema if specified if ($this->getUseSchemaValidation()) { if (!@$dom->schemaValidate(self::SITEMAP_XSD)) { require_once 'Zend/View/Exception.php'; $e = new Zend_View_Exception(sprintf( 'Sitemap is invalid according to XML Schema at "%s"', self::SITEMAP_XSD)); $e->setView($this->view); throw $e; } } return $dom; } // Zend_View_Helper_Navigation_Helper: /** * Renders helper * * Implements {@link Zend_View_Helper_Navigation_Helper::render()}. * * @param Zend_Navigation_Container $container [optional] container to * render. Default is to * render the container * registered in the helper. * @return string helper output */ public function render(Zend_Navigation_Container $container = null) { $dom = $this->getDomSitemap($container); $xml = $this->getUseXmlDeclaration() ? $dom->saveXML() : $dom->saveXML($dom->documentElement); return rtrim($xml, PHP_EOL); } }