В который раз убеждаюсь, что у программистов мысли рождаются одинаково) Честно, не этот пост послужил отправной точкой для создания нижеследующего пакета) Реально параллельно мыли шли)

Вообщем решил собрать по сусекам все что у меня есть полезного и нужного для СЕО оптимизации и объединить в один пакет.

Что мы умеем?

  • Проставлять alt длля картинок согласно шаблону, с порядковым номеро
  • Проставлять title для ссылок согласно содержанию, а если там пустота — ставить свой текст по шаблону, также с порядковым номером
  • Вытягивать код в одну строку (спасибо Agel_Nash), но оставлять нетронутым тэг pre (лично я не пользовался режением Евгения, именно из-за этого)
  • Убирать внешние ссылки, но оставлять возможность перехода по ним (не забываем в роботе прописать Disallow: /exit.php)
  • Убирать циклиечские ссылки (т.е.ссылки на самих себя) — где-то слышал, что это не зер гуд
  • Формировать ключевики из тэгов h1-h6,b,i,strong и докидывать получившиеся к основным

В принципе и не так много, но весьма полезно.

Минусы решения:
  • Т.к. идет постобработка, то увеличивается время генерации страницы. Хотя я разницы особо и не заметил, ибо там простые, по-сути, математические действия

Плюсы:
  • Установил и оно работает
  • Работает как со старыми, так и с новыми документами, не нужно ничего пересохранять
  • Смотрит не только в поле контента, а по всей странице. Поэтому дополняет «чанки, шаблоны и прочие телевизоры)»
  • Делает MODX реально лучшим движком для СЕО)


Установка:
Качаем отсель библиотеку simple_html_dom и кидаеи ее в папочку /assets/lib/ (нам нужен тольк один файлик — simple_html_dom.php), все остальное примеры и доки.
Создаем плагин, называем SEOPack, ставим события OnWebPagePrerender, OnPageNotFound
Вставляем нижеследующий код:

