Как получить 100/100 Google PageSpeed Insights и не подать виду

Один из заказчиков попросил меня немного причесать сайт, заодно прописать цели для Яндекс.Метрики и Google Analytics. Между делом он рассказал мне, что предыдущим «оптимизаторам» удалось разогнать сайт до 100 баллов в PageSpeed Insight, мол, работа по оптимизации была долгая и значение упорно не хотело расти, но однажды подскочило до 100 и на этом закончили.

Я был немного удивлен услышанным, т.к. это был сайт на Joomla на бюджетном шаред-хостинге, с косяками адаптивной версии, неоптимизированными прозрачными PNG-шками по несколько мегабайт весом. В перерыве между работой ради интереса решил замерить баллы, правда через встроенный в Google Chrome Lighthouse.

Читать далее Как получить 100/100 Google PageSpeed Insights и не подать виду

Как работают «оптимизаторы» в «SEO-компании №1» или небольшой отзыв на Demis Group

Есть у меня товарищ с сайтом на Joomla, который я сделал год назад. Т.к. я не занимался и не занимаюсь продвижением сайтов, на вопрос о повышении позиций в поисковиках, порекомендовал товарищу самостоятельно найти компанию, которая профессионально этим занимается.

logo

Такая компания нашлась, называется она Demis Group. Предоставили им доступы и начали работу. Тут стоит отметить, что все это время ко мне не обращались с правками, поэтому я не застал процесс «оптимизации», и очень зря, как оказалось.

Проблемы обнаружились, когда меня попросили сделать адаптивную версию сайта. Клиенту оптимизатор посоветовал, мол, Гуглу нравятся такие сайты, они выше в выдаче, да и в целом удобнее. Сайт тогда мы сделали фиксированным, был ограничен бюджет. Ну не проблема, сказано — сделано!

Открываю сайт, смотрю внешне, в админке. Параллельно запускаю PhpStorm, гружу проект к себе на компьютер с SFTP, ведь были изменения со стороны оптимизатора. Смотрю на тайтлы, как были при мне, такими же и остались. WTF? Где продвижение? В админке, в пунктах меню все тоже самое. Никаких компонентов SEO не установлено. Ооокей, смотрю корень сайта и нахожу чужеродные файлы:

_adminer.php
_varvara.php
d-seo.php
d-url-rewriter.php

Думаю, некоторые по названию уже начали понимать, с чем мне пришлось столкнуться.

_adminer.php — это менеджер БД, взят отсюда http://www.adminer.org/
_varvara.php — самописная хрень, которая делает поиск и замену во всех файлах на хостинге

Еще раз, Демису были предоставлены все доступы к хостингу. Что мешало воспользоваться встроенным у хостера PhpMyAdmin? Зачем устанавливать потенциально опасные скрипты, без спроса, какая в этом необходимость? Абсолютно непрофессиональный подход к делу, даже опасный.

Ок, смотрим файлы d-seo.php.

/**
* Оптимизаторский файл. Подключать только include_once!!! Не забываем global $aSEOData, где нужно.
*
* if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/d-seo.php')) {
* include_once($_SERVER['DOCUMENT_ROOT'] . '/d-seo.php');
* }
*
* Изменяемые параметры массива $aSEOData (квадратными скобками выделены неактивные)
* title - title страницы
* descr - meta descr
* keywr - meta keywords
*
*/

//Глобальные значения (по умолчанию)
$aSEOData['title'] = '';
$aSEOData['descr'] = '';
$aSEOData['keywr'] = '';
$aSEOData['h1'] = '';
$aSEOData['text-top'] = '';
$aSEOData['text-bot'] = '';

//Определяем адрес (REQUEST_URI есть не всегда)
$sSEOUrl = $_SERVER['REQUEST_URI'];
//$sSEOUrl = DUR_REQUEST_URI;

//Собственно вариации для страниц
switch ($sSEOUrl) {
case '/': // Добавлено 28 January 2015 в 10:36
$aSEOData['title'] = 'Металлопрокат — купить в «МТК» в Новосибирске | Цены на металлопрокат без надбавок';
$aSEOData['descr'] = 'В ООО «МТК» вы можете купить металлопрокат оптом и в розницу. Цены свободны от лишних надбавок. На всю металлопродукцию действует гарантия качества. Телефоны в Новосибирске: (383) 361-26-34, (383) 361-21-74.';
$aSEOData['keywr'] = 'купить металлопрокат в новосибирске цена стоимость металл';
break;

case '/metalloprokat/chernyi-prokat.html': // Добавлено 28 January 2015 в 10:36
$aSEOData['title'] = 'Черный металлопрокат — купить в «МТК» в Новосибирске | Продажа черного металлопроката: цены';
$aSEOData['descr'] = 'В ООО «МТК» вы можете купить черный металлопрокат. Цены на черный металлопрокат представлены на сайте. Телефоны в Новосибирске: (383) 361-26-34, (383) 361-21-74.';
$aSEOData['keywr'] = 'черный черный металлопрокат новосибирск цена продажа';
break;

case '/metalloprokat/listovoy-prokat.html': // Добавлено 28 January 2015 в 10:36
$aSEOData['title'] = 'Листовой прокат — купить в «МТК» в Новосибирске | Продажа листового проката: цены';
$aSEOData['descr'] = 'В ООО «МТК» вы можете купить листовой прокат. Цены на листовой прокат представлены на сайте. Телефоны в Новосибирске: (383) 361-26-34, (383) 361-21-74.';
$aSEOData['keywr'] = 'листовой прокат купить продажа цена';
$aSEOData['h1'] = 'Листовой прокат с доставкой по Новосибирску';
break;

case '/metalloprokat/armaturniy-prokat.html': // Добавлено 28 January 2015 в 10:36
$aSEOData['title'] = 'Стальная арматура — продажа в «МТК» в Новосибирске | Арматурный прокат: цены, характеристики';
$aSEOData['descr'] = 'В ООО «МТК» вы можете купить стальную арматуру. Цены на арматурный прокат представлены на сайте. Телефоны в Новосибирске: (383) 361-26-34, (383) 361-21-74.';
$aSEOData['keywr'] = 'арматура стальная цена арматурный прокат';
$aSEOData['h1'] = 'Арматурный прокат разных типов';
break;

case '/metalloprokat/trubniy-prokat.html': // Добавлено 28 January 2015 в 10:36
$aSEOData['title'] = 'Трубный прокат: цены, характеристики | Продажа трубного металлопроката в «МТК» в Новосибирске';
$aSEOData['descr'] = 'В ООО «МТК» вы можете купить трубный прокат. Цены на трубный прокат представлены на сайте. Телефоны в Новосибирске: (383) 361-26-34, (383) 361-21-74.';
$aSEOData['keywr'] = 'трубный прокат';
$aSEOData['h1'] = 'Трубный прокат различных типоразмеров';
break;

case '/metalloprokat/perfometall.html': // Добавлено 28 January 2015 в 10:36
$aSEOData['title'] = 'Перфорированный металл — продажа в «МТК» в Новосибирске | Продажа перфометалла';
$aSEOData['descr'] = 'В ООО «МТК» вы можете купить перфорированный металл. Цены на перфометалл представлены на сайте. Телефоны в Новосибирске: (383) 361-26-34, (383) 361-21-74.';
$aSEOData['keywr'] = 'перфорированный металл цена перфометалл';
$aSEOData['h1'] = 'Легкий и износостойкий перфометалл по индивидуальным эскизам';
break;

case '/metalloprokat/nerzhaveushiy-prokat.html': // Добавлено 28 January 2015 в 10:36
$aSEOData['title'] = 'Нержавеющий металлопрокат — продажа в Новосибирске | Прокат из нержавеющей стали';
$aSEOData['descr'] = 'В ООО «МТК» вы можете купить нержавеющий металлопрокат. Цены на нержавеющий металлопрокат представлены на сайте. Телефоны в Новосибирске: (383) 361-26-34, (383) 361-21-74.';
$aSEOData['keywr'] = 'нержавеющий металлопрокат новосибирск цена';
$aSEOData['h1'] = 'Нержавеющий металлопрокат';
break;

default:
break;
}

