web/Zend/View/Helper/Navigation/Sitemap.php
changeset 0 4eba9c11703f
equal deleted inserted replaced
-1:000000000000 0:4eba9c11703f
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_View
       
    17  * @subpackage Helper
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: Sitemap.php 20104 2010-01-06 21:26:01Z matthew $
       
    21  */
       
    22 
       
    23 /**
       
    24  * @see Zend_View_Helper_Navigation_HelperAbstract
       
    25  */
       
    26 require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
       
    27 
       
    28 /**
       
    29  * Helper for printing sitemaps
       
    30  *
       
    31  * @link http://www.sitemaps.org/protocol.php
       
    32  *
       
    33  * @category   Zend
       
    34  * @package    Zend_View
       
    35  * @subpackage Helper
       
    36  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    37  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    38  */
       
    39 class Zend_View_Helper_Navigation_Sitemap
       
    40     extends Zend_View_Helper_Navigation_HelperAbstract
       
    41 {
       
    42     /**
       
    43      * Namespace for the <urlset> tag
       
    44      *
       
    45      * @var string
       
    46      */
       
    47     const SITEMAP_NS = 'http://www.sitemaps.org/schemas/sitemap/0.9';
       
    48 
       
    49     /**
       
    50      * Schema URL
       
    51      *
       
    52      * @var string
       
    53      */
       
    54     const SITEMAP_XSD = 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd';
       
    55 
       
    56     /**
       
    57      * Whether XML output should be formatted
       
    58      *
       
    59      * @var bool
       
    60      */
       
    61     protected $_formatOutput = false;
       
    62 
       
    63     /**
       
    64      * Whether the XML declaration should be included in XML output
       
    65      *
       
    66      * @var bool
       
    67      */
       
    68     protected $_useXmlDeclaration = true;
       
    69 
       
    70     /**
       
    71      * Whether sitemap should be validated using Zend_Validate_Sitemap_*
       
    72      *
       
    73      * @var bool
       
    74      */
       
    75     protected $_useSitemapValidators = true;
       
    76 
       
    77     /**
       
    78      * Whether sitemap should be schema validated when generated
       
    79      *
       
    80      * @var bool
       
    81      */
       
    82     protected $_useSchemaValidation = false;
       
    83 
       
    84     /**
       
    85      * Server url
       
    86      *
       
    87      * @var string
       
    88      */
       
    89     protected $_serverUrl;
       
    90 
       
    91     /**
       
    92      * View helper entry point:
       
    93      * Retrieves helper and optionally sets container to operate on
       
    94      *
       
    95      * @param  Zend_Navigation_Container $container  [optional] container to
       
    96      *                                               operate on
       
    97      * @return Zend_View_Helper_Navigation_Sitemap   fluent interface, returns
       
    98      *                                               self
       
    99      */
       
   100     public function sitemap(Zend_Navigation_Container $container = null)
       
   101     {
       
   102         if (null !== $container) {
       
   103             $this->setContainer($container);
       
   104         }
       
   105 
       
   106         return $this;
       
   107     }
       
   108 
       
   109     // Accessors:
       
   110 
       
   111     /**
       
   112      * Sets whether XML output should be formatted
       
   113      *
       
   114      * @param  bool $formatOutput                   [optional] whether output
       
   115      *                                              should be formatted. Default
       
   116      *                                              is true.
       
   117      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
       
   118      *                                              self
       
   119      */
       
   120     public function setFormatOutput($formatOutput = true)
       
   121     {
       
   122         $this->_formatOutput = (bool) $formatOutput;
       
   123         return $this;
       
   124     }
       
   125 
       
   126     /**
       
   127      * Returns whether XML output should be formatted
       
   128      *
       
   129      * @return bool  whether XML output should be formatted
       
   130      */
       
   131     public function getFormatOutput()
       
   132     {
       
   133         return $this->_formatOutput;
       
   134     }
       
   135 
       
   136     /**
       
   137      * Sets whether the XML declaration should be used in output
       
   138      *
       
   139      * @param  bool $useXmlDecl                     whether XML delcaration
       
   140      *                                              should be rendered
       
   141      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
       
   142      *                                              self
       
   143      */
       
   144     public function setUseXmlDeclaration($useXmlDecl)
       
   145     {
       
   146         $this->_useXmlDeclaration = (bool) $useXmlDecl;
       
   147         return $this;
       
   148     }
       
   149 
       
   150     /**
       
   151      * Returns whether the XML declaration should be used in output
       
   152      *
       
   153      * @return bool  whether the XML declaration should be used in output
       
   154      */
       
   155     public function getUseXmlDeclaration()
       
   156     {
       
   157         return $this->_useXmlDeclaration;
       
   158     }
       
   159 
       
   160     /**
       
   161      * Sets whether sitemap should be validated using Zend_Validate_Sitemap_*
       
   162      *
       
   163      * @param  bool $useSitemapValidators           whether sitemap validators
       
   164      *                                              should be used
       
   165      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
       
   166      *                                              self
       
   167      */
       
   168     public function setUseSitemapValidators($useSitemapValidators)
       
   169     {
       
   170         $this->_useSitemapValidators = (bool) $useSitemapValidators;
       
   171         return $this;
       
   172     }
       
   173 
       
   174     /**
       
   175      * Returns whether sitemap should be validated using Zend_Validate_Sitemap_*
       
   176      *
       
   177      * @return bool  whether sitemap should be validated using validators
       
   178      */
       
   179     public function getUseSitemapValidators()
       
   180     {
       
   181         return $this->_useSitemapValidators;
       
   182     }
       
   183 
       
   184     /**
       
   185      * Sets whether sitemap should be schema validated when generated
       
   186      *
       
   187      * @param  bool $schemaValidation               whether sitemap should
       
   188      *                                              validated using XSD Schema
       
   189      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
       
   190      *                                              self
       
   191      */
       
   192     public function setUseSchemaValidation($schemaValidation)
       
   193     {
       
   194         $this->_useSchemaValidation = (bool) $schemaValidation;
       
   195         return $this;
       
   196     }
       
   197 
       
   198     /**
       
   199      * Returns true if sitemap should be schema validated when generated
       
   200      *
       
   201      * @return bool
       
   202      */
       
   203     public function getUseSchemaValidation()
       
   204     {
       
   205         return $this->_useSchemaValidation;
       
   206     }
       
   207 
       
   208     /**
       
   209      * Sets server url (scheme and host-related stuff without request URI)
       
   210      *
       
   211      * E.g. http://www.example.com
       
   212      *
       
   213      * @param  string $serverUrl                    server URL to set (only
       
   214      *                                              scheme and host)
       
   215      * @throws Zend_Uri_Exception                   if invalid server URL
       
   216      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
       
   217      *                                              self
       
   218      */
       
   219     public function setServerUrl($serverUrl)
       
   220     {
       
   221         require_once 'Zend/Uri.php';
       
   222         $uri = Zend_Uri::factory($serverUrl);
       
   223         $uri->setFragment('');
       
   224         $uri->setPath('');
       
   225         $uri->setQuery('');
       
   226 
       
   227         if ($uri->valid()) {
       
   228             $this->_serverUrl = $uri->getUri();
       
   229         } else {
       
   230             require_once 'Zend/Uri/Exception.php';
       
   231             $e = new Zend_Uri_Exception(sprintf(
       
   232                     'Invalid server URL: "%s"',
       
   233                     $serverUrl));
       
   234             $e->setView($this->view);
       
   235             throw $e;
       
   236         }
       
   237 
       
   238         return $this;
       
   239     }
       
   240 
       
   241     /**
       
   242      * Returns server URL
       
   243      *
       
   244      * @return string  server URL
       
   245      */
       
   246     public function getServerUrl()
       
   247     {
       
   248         if (!isset($this->_serverUrl)) {
       
   249             $this->_serverUrl = $this->view->serverUrl();
       
   250         }
       
   251 
       
   252         return $this->_serverUrl;
       
   253     }
       
   254 
       
   255     // Helper methods:
       
   256 
       
   257     /**
       
   258      * Escapes string for XML usage
       
   259      *
       
   260      * @param  string $string  string to escape
       
   261      * @return string          escaped string
       
   262      */
       
   263     protected function _xmlEscape($string)
       
   264     {
       
   265         $enc = 'UTF-8';
       
   266         if ($this->view instanceof Zend_View_Interface
       
   267             && method_exists($this->view, 'getEncoding')
       
   268         ) {
       
   269             $enc = $this->view->getEncoding();
       
   270         }
       
   271 
       
   272         // TODO: remove check when minimum PHP version is >= 5.2.3
       
   273         if (version_compare(PHP_VERSION, '5.2.3', '>=')) {
       
   274             // do not encode existing HTML entities
       
   275             return htmlspecialchars($string, ENT_QUOTES, $enc, false);
       
   276         } else {
       
   277             $string = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&amp;', $string);
       
   278             $string = str_replace(array('<', '>', '\'', '"'), array('&lt;', '&gt;', '&#39;', '&quot;'), $string);
       
   279             return $string;
       
   280         }
       
   281     }
       
   282 
       
   283     // Public methods:
       
   284 
       
   285     /**
       
   286      * Returns an escaped absolute URL for the given page
       
   287      *
       
   288      * @param  Zend_Navigation_Page $page  page to get URL from
       
   289      * @return string
       
   290      */
       
   291     public function url(Zend_Navigation_Page $page)
       
   292     {
       
   293         $href = $page->getHref();
       
   294 
       
   295         if (!isset($href{0})) {
       
   296             // no href
       
   297             return '';
       
   298         } elseif ($href{0} == '/') {
       
   299             // href is relative to root; use serverUrl helper
       
   300             $url = $this->getServerUrl() . $href;
       
   301         } elseif (preg_match('/^[a-z]+:/im', (string) $href)) {
       
   302             // scheme is given in href; assume absolute URL already
       
   303             $url = (string) $href;
       
   304         } else {
       
   305             // href is relative to current document; use url helpers
       
   306             $url = $this->getServerUrl()
       
   307                  . rtrim($this->view->url(), '/') . '/'
       
   308                  . $href;
       
   309         }
       
   310 
       
   311         return $this->_xmlEscape($url);
       
   312     }
       
   313 
       
   314     /**
       
   315      * Returns a DOMDocument containing the Sitemap XML for the given container
       
   316      *
       
   317      * @param  Zend_Navigation_Container $container  [optional] container to get
       
   318      *                                               breadcrumbs from, defaults
       
   319      *                                               to what is registered in the
       
   320      *                                               helper
       
   321      * @return DOMDocument                           DOM representation of the
       
   322      *                                               container
       
   323      * @throws Zend_View_Exception                   if schema validation is on
       
   324      *                                               and the sitemap is invalid
       
   325      *                                               according to the sitemap
       
   326      *                                               schema, or if sitemap
       
   327      *                                               validators are used and the
       
   328      *                                               loc element fails validation
       
   329      */
       
   330     public function getDomSitemap(Zend_Navigation_Container $container = null)
       
   331     {
       
   332         if (null === $container) {
       
   333             $container = $this->getContainer();
       
   334         }
       
   335 
       
   336         // check if we should validate using our own validators
       
   337         if ($this->getUseSitemapValidators()) {
       
   338             require_once 'Zend/Validate/Sitemap/Changefreq.php';
       
   339             require_once 'Zend/Validate/Sitemap/Lastmod.php';
       
   340             require_once 'Zend/Validate/Sitemap/Loc.php';
       
   341             require_once 'Zend/Validate/Sitemap/Priority.php';
       
   342 
       
   343             // create validators
       
   344             $locValidator        = new Zend_Validate_Sitemap_Loc();
       
   345             $lastmodValidator    = new Zend_Validate_Sitemap_Lastmod();
       
   346             $changefreqValidator = new Zend_Validate_Sitemap_Changefreq();
       
   347             $priorityValidator   = new Zend_Validate_Sitemap_Priority();
       
   348         }
       
   349 
       
   350         // create document
       
   351         $dom = new DOMDocument('1.0', 'UTF-8');
       
   352         $dom->formatOutput = $this->getFormatOutput();
       
   353 
       
   354         // ...and urlset (root) element
       
   355         $urlSet = $dom->createElementNS(self::SITEMAP_NS, 'urlset');
       
   356         $dom->appendChild($urlSet);
       
   357 
       
   358         // create iterator
       
   359         $iterator = new RecursiveIteratorIterator($container,
       
   360             RecursiveIteratorIterator::SELF_FIRST);
       
   361 
       
   362         $maxDepth = $this->getMaxDepth();
       
   363         if (is_int($maxDepth)) {
       
   364             $iterator->setMaxDepth($maxDepth);
       
   365         }
       
   366         $minDepth = $this->getMinDepth();
       
   367         if (!is_int($minDepth) || $minDepth < 0) {
       
   368             $minDepth = 0;
       
   369         }
       
   370 
       
   371         // iterate container
       
   372         foreach ($iterator as $page) {
       
   373             if ($iterator->getDepth() < $minDepth || !$this->accept($page)) {
       
   374                 // page should not be included
       
   375                 continue;
       
   376             }
       
   377 
       
   378             // get absolute url from page
       
   379             if (!$url = $this->url($page)) {
       
   380                 // skip page if it has no url (rare case)
       
   381                 continue;
       
   382             }
       
   383 
       
   384             // create url node for this page
       
   385             $urlNode = $dom->createElementNS(self::SITEMAP_NS, 'url');
       
   386             $urlSet->appendChild($urlNode);
       
   387 
       
   388             if ($this->getUseSitemapValidators() &&
       
   389                 !$locValidator->isValid($url)) {
       
   390                 require_once 'Zend/View/Exception.php';
       
   391                 $e = new Zend_View_Exception(sprintf(
       
   392                         'Encountered an invalid URL for Sitemap XML: "%s"',
       
   393                         $url));
       
   394                 $e->setView($this->view);
       
   395                 throw $e;
       
   396             }
       
   397 
       
   398             // put url in 'loc' element
       
   399             $urlNode->appendChild($dom->createElementNS(self::SITEMAP_NS,
       
   400                                                         'loc', $url));
       
   401 
       
   402             // add 'lastmod' element if a valid lastmod is set in page
       
   403             if (isset($page->lastmod)) {
       
   404                 $lastmod = strtotime((string) $page->lastmod);
       
   405 
       
   406                 // prevent 1970-01-01...
       
   407                 if ($lastmod !== false) {
       
   408                     $lastmod = date('c', $lastmod);
       
   409                 }
       
   410 
       
   411                 if (!$this->getUseSitemapValidators() ||
       
   412                     $lastmodValidator->isValid($lastmod)) {
       
   413                     $urlNode->appendChild(
       
   414                         $dom->createElementNS(self::SITEMAP_NS, 'lastmod',
       
   415                                               $lastmod)
       
   416                     );
       
   417                 }
       
   418             }
       
   419 
       
   420             // add 'changefreq' element if a valid changefreq is set in page
       
   421             if (isset($page->changefreq)) {
       
   422                 $changefreq = $page->changefreq;
       
   423                 if (!$this->getUseSitemapValidators() ||
       
   424                     $changefreqValidator->isValid($changefreq)) {
       
   425                     $urlNode->appendChild(
       
   426                         $dom->createElementNS(self::SITEMAP_NS, 'changefreq',
       
   427                                               $changefreq)
       
   428                     );
       
   429                 }
       
   430             }
       
   431 
       
   432             // add 'priority' element if a valid priority is set in page
       
   433             if (isset($page->priority)) {
       
   434                 $priority = $page->priority;
       
   435                 if (!$this->getUseSitemapValidators() ||
       
   436                     $priorityValidator->isValid($priority)) {
       
   437                     $urlNode->appendChild(
       
   438                         $dom->createElementNS(self::SITEMAP_NS, 'priority',
       
   439                                               $priority)
       
   440                     );
       
   441                 }
       
   442             }
       
   443         }
       
   444 
       
   445         // validate using schema if specified
       
   446         if ($this->getUseSchemaValidation()) {
       
   447             if (!@$dom->schemaValidate(self::SITEMAP_XSD)) {
       
   448                 require_once 'Zend/View/Exception.php';
       
   449                 $e = new Zend_View_Exception(sprintf(
       
   450                         'Sitemap is invalid according to XML Schema at "%s"',
       
   451                         self::SITEMAP_XSD));
       
   452                 $e->setView($this->view);
       
   453                 throw $e;
       
   454             }
       
   455         }
       
   456 
       
   457         return $dom;
       
   458     }
       
   459 
       
   460     // Zend_View_Helper_Navigation_Helper:
       
   461 
       
   462     /**
       
   463      * Renders helper
       
   464      *
       
   465      * Implements {@link Zend_View_Helper_Navigation_Helper::render()}.
       
   466      *
       
   467      * @param  Zend_Navigation_Container $container  [optional] container to
       
   468      *                                               render. Default is to
       
   469      *                                               render the container
       
   470      *                                               registered in the helper.
       
   471      * @return string                                helper output
       
   472      */
       
   473     public function render(Zend_Navigation_Container $container = null)
       
   474     {
       
   475         $dom = $this->getDomSitemap($container);
       
   476 
       
   477         $xml = $this->getUseXmlDeclaration() ?
       
   478                $dom->saveXML() :
       
   479                $dom->saveXML($dom->documentElement);
       
   480 
       
   481         return rtrim($xml, PHP_EOL);
       
   482     }
       
   483 }