$error_page = 'exit.php'; //Уникальное название страницы, куда первоначально будем делать редирект при внешнем ресурсе 
	
	$e =&$modx->Event;
	
	function compress_html($compress)
	{
		// Подсмотренно у Agel Nash
		$compress = str_replace("\n", '', $compress);
		$compress = str_replace("\s", '', $compress);
		$compress = str_replace("\r", '', $compress);
		$compress = str_replace("\t", '', $compress);
		$compress = preg_replace('/(?:(?<=\>)|(?<=\/\>))\s+(?=\<\/?)/', '', $compress);
		$compress = preg_replace('/[\t\r]\s+/', ' ', $compress);
		$compress = preg_replace('/<!(--)([^\[|\|])^(<!-->.*<!--.*-->)/', '', $compress);
		$compress = preg_replace('/\/\*.*?\*\//', '', $compress);
		return preg_replace("#\\s+#ism"," ",$compress);
	}
	
	if ($e->name=='OnWebPagePrerender')
	{
		
		
		
		$content = $modx->Event->params['documentOutput'];
		
		require_once MODX_BASE_PATH.'assets/lib/simple_html_dom.php';
		$html = new simple_html_dom();
		$html->load($content, false, null, -1, -1, true, true, DEFAULT_TARGET_CHARSET, false); 
		
		$title = $html->find('title',0);	
		$metaTitle = str_replace("'","'",$title->plaintext); // Чтобы не искать потом - заголовок страницы
		$imagealt = $metaTitle.'. Картинка '; //в конце подставляется порядковый номер начиная с 1
		$atitle = $metaTitle.' Ссылка '; //в конце подставляется порядковый номер начиная с 1
		
		//Проставляем тэг title для ссылок
		$links = $html->find("a"); 
		$ln = 1;	
		foreach ($links as $key => $link)
		{
			
			if (!$link->title)
			{
				if ($link->plaintext) 
				{
					
					$t = str_replace('"',"'",trim(mb_substr($link->plaintext,0,50)));
					if (strlen($t)<10)
					{
						$link->title = $atitle.' '.$ln;
						$ln++;
					}
					else $link->title = $t;
				}
				else 
				{
					$link->title = $atitle.' '.$ln;
					$ln++;
				}
			}
		}
		
		//Убираем циклические ссылки
		$url = substr($_SERVER['REQUEST_URI'], 1);
		
		if ($url)
		{
			$cu =  $html->find("a[href='".$url."']"); 
			foreach($cu as $u) $u->href = null;
		}
		
		//Закрываем внешние ссылки
		$outlink = $html->find("a[href^=http]");
		foreach($outlink as $ou) if (strpos($ou->href, MODX_SITE_URL)===false) $ou->href = MODX_SITE_URL.''.$error_page.'?url='.$ou->href;
		
		
		
		//Проставляем тэг alt для картинок	
		$imgs = $html->find("img"); 
		$ln = 1;	
		foreach ($imgs as $key => $img)
		{
			
			if (!$img->alt) 
			{
				$img->alt = $imagealt.' '.$ln;
				$ln++;
			}
			
		}
		
		//Генерим ключевики по тэгам
		
		$mk = $html->find('meta[name=keywords]',0);
		if (count($mk))		
		{
			$min = isset($min) ? $min : 4; //минимальное количество символов в слове при выборке
			$limit = isset($limit) ? $limit : 20; // сколько слов выводить

			$keywords = [];
			$keyws = $html->find("h1,h2,h3,h4,h5,h6,b,i,strong");
			foreach($keyws as $keyw) $keywords[]=$keyw->plaintext;

			$str = implode(',',$keywords);		
			$str = strip_tags($str);
			$str2 = preg_replace('/[^a-zA-Zа-яА-Я0-9\s]/ui', ' ',$str ); 
			$str2 = str_replace('  ',' ',$str2);
			$str2 = str_replace(PHP_EOL,' ',$str2);
			$arr = explode(' ',$str2);
			foreach($arr as $key=>$val) if ($val) $arr[$key] = mb_strtolower(trim($val),'utf-8');

			//Выбираем наиболее часто встречающиеся
			$array_words = array_count_values($arr);
			arsort($array_words);
			$out = array();
			foreach($array_words as $word => $val)
			{
				if (mb_strlen($word,'utf-8')>=$min) $out[]=$word;
				if (count($out)==$limit) break;
			}
			$mk->content = mb_substr($mk->content.','.implode(',',$out), 0, 200);
		}	
		
		// Хак для того, чтобы не сжимать тэг <pre>	
		$pre = $html->find("pre"); 
		if (count($pre)) 
		{
			$arr_pre = [];
			foreach ($pre as $p) $arr_pre[] = $p->outertext;
		}
		
		
		$content = $html->save();
		$html->clear();	
		
		$content = compress_html($content); //Вытягиваем в 1-у строку
		
		// Хак для того, чтобы не сжимать тэг <pre>
		if (count($pre)) 
		{	
			preg_match_all('~<pre(.*?)pre>~s', $content, $matches);
			foreach ($matches[0] as $key => $pre) $content = str_replace($pre,$arr_pre[$key],$content);
		}
		
		
		
		
		
		
		$modx->Event->output($content);
	}
	
	if ($e->name=='OnPageNotFound')
	{
		$q = $modx->db->escape($_REQUEST['q']);
		if (isset($_GET['url']) && (!empty($_GET['url'])) && ($q==$error_page))
		{
			$url = $_GET['url'];
			if (!preg_match('#(https?|ftp)://\S+[^\s.,>)\];\'\"!?]#i',$url)) 
			{
				exit ("<p>Неверный формат запроса! Проверьте URL!</p>");
			} 
			header("Location:$url");
			exit();
		}
	}	

Наслаждаемся результатом.

Да. Проверял уже на обновленных движках; на старых, у меня че-та не хотел срабатывать вывод на плагине OnWebPagePrerender, решил тогда echo $content; exit(); Ну это так, на всякий случай. Ну и если будут желающие задонатить, то как бы я не против)
Ну а так, предоставляю как есть. Будут всплывать баги — будем править. Если кому-то нужна какая-то тонка настройка — обращайтесь в личку.
Не реклама, но можете глянуть плагин в действии на моем сайте liber.pro