// // генерация мета-тегов
// $arExceptions = array (
// '/catalogue/shiny/dlya-pogruzchikov/',
// '/catalogue/arenda-pogruzchikov/',
// );

//if(!in_array($sSEOUrl, $arExceptions)) {
if (empty($aSEOData['title']) or empty($aSEOData['descr'])) {
if (preg_match('%<h1[^>]*>(.*)</h1>%siU', $sContent, $h1)) {
//чистим h1 от лишних тегов
$h1[1] = preg_replace('%<[^>]*>%siU', '', $h1[1]);
$h1[1] = trim($h1[1]);

//if (empty($aSEOData['title']))
// $aSEOData['title'] = $h1[1] .' | Мебель для офиса';

if (empty($aSEOData['descr']))
$aSEOData['descr'] = 'Продажа изделий черного металлопроката, услуги раскройки металла, помощь в транспортировке. '. $h1[1] .'. Наш телефон в Новосибирске: +7 (383) 361-26-34';
}
}
//}

///РЕПЛЕЙСЫ
///////////

// вставка текста до каталога
// if (isset($aSEOData['text-top']) && !empty($aSEOData['text-top'])) {
// $sContent = str_replace('<!--text-top-->', '<div id="text-top" class="seo">'.$aSEOData['text-top'].'</div>', $sContent);
// }
// // вставка текста после каталога
// if (isset($aSEOData['text-bot']) && !empty($aSEOData['text-bot'])) {
// $sContent = str_replace('<!--text-bot-->', '<div id="text-bot" class="seo">'.$aSEOData['text-bot'].'</div>', $sContent);
// }

// замена тегов на css (не расскоменчивать - пропадают заголовки)
//$sContent = preg_replace('%<h3 class="chg-to-css">(.*)</h3>%siU', '<div class="kak_h3">$1</div>', $sContent);

// цели
$sContent = str_replace('<input type="submit" value="Отправить" name="form[send]" id="send" class="rsform-submit-button" />',
'<input type="submit" value="Отправить" name="form[send]" id="send" class="rsform-submit-button" onclick="yaCounter25698332.reachGoal(\'zvonok\'); ga(\'send\', \'event\', \'knopka\', \'звонок\'); return true;"/>',
$sContent);

// реплейсы для конкретных страниц
// switch ($sSEOUrl) {
// case '/index.php':
// break;
// case '/kontakty.php':
// break;
// }

//Обработка
function changeHeadBlock ($sContent, $sRegExp, $sBlock) {
if (preg_match($sRegExp, $sContent)) {
return preg_replace($sRegExp, $sBlock, $sContent);
}
else {
return str_replace('<head>', '<head>' . $sBlock, $sContent);
}
}
if(isset($_SERVER['X_DUSYA']) || isset($_SERVER['HTTP_X_DUSYA'])) {
$sContent = str_replace('<head>', '<head><!--origUrl="' . $sSEOUrl . '"-->' , $sContent);
}
if (isset($aSEOData['title']) && !empty($aSEOData['title'])) {
$aSEOData['title'] = htmlspecialchars($aSEOData['title']);
$sContent = changeHeadBlock($sContent, '#<title>.*</title>#siU', '<title>' . $aSEOData['title'] . '</title>');
}
if (isset($aSEOData['descr']) && !empty($aSEOData['descr'])) {
$aSEOData['descr'] = htmlspecialchars($aSEOData['descr']);
$sContent = changeHeadBlock($sContent, '#<meta[^>]+name[^>]{1,7}description[^>]*>#siU', '<meta name="description" content="' . $aSEOData['descr'] . '" />');
}
if (isset($aSEOData['keywr']) && !empty($aSEOData['keywr'])) {
$aSEOData['keywr'] = htmlspecialchars($aSEOData['keywr']);
$sContent = changeHeadBlock($sContent, '#<meta[^>]+name[^>]{1,7}keywords[^>]*>#siU', '<meta name="keywords" content="' . $aSEOData['keywr'] . '" />');
}
if (isset($aSEOData['h1']) && !empty($aSEOData['h1'])) {
$sContent = preg_replace('#(<h1[^>]*>).*(</h1>)#siU', '$1'.$aSEOData['h1'].'$2', $sContent);
}

 

Великолепно, Demis! Заместо того, чтобы воспользоваться встроенными SEO функциями Joomla, мы сделаем свой велосипед!

velosiped-sdelay-samЧто, если клиент поменяет алиас URL у пункта меню? Ничего страшного, мы ведь об этом не узнаем, да и клиент не сразу догадается об отвалившихся тайтлах, а если и узнает, ну так что стоит зайти и исправить? Ну да, клиент сам не полезет в этот ужас, для этого ведь существуем мы!

Теперь файл d-url-rewriter.php

// Раздел настройки ЧПУ (Все пути начинаются с ведущего слеша от корневой директории)
$aURLRewriter = array (
// '/test1' => '/test-OK',
);
//Сквозные редиректы
$aR301SkipCheck = array (
// '/test2' => '/test-OK',
'/sitemap' => '/sitemap.html',
);
//Удаленные страницы
$a410Response = array (
// '/test3',
'/contacts.html/',
);
$a404Response = array (
// '/test4',
);
// if(in_array($_SERVER['REQUEST_URI'], $a404)) {
// header("HTTP/1.1 404 Not Found");
// echo file_get_contents('http://uitek.ru/rus/404');
// exit;
// }
// Только замена ссылок
$aURLRewriterOnly = array (
// '/test5' => '/test-OK',
);

define('DUR_DEBUG', 0); //Включение режима отладки (вывод инфо в конце исходного текста на странице)
define('DUR_PREPEND_APPEND', 0); //Единая точка входа (.htaccess) Не рекомендуется
define('DUR_BASE_ROOT', 0); //Прописать принудительно <base href="http://domain.com/"> Бывает полезно при ссылках вида href="?page=2". При указании строки, пропишет ее
define('DUR_LINK_PARAM', 0); //Дописать путь перед ссылками вида href="?page=2"
define('DUR_ANC_HREF', 0); //Пофиксить ссылки вида href="#ancor"
define('DUR_ROOT_HREF', 1); //Пофиксить ссылки вида href="./"
define('DUR_REGISTER_GLOBALS', 0); //Регистрировать глобальные переменные
define('DUR_SKIP_POST', 1); //Не выполнять подмену при запросе POST
define('DUR_CMS_TYPE', 'NONE'); //Включение особенностей для CMS, возможные значения: NONE, NETCAT, JOOMLA, HTML, DRUPAL, WEBASYST, ICMS
define('DUR_OUTPUT_COMPRESS', 'NONE'); //Сжатие выходного потока, возможные значения: NONE, GZIP, DEFLATE, AUTO, SKIP
define('DUR_SUBDOMAINS', 0); //Обрабатывать поддомены, указываем здесь основной домен!
define('DUR_SKIP_USERAGENT', '#^(|mirror)$#'); //Не выполнять редиректы при указанном HTTP_USER_AGENT (регулярка)
define('DUR_SKIP_URLS', '#^/_?(admin|manag|bitrix|indy|cms|phpshop|varvara.php|captcha|jscripts/|modules|includes|templates|wp-admin)#siU'); //Skip URLS
define('DUR_FIX_CONTLEN', 1); //Фиксить Content-Length
define('DUR_PATHINFO', 0); //Регистрировать переменные для передачи вида /index.php/uri

