Описание

Sitemap — простой способ для вебмастера показать поисковой системе страницы сайта, которые необходимо сканировать. Для этого описан протокол Sitemap, который используется во многих поисковых системах, включая Google, Yahoo и MSN.

Работает это просто: в корень сервера кладется файл sitemap.xml, содержащий ссылки на разделы сайта и сведения о том, как часто они обновляются. Например: http://www.sinacos.com/sitemap.xml.

Ниже представлен класс для php5, который поможет Вам сгенерировать файл Sitemap.

Пример использования


// Заменяем следующую строку на актуальный метод получения разделов сайта
// для поисковой системы, в виде списка
$sections = Site::getSectionsList();

$sitemap = new Sitemap();

// Устанавливаем папку, в которую нужно будет положить файл sitemap.xml
$sitemap->setDocumentRoot($_SERVER['DOCUMENT_ROOT']);

foreach ($sections as $section)
{
	// По очереди добавляем адреса в карту сайта
	$sitemap->addUrl(
		new SitemapUrl(
			$section->url,
			time(),
			SitemapUrl::CHANGE_FREQUENCY_WEEKLY,
			0.5
		)
	);
}

// Создаем файл sitemap.xml
$sitemap->generate();

Методы класса Sitemap

void setDocumentRoot(string $_path);
Устанавливает адрес папки для генерации файла sitemap.xml

Параметр Описание
$_path Адрес корневой папки сайта, в которую будет записываться файл sitemap.xml.
void addUrl(SitemapUrl $_url);
Добавляет в карту сайта объект SitemapUrl, описывающий элемент карты сайта.

Параметр Описание
$_url Ссылка на раздел сайта с датой последнего обновления, периодичностью обновлений и приоритетом.
void generate();
Генерирует и записывает файл sitemap.xml

Методы класса SitemapUrl

SitemapUrl __construct(string $_location [, int $_modification_date [, string $_change_frequency [, float $_priority ]]]);

Параметр Описание
$_location Url страницы сайта.
$_modification_date Дата последнего обновления страницы. По умолчанию — текущая дата.
$_change_frequency Предполагаемая периодичность обновления страницы. Доступны следующие значения:
  • SitemapUrl::CHANGE_FREQUENCY_ALWAYS
  • SitemapUrl::CHANGE_FREQUENCY_HOURLY
  • SitemapUrl::CHANGE_FREQUENCY_DAILY
  • SitemapUrl::CHANGE_FREQUENCY_WEEKLY
  • SitemapUrl::CHANGE_FREQUENCY_MONTHLY
  • SitemapUrl::CHANGE_FREQUENCY_YEARLY
  • SitemapUrl::CHANGE_FREQUENCY_NEVER
Значение по умолчанию — SitemapUrl::CHANGE_FREQUENCY_WEEKLY.
$_priority Приоритет этой страницы по отношению к остальным при сканировании сайта поисковой системой. Может помочь выделить страницу, как основную, из страниц, похожих по содержанию. Может принимать значения от 0.0 до 1.0. Значение по умолчанию — 0.5.

Листинг


class Sitemap
{
	private $document_root;
	private $urls = array();
	
	public function __construct()
	{
		if (isset($_SERVER['DOCUMENT_ROOT']))
			$this->setDocumentRoot($_SERVER['DOCUMENT_ROOT']);
	}
	
	public function setDocumentRoot($_path)
	{
		$_path = rtrim($_path) . '/';
		if (!is_dir($_path))
			throw new Exception('Directory ' . $_path . ' doesnt exist.');
		$this->document_root = $_path;
	}
	
	public function addUrl(SitemapUrl $_url)
	{
		$this->urls[] = $_url;
	}
	
	public function getXml()
	{
		$result = '<?xml version="1.0" encoding="UTF-8"?>'
			. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
		
		foreach ($this->urls as $item)
		{
			$result .= $item->getXml();
		}
			
		$result .= '</urlset>';
		
		return $result;
	}
	
	private function getFilePath()
	{
		if (!$this->document_root)
			throw new Exception('Document root isnt specified.');
		return $this->document_root . 'sitemap.xml';
	}
	
	public function generate()
	{
		file_put_contents($this->getFilePath(), $this->getXml());
	}
}

class SitemapUrl
{
	const DEFAULT_PRIORITY = 0.5;
	
	const CHANGE_FREQUENCY_ALWAYS = 'always';
	const CHANGE_FREQUENCY_HOURLY = 'hourly';
	const CHANGE_FREQUENCY_DAILY = 'daily';
	const CHANGE_FREQUENCY_WEEKLY = 'weekly';
	const CHANGE_FREQUENCY_MONTHLY = 'monthly';
	const CHANGE_FREQUENCY_YEARLY = 'yearly';
	const CHANGE_FREQUENCY_NEVER = 'never';
	
	private $location;
	private $modification_date;
	private $change_frequency;
	private $priority;
	
	public function __construct($_location, $_modification_date = null, $_change_frequency = null, $_priority = null)
	{
		$this->setLocation($_location);
		$this->setModificationDate(!is_null($_modification_date) ? $_modification_date : time());
		$this->setChangeFrequency(!is_null($_change_frequency) ? $_change_frequency : self::CHANGE_FREQUENCY_WEEKLY);
		$this->setPriority(!is_null($_priority) ? $_priority : 0.5);
	}
	
	public function setLocation($_str)
	{
		$this->location = $_str;
	}
	
	public function setModificationDate($_date)
	{
		$this->modification_date = $_date;
	}
	
	public function setChangeFrequency($_value)
	{
		if (!in_array($_value, self::getAvaliableFrequencies()))
			throw new Exception($_value . ' - is not allowed frequency value. Allowed values are '
				. implode(', ', self::getAvaliableFrequencies()) . '.');
		$this->change_frequency = $_value;
	}
	
	public function setPriority($_value)
	{
		if ((float) $_value < 0 || (float) $_value > 1)
			throw new Exception('Priority value must be in range from 0.0 to 1.0.');
		$this->priority = number_format((float) $_value, 1, '.', '');
	}
	
	public static function getAvaliableFrequencies()
	{
		$result = array();
		$reflection = new ReflectionClass(__CLASS__);
		foreach ($reflection->getConstants() as $const_name => $const_value)
		{
			if (substr($const_name, 0, 17) == 'CHANGE_FREQUENCY_')
			{
				$result[] = $const_value;
			}
		}
		return $result;
	}
	
	public function getXml()
	{
		return '<url>'
			. '<loc>' . $this->location . '</loc>'
			. '<lastmod>' . date('c', $this->modification_date) . '</lastmod>'
			. '<changefreq>' . $this->change_frequency . '</changefreq>'
			. '<priority>' . $this->priority . '</priority>'
			. '</url>';
	}
}

При этом, формат Sitemap имеет некоторые ограничения. Например, файл sitemap.xml не должен весить более 10MB. Если у Вас большой сайт, предлагающий для индексирования больше 500 страниц, то формат позволяет использовать сжатие gzip. Кроме того, есть возможность разбить карту сайта на несколько файлов Sitemap, используя Sitemap index.

Об этих тонкостях можно почитать на странице описания протокола. Возможно, в скором будущем и они будут реализованы в этом классе.