471 lines
15 KiB
PHP
471 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This source file is subject to the new BSD license that is bundled
|
|
* with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://framework.zend.com/license/new-bsd
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@zend.com so we can send you a copy immediately.
|
|
*
|
|
* @category Zend
|
|
* @package Zend_View
|
|
* @subpackage Helper
|
|
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
* @version $Id: Sitemap.php 16971 2009-07-22 18:05:45Z mikaelkael $
|
|
*/
|
|
|
|
/**
|
|
* @see Zend_View_Helper_Navigation_HelperAbstract
|
|
*/
|
|
require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
|
|
|
|
/**
|
|
* Helper for printing sitemaps
|
|
*
|
|
* @link http://www.sitemaps.org/protocol.php
|
|
*
|
|
* @category Zend
|
|
* @package Zend_View
|
|
* @subpackage Helper
|
|
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
class Zend_View_Helper_Navigation_Sitemap
|
|
extends Zend_View_Helper_Navigation_HelperAbstract
|
|
{
|
|
/**
|
|
* Namespace for the <urlset> 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';
|
|
throw new Zend_Uri_Exception(sprintf(
|
|
'Invalid server URL: "%s"',
|
|
$serverUrl));
|
|
}
|
|
|
|
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)
|
|
{
|
|
// 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, 'UTF-8', 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';
|
|
throw new Zend_View_Exception(sprintf(
|
|
'Encountered an invalid URL for Sitemap XML: "%s"',
|
|
$url));
|
|
}
|
|
|
|
// 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';
|
|
throw new Zend_View_Exception(sprintf(
|
|
'Sitemap is invalid according to XML Schema at "%s"',
|
|
self::SITEMAP_XSD));
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|