///new
define('DUR_FIX_RELATIVE', 1); //Фиксить относительные ссылки (только для DUR_MAIN_CYCLE = ortodox)
define('DUR_FIX_DOTTED', 0); //Фиксить ссылки от "./" (только для DUR_MAIN_CYCLE = ortodox)
define('DUR_FIX_HTTP_HOST', $_SERVER['HTTP_HOST']); //Фиксить HTTP_HOSTв ссылках, прописываем, например, значение "www.mysite.ru", чтобы сократить количество host-зависимых подмен ссылок
define('DUR_CACHE_REWRITED', 0); //Кэшировать все замены в этом рерайтере, должна быть создана папка d-cache в корне с правами на запись
define('DUR_CACHE_MEMORY', 40960000); //Критическая масса кеша (в байтах), при превышении этого значения кеш очищается
define('DUR_CACHE_TIME', 3); //Критическое время жизни кеша, при превышении этого значения кеш очищается
define('DUR_MAIN_CYCLE', 'ortodox'); //Константа для выбора типа основного цикла, значения: callback, str_replace, ortodox

// Раздел обработки
define('DUR_TIME_START', microtime(true));
define('DUR_REQUEST_URI', $_SERVER['REQUEST_URI']);
define('DUR_HTTP_HOST', $_SERVER['HTTP_HOST']);
define('DUR_FULL_URI', $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
define('BX_COMPRESSION_DISABLED', true); //Hack for bitrix
define('DUR_SKIP_THIS', preg_match(DUR_SKIP_URLS, DUR_REQUEST_URI, $aM));
define('DUR_SKIP_R301', !isset($_SERVER['HTTP_USER_AGENT']) || preg_match(DUR_SKIP_USERAGENT, $_SERVER['HTTP_USER_AGENT']));
if (defined('DUR_DEBUG') && DUR_DEBUG) {
ini_set('display_errors', 1);
ini_set('error_reportings', E_ALL);
}
if (isset($_GET['_openstat'])) {
unset($_GET['_openstat']);
unset($_REQUEST['_openstat']);
unset($HTTP_GET_VARS['_openstat']);
$_SERVER['REQUEST_URI'] = preg_replace('%[&?]_openstat=[^&]+(&|$)%siU', '', $_SERVER['REQUEST_URI']);
}
if (in_array($_SERVER['REQUEST_URI'], $a410Response) && !DUR_SKIP_THIS) {
header('HTTP/1.0 410 Gone');
echo '<h1 style="font-size: 18pt;">Ошибка 410</h1><p>Страница удалена</p><p style="text-align: right; margin: 10px;"><a href="/">На главную</a></p>';
exit;
}
if (in_array($_SERVER['REQUEST_URI'], $a404Response) && !DUR_SKIP_THIS) {
dur404native();
}
if (isset($aR301SkipCheck[$_SERVER['REQUEST_URI']]) && !DUR_SKIP_THIS && !DUR_SKIP_R301) {
if (!defined('DUR_SKIP_POST') || !DUR_SKIP_POST || (strtoupper($_SERVER['REQUEST_METHOD']) != 'POST')) {
header('Location: ' . $aR301SkipCheck[$_SERVER['REQUEST_URI']], true, 301);
exit;
}
}
foreach ($aURLRewriter as $sKey => $sVal) {
$aURLRewriter[$sKey] = str_replace(
array('р', 'у', 'к', 'е', 'н', 'х', 'в', 'а', 'о', 'ч', 'с', 'м', 'и', 'т', ' '),
array('p', 'y', 'k', 'e', 'h', 'x', 'b', 'a', 'o', '4', 'c', 'm', 'n', 't', '_'),
$sVal
);
if (!defined('DUR_SEO_REQUEST_URI') && ($sVal == $_SERVER['REQUEST_URI'])) {
define('DUR_SEO_REQUEST_URI', $sKey);
}
}
$aURFlip = array_flip($aURLRewriter);
//Многократная вложенность замен (до 10)
for ($i = 0; $i < 10; $i++) {
foreach ($aURLRewriter as $sFrom => $sTo) {
if (isset($aURLRewriter[$sTo])) {
$aURLRewriter[$sFrom] = $aURLRewriter[$sTo];
$aURFlip[$aURLRewriter[$sTo]] = $sFrom;
}
}
}
//Joomla hack! (Против защиты от register globals)
if (defined('DUR_CMS_TYPE') && (DUR_CMS_TYPE == 'JOOMLA')) {
$_SERVER['dur'] = array($aURLRewriter, $aURFlip, $aURLRewriterOnly);
}
//Единая точка входа
if (defined('DUR_PREPEND_APPEND') && DUR_PREPEND_APPEND && !DUR_SKIP_THIS) {
durRun ();
}

// Функции
function durRun () {
if (defined('DUR_RUNNED')) return;
// if (isset())
define('DUR_RUNNED', 1);
durR301();
ob_start('durLinkChanger');
durIFRewrite();
}

function dur404 () {
$aPages404 = array('404.php', '404.html', '404.htm', 'index.php', 'index.html', 'index.htm');
header('HTTP/1.1 404 Not found');
foreach ($aPages404 as $sPage404) {
if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/' . $sPage404)) {
include($_SERVER['DOCUMENT_ROOT'] . '/' . $sPage404);
exit;
}
}
echo '<h1>Ошибка 404</h1><p>Страница не найдена</p><p style="text-align: right; margin: 10px;"><a href="/">На главную</a></p>';
exit;
}
function dur404native() {
$_SERVER['REQUEST_URI'] = '/thispagewasdeleted';
$_GET = $_REQUEST = array();
}
function durRewrite ($sURL) {
global $QUERY_STRING, $REQUEST_URI, $REDIRECT_URL, $HTTP_GET_VARS;
define('DUR_DEBUG_BEFORE', "SERVER:\n" . durDebugVar($_SERVER) . "\n\nGET:\n" . durDebugVar($_GET) . "\n\nREQUEST:\n" . durDebugVar($_REQUEST) . "\n");
if (defined('DUR_CMS_TYPE') && (DUR_CMS_TYPE == 'WEBASYST')) {
$sURL = '/?__furl_path=' . substr($sURL, 1) . '&frontend=1';
}
if (defined('DUR_CMS_TYPE') && (DUR_CMS_TYPE == 'ICMS')) {
$sURL = '/index.php?path=' . substr($sURL, 1, -5) . '&frontend=1';
}
$QUERY_STRING = strpos($sURL, '?') ? substr($sURL, strpos($sURL, '?') + 1) : '';
$REQUEST_URI = $sURL;
$REDIRECT_URL = $sURL;
$_SERVER['QUERY_STRING'] = $QUERY_STRING;
$_SERVER['REDIRECT_URL'] = $sURL;
$_SERVER['REQUEST_URI'] = $sURL;
if (defined('DUR_CMS_TYPE') && (DUR_CMS_TYPE == 'NETCAT')) {
putenv('REQUEST_URI=' . $sURL);
//$_ENV['REQUEST_URI'] = $sURL;
}
if (defined('DUR_CMS_TYPE') && (DUR_CMS_TYPE == 'DRUPAL')) {
$_GET['q'] = substr($sURL, 1);
$_REQUEST['q'] = substr($sURL, 1);
}
if (preg_match_all('%[\?&]([^\=]+)\=([^&]*)%', $sURL, $aM)) {
$aParams = array();
foreach ($aM[1] as $iKey => $sName) {
$sVal = urldecode($aM[2][$iKey]);
if (preg_match('#^(.+)\[\]$#siU', $sName, $aMatch)) {
$aParams[$aMatch[1]][] = $sVal;
}
elseif (preg_match('#^(.+)\[([\w-]+)\]$#siU', $sName, $aMatch)) {
$aParams[$aMatch[1]][$aMatch[2]] = $sVal;
}
else {
$aParams[$sName] = $sVal;
}
}
foreach ($aParams as $sKey => $mVal) {
$_GET[$sKey] = $mVal;
$HTTP_GET_VARS[$sKey] = $mVal;
$_REQUEST[$sKey] = $mVal;
if (defined('DUR_REGISTER_GLOBALS') && DUR_REGISTER_GLOBALS) {
global $$sKey;
$$sKey = $mVal;
}
}
}
if (defined('DUR_PATHINFO') && DUR_PATHINFO) {
$_SERVER['PATH_INFO'] = substr($sURL, 1);
$_SERVER['PHP_SELF'] = $sURL;
}
if (DUR_CMS_TYPE == 'HTML') {
$sFName = $sURL;
if ($iPos = strpos($sFName, '?')) {
$sFName = substr($sFName, 0, $iPos);
}
if (file_exists($_SERVER['DOCUMENT_ROOT'] . $sFName)) {
include($_SERVER['DOCUMENT_ROOT'] . $sFName);
exit;
}
else {
dur404();
}
}
}

function durIFRewrite () {
global $aURFlip, $aURLRewriter;
if (DUR_SKIP_THIS) return;
$sKey = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
if (defined('DUR_SUBDOMAINS') && DUR_SUBDOMAINS && isset($aURFlip[$sKey])) {
if (!defined('DUR_ORIG_RURI')) {
define('DUR_ORIG_RURI', $aURFlip[$sKey]);
}
durRewrite ($aURFlip[$sKey]);
}
elseif (isset($aURFlip[$_SERVER['REQUEST_URI']])) {
if (!defined('DUR_ORIG_RURI')) {
define('DUR_ORIG_RURI', $aURFlip[$_SERVER['REQUEST_URI']]);
}
durRewrite ($aURFlip[$_SERVER['REQUEST_URI']]);
}
elseif (defined('DUR_CMS_TYPE') && (DUR_CMS_TYPE == 'HTML')) {
if (file_exists($_SERVER['DOCUMENT_ROOT'] . $_SERVER['REQUEST_URI'])) {
durRewrite ($_SERVER['REQUEST_URI']);
}
else {
dur404();
}
}
}

function durR301 () {
global $aURFlip, $aURLRewriter;
if (DUR_SKIP_THIS || DUR_SKIP_R301) return;
if (defined('DUR_SKIP_POST') && DUR_SKIP_POST && (strtoupper($_SERVER['REQUEST_METHOD']) == 'POST')) {
return;
}
if (isset($aURLRewriter[$_SERVER['REQUEST_URI']])) {
if ('http://' . DUR_HTTP_HOST == trim($aURLRewriter[$_SERVER['REQUEST_URI']], '/')) {
return;
}
header('Location: ' . $aURLRewriter[$_SERVER['REQUEST_URI']], true, 301);
exit;
}
}

function durRExpEscape ($sStr) {
return str_replace(array('?', '.', '-', ':', '%', '[', ']', '(', ')'), array('\\?', '\\.', '\\-', '\\:', '\\%', '\\[', '\\]', '\\(', '\\)'), $sStr);
}

function durReplaceOnceLink ($sLink, $sNewLink, $sContent) {
$sContent = preg_replace('%(href\s*=\s*[\'"]?)\s*' . durRExpEscape ($sLink) . '([#\'"\s>])%siU', '$1' . $sNewLink . '$2', $sContent);
if (strpos($sLink,'&'))
$sContent = preg_replace('%(href\s*=\s*[\'"]?)\s*' . durRExpEscape (str_replace('&', '&amp;', $sLink)) . '([#\'"\s>])%siU', '$1' . $sNewLink . '$2', $sContent);
return $sContent;
}

function durReplaceLink ($sHost, $sBase, $sFrom, $sTo, $sContent) {
$sNewLink = $sTo;
// Link type: "http://domain/link"
$sContent = durReplaceOnceLink ('http://' . $sHost . $sFrom, $sNewLink, $sContent);
// Link type: "https://domain.com/link"
// $sContent = durReplaceOnceLink ('https://' . $sHost . $sFrom, $sNewLink, $sContent);
// Link type: "//domain.com/link"
// $sContent = durReplaceOnceLink ('//' . $sHost . $sFrom, $sNewLink, $sContent);
if (!defined('DUR_FIRST_TIC'))
{
// Link type: "/link"
$sContent = durReplaceOnceLink ($sFrom, $sNewLink, $sContent);
// Link type: "./link"
if (defined('DUR_FIX_DOTTED') && DUR_FIX_DOTTED) {
$sContent = durReplaceOnceLink ('.' . $sFrom, $sNewLink, $sContent);
}
if (defined('DUR_FIX_RELATIVE') && DUR_FIX_RELATIVE) {
// Link type: "link" (Calc fromlink)
$aLink = explode('/', $sFrom);
$aBase = empty($sBase) ? array('') : explode('/', str_replace('//', '/', '/' . $sBase));
$sReplLnk = '';
for ($i = 0; $i < max(count($aLink), count($aBase)); $i++) {
if (isset($aBase[$i]) && isset($aLink[$i])) {
if ($aLink[$i] == $aBase[$i]) {
continue;
}
else {
for ($j = $i; $j < count($aBase); $j++) {
$sReplLnk .= '../';
}
for ($j = $i; $j < count($aLink); $j++) {
$sReplLnk .= $aLink[$j] . '/';
}
break;
}
}
elseif (isset($aLink[$i])) {
$sReplLnk .= $aLink[$i] . '/';
}
elseif (isset($aBase[$i])) {
$sReplLnk .= '../';
}
}
$sReplLnk = preg_replace('%/+%', '/', $sReplLnk);
$sReplLnk2 = trim($sReplLnk, '/');
$sReplLnk3 = rtrim($sReplLnk2, '.');
if (strlen($sReplLnk) > 1) {
$sContent = durReplaceOnceLink ($sReplLnk, $sNewLink, $sContent);
if (defined('DUR_FIX_DOTTED') && DUR_FIX_DOTTED) {
$sContent = durReplaceOnceLink ('./' . $sReplLnk, $sNewLink, $sContent);
}
}
if (($sReplLnk2 != $sReplLnk) && (strlen($sReplLnk2) > 1)) {
$sContent = durReplaceOnceLink ($sReplLnk2, $sNewLink, $sContent);
if (defined('DUR_FIX_DOTTED') && DUR_FIX_DOTTED) {
$sContent = durReplaceOnceLink ('./' . $sReplLnk2, $sNewLink, $sContent);
}
}
if (($sReplLnk3 != $sReplLnk2) && (strlen($sReplLnk3) > 1)) {
$sContent = durReplaceOnceLink ($sReplLnk3, $sNewLink, $sContent);
if (defined('DUR_FIX_DOTTED') && DUR_FIX_DOTTED) {
$sContent = durReplaceOnceLink ('./' . $sReplLnk3, $sNewLink, $sContent);
}
}
}
}
return $sContent;
}

function durGZDecode($sS) {
$sM = ord(substr($sS,2,1)); $iF = ord(substr($sS,3,1));
if ($iF & 31 != $iF) return null;
$iLH = 10; $iLE = 0;
if ($iF & 4) {
if ($iL - $iLH - 2 < 8) return false;
$iLE = unpack('v',substr($sS,8,2));
$iLE = $iLE[1];
if ($iL - $iLH - 2 - $iLE < 8) return false;
$iLH += 2 + $iLE;
}
$iFCN = $iFNL = 0;
if ($iF & 8) {
if ($iL - $iLH - 1 < 8) return false;
$iFNL = strpos(substr($sS,8+$iLE),chr(0));
if ($iFNL === false || $iL - $iLH - $iFNL - 1 < 8) return false;
$iLH += $iFNL + 1;
}
if ($iF & 16) {
if ($iL - $iLH - 1 < 8) return false;
$iFCN = strpos(substr($sS,8+$iLE+$iFNL),chr(0));
if ($iFCN === false || $iL - $iLH - $iFCN - 1 < 8) return false;
$iLH += $iFCN + 1;
}
$sHCRC = '';
if ($iF & 2) {
if ($iL - $iLH - 2 < 8) return false;
$calccrc = crc32(substr($sS,0,$iLH)) & 0xffff;
$sHCRC = unpack('v', substr($sS,$iLH,2));
$sHCRC = $sHCRC[1];
if ($sHCRC != $calccrc) return false;
$iLH += 2;
}
$sScrc = unpack('V',substr($sS,-8,4));
$sScrc = $sScrc[1];
$iSZ = unpack('V',substr($sS,-4));
$iSZ = $iSZ[1];
$iLBD = $iL-$iLH-8;
if ($iLBD < 1) return null;
$sB = substr($sS,$iLH,$iLBD);
$sS = '';
if ($iLBD > 0) {
if ($sM == 8) $sS = gzinflate($sB);
else return false;
}
if ($iSZ != strlen($sS) || crc32($sS) != $sScrc) return false;
return $sS;
}

function durGZDecode2($sS) {
$iLen = strlen($sS);
$sDigits = substr($sS, 0, 2);
$iMethod = ord(substr($sS, 2, 1));
$iFlags = ord(substr($sS, 3, 1));
if ($iFlags & 31 != $iFlags) return false;
$aMtime = unpack('V', substr($sS, 4, 4));
$iMtime = $aMtime[1];
$sXFL = substr($sS, 8, 1);
$sOS = substr($sS, 8, 1);
$iHeaderLen = 10;
$iExtraLen = 0;
$sExtra = '';
if ($iFlags & 4) {
if ($iLen - $iHeaderLen - 2 < 8) return false;
$iExtraLen = unpack('v', substr($sS, 8, 2));
$iExtraLen = $iExtraLen[1];
if ($iLen - $iHeaderLen - 2 - $iExtraLen < 8) return false;
$sExtra = substr($sS, 10, $iExtraLen);
$iHeaderLen += 2 + $iExtraLen;
}
$iFilenameLen = 0;
$sFilename = '';
if ($iFlags & 8) {
if ($iLen - $iHeaderLen - 1 < 8) return false;
$iFilenameLen = strpos(substr($sS, $iHeaderLen), chr(0));
if ($iFilenameLen === false || $iLen - $iHeaderLen - $iFilenameLen - 1 < 8) return false;
$sFilename = substr($sS, $iHeaderLen, $iFilenameLen);
$iHeaderLen += $iFilenameLen + 1;
}
$iCommentLen = 0;
$sComment = '';
if ($iFlags & 16) {
if ($iLen - $iHeaderLen - 1 < 8) return false;
$iCommentLen = strpos(substr($sS, $iHeaderLen), chr(0));
if ($iCommentLen === false || $iLen - $iHeaderLen - $iCommentLen - 1 < 8) return false;
$sComment = substr($sS, $iHeaderLen, $iCommentLen);
$iHeaderLen += $iCommentLen + 1;
}
$sCRC = '';
if ($iFlags & 2) {
if ($iLen - $iHeaderLen - 2 < 8) return false;
$sCalcCRC = crc32(substr($sS, 0, $iHeaderLen)) & 0xffff;
$sCRC = unpack('v', substr($sS, $iHeaderLen, 2));
$sCRC = $sCRC[1];
if ($sCRC != $sCalcCRC) return false;
$iHeaderLen += 2;
}
$sDataCRC = unpack('V', substr($sS, -8, 4));
$sDataCRC = sprintf('%u', $sDataCRC[1] & 0xFFFFFFFF);
$iSize = unpack('V', substr($sS, -4));
$iSize = $iSize[1];
$iBodyLen = $iLen - $iHeaderLen - 8;
if ($iBodyLen < 1) return false;
$sBody = substr($sS, $iHeaderLen, $iBodyLen);
$sS = '';
if ($iBodyLen > 0) {
switch ($iMethod) {
case 8: $sS = gzinflate($sBody); break;
default: return false;
}
}
$sCRC = sprintf('%u', crc32($sS));
$bCRCOK = ($sCRC == $sDataCRC);
$bLenOK = ($iSize == strlen($sS));
if (!$bLenOK || !$bCRCOK) return false;
return $sS;
}

function durGZCheck ($sContent) {
$iLen = strlen($sContent);
if ($iLen < 18 || strcmp(substr($sContent, 0, 2), "\x1f\x8b")) {
return $sContent;
}
$sData = durGZDecode2($sContent);
if (!$sData) {
$sData = durGZDecode($sContent);
}
return $sData ? $sData : $sContent;
}

function durOutputCompress ($sContent) {
if (!defined('DUR_OUTPUT_COMPRESS')) {
define('DUR_OUTPUT_COMPRESS', 'SKIP');
}
if (DUR_OUTPUT_COMPRESS == 'SKIP') {
return $sContent;
}
$aAccept = array();
if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
$aAccept = array_map('trim', explode(',', strtolower($_SERVER['HTTP_ACCEPT_ENCODING'])));
}
$bGZIP = in_array('gzip', $aAccept) && function_exists('gzencode');
$bDEFL = in_array('deflate', $aAccept) && function_exists('gzdeflate');
$sCompress = DUR_OUTPUT_COMPRESS;
if ((!$bGZIP && !$bDEFL) || (!$bGZIP && ($sCompress == 'GZIP')) || (!$bDEFL && ($sCompress == 'DEFLATE'))) {
$sCompress = 'NONE';
}
if ($sCompress == 'AUTO') {
$sCompress = $bGZIP ? 'GZIP' : ($bDEFL ? 'DEFLATE' : 'NONE');
}
switch ($sCompress) {
case 'GZIP':
header('Content-Encoding: gzip');
$sContent = gzencode($sContent);
break;
case 'DEFLATE':
header('Content-Encoding: deflate');
$sContent = gzdeflate($sContent, 9);
break;
default:
//header('Content-Encoding: none');
}
return $sContent;
}

function durDebugEscape ($sText) {
return str_replace(array('--', '-->'), array('==', '==}'), $sText);
}

function durDebugVar ($mVar, $sPref = ' ') {
$Ret = '';
foreach ($mVar as $sKey => $sVal) {
$Ret .= "{$sPref}{$sKey} => ";
if (is_array($sVal)) {
$Ret .= "ARRAY (\n" . durDebugVar($sVal, $sPref.' ') . "{$sPref})\n";
}
else {
$Ret .= "{$sVal}\n";
}
}
return durDebugEscape($Ret);
}

function durLinkChanger ($sContent) {
global $aURFlip, $aURLRewriter, $aURLRewriterOnly;

if (DUR_SKIP_THIS) return $sContent;
if (strlen($sContent) < 500) return $sContent;
if (DUR_CACHE_REWRITED && file_exists($_SERVER['DOCUMENT_ROOT'].'/d-cache')) {

/// Модуль кеширования контента - start

$aDataStore = array();

$icachedays = DUR_CACHE_TIME*60*60*24;

$sMD5Content = md5($sContent); // MD5 от контента составляет часть имени файла кэша; это значение нужно для поиска готового к выводу контента в кеше
$sCacheFName = $_SERVER['DOCUMENT_ROOT'].'/d-cache/'.$sMD5Content.'.html.cache'; // имя файла кеша текущего контента
$sTimeFName = $_SERVER['DOCUMENT_ROOT'].'/d-cache/time.cache'; // имя файла данных кеша

$aStoredData = array();
$aStoredData = unserialize(file_get_contents($sTimeFName)); // массив из файла данных кеша
$timestamp = $aStoredData['d_scripts_time']; // время последнего изменения файлов d-seo и d-url-rewriter
$tOverallLenght = $aStoredData['cache_weight']; // Занимаемое кешем место на диске
$tLastClear = $aStoredData['last_clear_time'];
$dtimestamp = filemtime($_SERVER['DOCUMENT_ROOT'].'/d-url-rewriter.php'); // время изменения d-url-rewriter

// если есть файл d-seo, то берем его время изменения и записываем в переменную $dtimestamp максимум от времени последнего изменения скриптов
if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/d-seo.php')) {
$dTMPtimestamp = filemtime($_SERVER['DOCUMENT_ROOT'].'/d-seo.php');
if ($dTMPtimestamp > $dtimestamp)
$dtimestamp = $dTMPtimestamp;
}

// если время последнего изменения, записанное в файл данных кеша отличается от реального, чистим весь кеш.
if ($timestamp != $dtimestamp || $tOverallLenght > DUR_CACHE_MEMORY || time() - $tLastClear > $icachedays) {
if($dh = @opendir($_SERVER['DOCUMENT_ROOT'].'/d-cache/')) {
while (($obj = readdir($dh)) !== false) {
if($obj=='.' || $obj=='..') continue;
@unlink($_SERVER['DOCUMENT_ROOT'].'/d-cache/'.$obj);
}
closedir($dh);
}
$aDataToStore['d_scripts_time'] = $dtimestamp; // записываем в массив время последнего изменения файлов d-seo и d-url-rewriter
$aDataToStore['last_clear_time'] = $tLastClear = time();
$aDataToStore['cache_weight'] = $tOverallLenght = 0;
file_put_contents($sTimeFName,serialize($aDataToStore));
}
}
// если есть соотв. файл в кеше, записываем его содержимое в $sContent
if (isset($sCacheFName) && file_exists($sCacheFName)) {
$sContent = file_get_contents($sCacheFName);
}

/// Модуль кеширования контента - break

else {

$iTimeStart = microtime(true);
$sContent = durGZCheck($sContent);
if (defined('DUR_CMS_TYPE') && (DUR_CMS_TYPE == 'JOOMLA') && isset($_SERVER['dur'])) {
$aURLRewriter = $_SERVER['dur'][0];
$aURFlip = $_SERVER['dur'][1];
$aURLRewriterOnly = $_SERVER['dur'][2];
unset($_SERVER['dur']);
}
$aURLRewriter = array_merge($aURLRewriter, $aURLRewriterOnly);
//Base path
if (preg_match('%<[^<>]*base[^<>]*href=[\'"]?([\w_\-\.\:/]+)[\'"\s>][^<>]*>%siU', $sContent, $aM)) {
$sBase = $aM[1];
$sBaseHref = $aM[1];
}
else {
$sBase = 'http://' . $_SERVER['HTTP_HOST'] . substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/'));
$sBaseHref = '';
}
$sBase = trim(str_replace(array('http://', 'https://'), '', $sBase), '/');
$aHosts = array($_SERVER['HTTP_HOST']);
if (substr($_SERVER['HTTP_HOST'], 0, 4) == 'www.') {
$aHosts[] = substr($_SERVER['HTTP_HOST'], 4);
}
if (defined('DUR_SUBDOMAINS') && DUR_SUBDOMAINS) {
$sExtHost = str_replace('www.www.', 'www.', 'www.' . DUR_SUBDOMAINS);
$aHosts[] = $sExtHost;
$aHosts[] = str_replace('www.', '', $sExtHost);
}
$aHosts = array_unique($aHosts);
$sBase = str_replace($aHosts, '', $sBase);
//href="?..."
if (defined('DUR_LINK_PARAM') && defined('DUR_ORIG_RURI') && DUR_LINK_PARAM) {
$sContent = preg_replace('%(href\s*=\s*[\'"]?)\s*([?#].*[#\'"\s>])%siU', '$1' . DUR_ORIG_RURI . '$2', $sContent);
}

if (defined('DUR_FIX_HTTP_HOST') && DUR_FIX_HTTP_HOST) {
$aHosts = array(DUR_FIX_HTTP_HOST);
$sFalseHost = str_replace('www.www.','','www.'.DUR_FIX_HTTP_HOST);
$sContent = str_replace('http://'.$sFalseHost, 'http://'.DUR_FIX_HTTP_HOST, $sContent);

}

//Main cicle
if (defined('DUR_MAIN_CYCLE'))
{
if (DUR_MAIN_CYCLE == 'str_replace' || DUR_MAIN_CYCLE == 'callback')
{
///Нормализация ссылок, все ссылки на сайте должны прийти к виду href="http://HTTP_HOST/REQUEST_URI" , т.е. в кавычках, без пробелов и с хостом
///Опасносте начинается здесь
$sContent = preg_replace('#\shref\s*=[\s]?([A-Za-z0-9\?\/][^\s]*)(\s|/>|>)#siU',' href="http://'.DUR_FIX_HTTP_HOST.'/$1"$2',$sContent);
$sContent = preg_replace('#\shref\s*=[\s]?"([A-Za-z0-9\?\/][^"]*)"#siU',' href="http://'.DUR_FIX_HTTP_HOST.'/$1"',$sContent);
$sContent = str_replace(array(
'http://'.DUR_FIX_HTTP_HOST.'/http://'.DUR_FIX_HTTP_HOST.'',
'http://'.DUR_FIX_HTTP_HOST.'//',
'http://'.DUR_FIX_HTTP_HOST.'/http',
'http://'.DUR_FIX_HTTP_HOST.'/mailto:',
'http://'.DUR_FIX_HTTP_HOST.'/javascript',
'http://'.DUR_FIX_HTTP_HOST.'/skype',
'http://'.DUR_FIX_HTTP_HOST.'/tel',
),
array(
'http://'.DUR_FIX_HTTP_HOST.'',
'http://'.DUR_FIX_HTTP_HOST.'/',
'http',
'mailto:',
'javascript',
'skype',
'tel',
),
$sContent
);
/// - end
}

if (DUR_MAIN_CYCLE == 'str_replace')
{
foreach ($aURLRewriter as $sFrom => $sTo) {
if (strpos($sContent, 'href="http://'.DUR_FIX_HTTP_HOST.$sFrom.'"'))
$sContent = str_replace('href="http://'.DUR_FIX_HTTP_HOST.$sFrom.'"','href="http://'.DUR_FIX_HTTP_HOST.$sTo.'"',$sContent);
}
}
else if(DUR_MAIN_CYCLE == 'callback')
{
function durMainCycleCallback($href) {
global $aURLRewriter;
if (isset($aURLRewriter[$href[1]])) return 'href="http://'.DUR_FIX_HTTP_HOST.$aURLRewriter[$href[1]].'"';
else return $href[0];
}

$sContent = preg_replace_callback('#href="http://'.DUR_FIX_HTTP_HOST.'([^"]*)"#siU','durMainCycleCallback',$sContent);
}
else if(DUR_MAIN_CYCLE == 'ortodox')
{
foreach ($aHosts as $sHost) {
foreach ($aURLRewriter as $sFrom => $sTo) {
$sContent = durReplaceLink ($sHost, $sBase, $sFrom, $sTo, $sContent);
}
if (!defined("DUR_FIRST_TIC")) define("DUR_FIRST_TIC", true);
}
}
else {
$sContent .= "<!--Nothing to do here!-->";
}
}

if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/d-seo.php')) {
include_once($_SERVER['DOCUMENT_ROOT'] . '/d-seo.php');
}
if ((defined('DUR_BASE_ROOT') && DUR_BASE_ROOT) || !empty($sBaseHref)) {
if (strlen(DUR_BASE_ROOT) > 7) {
$sBaseHref = DUR_BASE_ROOT;
}
else {
$sBaseHref = (empty($sBaseHref) ? 'http://' . $aHosts[0] : $sBaseHref) . '/';
}
$sBaseHref = trim($sBaseHref, '/') . '/';
$sBaseHref = '<base href="' . $sBaseHref . '">';
$sContent = preg_replace('%<base[^>]+href[^>]+>%siU', '', $sContent);
$sContent = preg_replace('%(<head(\s.*)?>)%siU', "$1" . $sBaseHref, $sContent);
}
if (defined('DUR_ANC_HREF') && DUR_ANC_HREF) {
$sContent = preg_replace('%(href\s*=\s*["\']+)(#\w)%siU', '$1' . DUR_REQUEST_URI . '$2', $sContent);
}
if (defined('DUR_ROOT_HREF') && DUR_ROOT_HREF) {
$sContent = preg_replace('%(href\s*=\s*["\']*)\./(\w)%siU', '$1http://' . $_SERVER['HTTP_HOST'] . $sBase . '/$2', $sContent);
}
if (function_exists('durOtherReplacer')) {
$sContent = durOtherReplacer ($sContent);
}
}

if (DUR_CACHE_REWRITED && file_exists($_SERVER['DOCUMENT_ROOT'].'/d-cache')) {
/// Модуль кеширования контента - continue
file_put_contents($sCacheFName, $sContent);
// $aDataToStore = $aStoredData;
$aDataToStore['last_clear_time'] = $tLastClear;
$aDataToStore['d_scripts_time'] = $dtimestamp;
$aDataToStore['cache_weight'] = (int)$tOverallLenght + (int)strlen($sContent);
file_put_contents($sTimeFName,serialize($aDataToStore));
/// Модуль кеширования контента - end
}

if (defined('DUR_DEBUG') && DUR_DEBUG) {
$sContent .= "\n<!--\n";
if (defined('DUR_DEBUG_BEFORE') && DUR_DEBUG_BEFORE) {
$sContent .= " ===== VARS BEFORE REWRITE =====\n\n" . DUR_DEBUG_BEFORE;
}
$sContent .= "===== VARS AFTER REWRITE =====\n\nSERVER:\n" . durDebugVar($_SERVER) . "\n\nGET:\n" . durDebugVar($_GET) . "\n\nREQUEST:\n" . durDebugVar($_REQUEST) . "\n";
$sContent .= "\nCONSTANTS:\n" .
' DUR_REQUEST_URI => ' . durDebugEscape(DUR_REQUEST_URI) . "\n" .
' DUR_HTTP_HOST => ' . durDebugEscape(DUR_HTTP_HOST) . "\n" .
' DUR_FULL_URI => ' . durDebugEscape(DUR_FULL_URI) . "\n" .
' DUR_ORIG_RURI => ' . (defined('DUR_ORIG_RURI') ? durDebugEscape(DUR_ORIG_RURI) : 'NOT-SET') . "\n" .
' DUR_SEO_REQUEST_URI => ' . (defined('DUR_SEO_REQUEST_URI') ? durDebugEscape(DUR_SEO_REQUEST_URI) : 'NOT-SET') . "\n";

$iTimeNow = microtime(true);
$iTimeAll = ($iTimeNow - DUR_TIME_START) / 1000;
$iTimeContent = ($iTimeStart - DUR_TIME_START) / 1000;
$iTimeLinks = ($iTimeNow - $iTimeStart) / 1000;
$sContent .= "\nTIME:\n" .
' ALL: ' . number_format($iTimeAll, 8) . " sec. (100%)\n" .
' CMS: ' . number_format($iTimeContent, 8) . ' sec. (' . number_format($iTimeContent / $iTimeAll * 100, 2) . "%)\n" .
' DUR: ' . number_format($iTimeLinks, 8) . ' sec. (' . number_format($iTimeLinks / $iTimeAll * 100, 2) . "%)\n";

$sContent .= "\nD-Data:\n" . durDebugVar($aDataToStore);
$sContent .= '-->';
}

$sContent = durOutputCompress($sContent);
if (defined('DUR_FIX_CONTLEN') && DUR_FIX_CONTLEN) {
header('Content-Length: ' . strlen($sContent));
}

return $sContent;
}

//ХУК ДЛЯ СКАНИРОВАНИЯ КСЕНЕЙ
/*if (strpos($_SERVER['HTTP_USER_AGENT'],'Xenu') === 0) {
$sContent = preg_replace('#src\s*=\s*[\'"]?[^\s]+[\'"]#','',$sContent);
$sContent = preg_replace('#<link[^>]*>#','',$sContent);
$robotsname = 'http://'.DUR_FIX_HTTP_HOST.'/robots.txt';
$sRobots = file_get_contents($robotsname);
$aRobots = explode("\n",$sRobots);
foreach($aRobots as $sRule) {
if (strpos($sRule,'Disallow: ') === 0) {
$sRepFrom = array('Disallow: ', '*','?');
$sRepTo = array('', '[^>]*','\?');
$sRule = str_replace($sRepFrom,$sRepTo,$sRule);
if (!strpos($sRule,'$')) $sRule.= '[^>]*';
$sContent = preg_replace('#href\s*=\s*[\'"]?\s*'.$sRule.'[\#\'"\s>]#siU','',$sContent);
$sContent = preg_replace('#href\s*=\s*[\'"]?\s*http://'.$_SERVER['HTTP_HOST'].$sRule.'[\#\'"\s>]#siU','',$sContent);
}
}
}*/

function durOtherReplacer ($sContent) {

// // закрываем от индексации внешние ссылки
// function outerlinks($matches) {
// $sEq = false; //есть ли совпадения
// $res = $matches[0];
//
// $arMassIndex = array(
// 'www.tkrus.ru',
// 'www.tidici.com',
// 'www.thermoking.com',
// );
//
// foreach($arMassIndex as $item) {
// if (preg_match('%'.quotemeta($item).'%siU',$matches[0])) {$sEq = true; break;}
// }
//
// if ($sEq){ // если совпадение нашлось
// //если отсутствует rel, то добавляем его
// if (!strpos($res, 'rel=')) {
// $res = str_replace('<a ','<a rel=nofollow ',$res);
// //$res = str_replace('<A', '<a rel="nofollow" ', $res);
// }
// }
//
// return $res;
// }
//
// //закрываем исходящие ссылки в ноиндекс и нофоллоу
// $sContent = preg_replace_callback('%<a[^>]*href=[\'"]?https?://.*</a>%siU','outerlinks',$sContent);
//
// // красивый аналог
// $sContent = preg_replace('#href="(http://(?!www\.kovea\.ru|kovea\.ru)[^"]*)"#siU','href="$1" rel="nofollow"',$sContent);

// // удаление битых ссылок
// $aDelLNK=array(
// //ссылки для удаления (img, script, a, link)
// '/test',
// );
//
// foreach($aDelLNK as $del)
// {
// $sContent = preg_replace('%<(a|script) [^>]*(href|src)\s*=\s*[\'"]?\s*[^\'"]*' . durRExpEscape ($del) . '[#\'"\s][^>]*>(.*)</(a|script)[^>]*>%siU', '$3', $sContent);
// $sContent = preg_replace('%<img [^>]*src\s*=\s*[\'"]?\s*[^\'"]*' . durRExpEscape ($del) . '[#\'"\s][^>]*>%siU', '', $sContent);
// $sContent = preg_replace('%<link [^>]*href\s*=\s*[\'"]?\s*[^\'"]*' . durRExpEscape ($del) . '[#\'"\s][^>]*>%siU', '', $sContent);
// }

// // замена внешних ссылок загружаемых ресурсов
// $aReplaceImageLink = array (
// //ссылки для замены
// 'http://www.google.ru/images/img.jpg' => '/images/img.jpg',
// );
//
// foreach ($aReplaceImageLink as $before => $after) {
// $sContent = preg_replace('%(<img [^>]*src\s*=\s*[\'"]?\s*[^\'"]*)' . durRExpEscape ($before) . '([#\'"\s][^>]*>)%siU', '$1'. $after. '$2', $sContent);
// }

return $sContent;
}

/* Подключение в начале файла

// ЧПУ ---
if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/d-url-rewriter.php')) {
include_once($_SERVER['DOCUMENT_ROOT'] . '/d-url-rewriter.php');
durRun ();
}
// --- ЧПУ

/* Для поддоменов неплохо было прописывать
RewriteCond %{HTTP_HOST} ^www.(.{4,}.nickon.ru)$
RewriteRule ^(.*)$ http://%1/$1 [R=301,L]

RewriteCond %{HTTP_HOST} ^(.{4,}).nickon.ru$
RewriteRule ^robots\.txt$ robots-%1.txt [L]
*/

/* Подключение с единой точкой входа
RemoveHandler .html .htm
AddType application/x-httpd-php .php .htm .html .phtml
php_value auto_prepend_file "d-url-rewriter.php"
*/

 

Я надеюсь, после такой жести у PHP-программистов глаза не вытекли, но кровоточат однозначно. Интересно было бы узнать, есть ли тут уязвимости? Если вы отлично шарите в PHP, напишите в комментах, пожалуйста, что нашли, да и в целом про код свои комментарии интересны.

Меня лично заинтересовала эта строчка:

define('DUR_SKIP_URLS', '#^/_?(admin|manag|bitrix|indy|cms|phpshop|varvara.php|captcha|jscripts/|modules|includes|templates|wp-admin)#siU'); //Skip URLS

Судя по всему, Demis Group использует данное решение не только с Joomla, но и в других популярных CMS, таких как Bitrix.

Но страшно не это. Страшно то, что заместо того, чтобы поменять заголовки в материалах или в шаблонах, Demis Group пишет ультра-костыль, который автоматически находит все найденные заголовки и обрабатывает их через свои preg_replace с жуткими регэкпами.

Далее, Demis Group проявила профессионализм, написав ВРУЧНУЮ файл sitemap.xml. Это при том, что в админке уже существовал компонент, который генерирует sitemap.xml автоматически из существующих пунктов меню. Интересно, оптимизатор заглядывал в админку?

Немного успокоившись от увиденного, я начал делать адаптивную версию. Но и тут меня ждал сюрприз. Дело в том, что я использую LESS-препроцессор для генерации CSS. Потому, что удобно и быстро. Догадываетесь, о чем я? Интуиция и тут не подвела. Бэкаплю текущий template.css, затем генерирую новый через LESS, делаю DIFF и что я вижу?

«Оптимизатор» проявил чудеса усердия! Он вставил переносы между каждыми правилами, после закрывающихся фигурных скобок. Честно, мне даже стало немного его жалко.

2015-05-15 13-15-13 Clipboard vs  Applications MAMP htdocs mtk-met.ru templates mtk delete-css template.css_

Естественно «оптимизатор» не знал, что такое normalize.css, поэтому свои правила он вставлял и выше и ниже и в сам normalize, который был импортирован при компиляции. При этом не обошлось без ошибок (подчеркнуты красным). Удивительно, но все браузеры переварили без проблем. 2015-05-15 13-13-12 template.css - mtk-met.ru - [ Applications MAMP htdocs mtk-met.ru]

На просьбы поменять все это безобразие, Demis Group всего лишь переподключили свои скрипты и они как-то заработали. На резонное замечание о костылях, Demis ответили, что скрипты нужны и что они выполняют важную задачу генерации заголовков. Что же, с этим сложно спорить. Все мои претензии в итоге оказались монологом со стеной. Demis Group, насколько я понял, не выгодно делать оптимизацию хорошо.

Какие выводы можно сделать из всего этого? Для себя лично я вынес вот что:

1) Если вы ведете проект как разработчик, но не занимаетесь продвижением, то имеет смысл пристально наблюдать за тем, как ведется продвижение, чтобы не столкнуться с описанным выше.

2) Всю компиляцию я делаю в Grunt, там же, отныне, использую minify для сжатия CSS и JS-файлов. Это немного уменьшит шанс того, что идиот начнет править файлы (хотя, мне попадались и такие).

UPD: Спустя пару недель, после того, как я переделал все CSS файлы в сжатые, Demis Group не постеснялись и их поменять. :/ Еще и CSS файлы, которые компилируются удалили. Действильно, зачем они нужны, если есть минифицированный? Клинический случай.

3) Рейтинг SEO компаний — пустой треп. Чем больше клиентов, тем больше текучка, качество на нуле, ответственность тоже. Я об этом догадывался, но теперь на личном примере убедился.

2015-05-15 13-52-27 ПРОДВИЖЕНИЕ САЙТОВ

2015-05-15 13-49-03 Seo-компания Demis Group

Если вы видите, что компания на своем сайте навязчиво начинает трясти своими «дипломами», «рейтингами» и прочей ахинеей, то это повод серьезно задуматься.