PalmaSolutions

sape.php

Oct 8th, 2018
419
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 74.35 KB | None | 0 0
  1. <?php
  2. /**
  3.  * SAPE.ru - Интеллектуальная система купли-продажи ссылок
  4.  *
  5.  * PHP-клиент
  6.  *
  7.  * Вебмастеры! Не нужно ничего менять в этом файле!
  8.  * Все настройки - через параметры при вызове кода.
  9.  *
  10.  * Подробную информацию по добавлению сайта в систему,
  11.  * установки кода, а так же по всему остальным вопросам
  12.  * Вы можете найти здесь:
  13.  * @link http://help.sape.ru/sape/faq/27
  14.  * @link http://help.sape.ru/articles/faq/1041
  15.  *
  16.  */
  17.  
  18. /**
  19.  * Основной класс, выполняющий всю рутину
  20.  */
  21. class SAPE_base
  22. {
  23.     var $_version = '1.2.7';
  24.  
  25.     var $_verbose = false;
  26.  
  27.     /**
  28.      * Кодировка сайта
  29.      * @link http://www.php.net/manual/en/function.iconv.php
  30.      * @var string
  31.      */
  32.     var $_charset = '';
  33.  
  34.     var $_sape_charset = '';
  35.  
  36.     var $_server_list = array('dispenser-01.saperu.net', 'dispenser-02.saperu.net');
  37.  
  38.     /**
  39.      * Пожалейте наш сервер :о)
  40.      * @var int
  41.      */
  42.     var $_cache_lifetime = 3600;
  43.  
  44.     /**
  45.      * Если скачать базу ссылок не удалось, то следующая попытка будет через столько секунд
  46.      * @var int
  47.      */
  48.     var $_cache_reloadtime = 600;
  49.  
  50.     var $_errors = array();
  51.  
  52.     var $_host = '';
  53.  
  54.     var $_request_uri = '';
  55.  
  56.     var $_multi_site = false;
  57.  
  58.     /**
  59.      * Способ подключения к удалённому серверу [file_get_contents|curl|socket]
  60.      * @var string
  61.      */
  62.     var $_fetch_remote_type = '';
  63.  
  64.     /**
  65.      * Сколько ждать ответа
  66.      * @var int
  67.      */
  68.     var $_socket_timeout = 6;
  69.  
  70.     var $_force_show_code = false;
  71.  
  72.     /**
  73.      * Если наш робот
  74.      * @var bool
  75.      */
  76.     var $_is_our_bot = false;
  77.  
  78.     var $_debug = false;
  79.  
  80.     /**
  81.      * Регистронезависимый режим работы, использовать только на свой страх и риск
  82.      * @var bool
  83.      */
  84.     var $_ignore_case = false;
  85.  
  86.     /**
  87.      * Путь к файлу с данными
  88.      * @var string
  89.      */
  90.     var $_db_file = '';
  91.  
  92.     /**
  93.      * Откуда будем брать uri страницы: $_SERVER['REQUEST_URI'] или getenv('REQUEST_URI')
  94.      * @var bool
  95.      */
  96.     var $_use_server_array = false;
  97.  
  98.     /**
  99.      * Показывать ли код js отдельно от выводимого контента
  100.      *
  101.      * @var bool
  102.      */
  103.     var $_show_counter_separately = false;
  104.  
  105.     var $_force_update_db = false;
  106.  
  107.     var $_user_agent = '';
  108.  
  109.     function SAPE_base($options = null)
  110.     {
  111.  
  112.         // Поехали :o)
  113.  
  114.         $host = '';
  115.  
  116.         if (is_array($options)) {
  117.             if (isset($options['host'])) {
  118.                 $host = $options['host'];
  119.             }
  120.         } elseif (strlen($options)) {
  121.             $host    = $options;
  122.             $options = array();
  123.         } else {
  124.             $options = array();
  125.         }
  126.  
  127.         if (isset($options['use_server_array']) && $options['use_server_array'] == true) {
  128.             $this->_use_server_array = true;
  129.         }
  130.  
  131.         // Какой сайт?
  132.         if (strlen($host)) {
  133.             $this->_host = $host;
  134.         } else {
  135.             $this->_host = $_SERVER['HTTP_HOST'];
  136.         }
  137.  
  138.         $this->_host = preg_replace('/^http:\/\//', '', $this->_host);
  139.         $this->_host = preg_replace('/^www\./', '', $this->_host);
  140.  
  141.         // Какая страница?
  142.         if (isset($options['request_uri']) && strlen($options['request_uri'])) {
  143.             $this->_request_uri = $options['request_uri'];
  144.         } elseif ($this->_use_server_array === false) {
  145.             $this->_request_uri = getenv('REQUEST_URI');
  146.         }
  147.  
  148.         if (strlen($this->_request_uri) == 0) {
  149.             $this->_request_uri = $_SERVER['REQUEST_URI'];
  150.         }
  151.  
  152.         // На случай, если хочется много сайтов в одной папке
  153.         if (isset($options['multi_site']) && $options['multi_site'] == true) {
  154.             $this->_multi_site = true;
  155.         }
  156.  
  157.         // Выводить информацию о дебаге
  158.         if (isset($options['debug']) && $options['debug'] == true) {
  159.             $this->_debug = true;
  160.         }
  161.  
  162.         // Определяем наш ли робот
  163.         if (isset($_COOKIE['sape_cookie']) && ($_COOKIE['sape_cookie'] == _SAPE_USER)) {
  164.             $this->_is_our_bot = true;
  165.             if (isset($_COOKIE['sape_debug']) && ($_COOKIE['sape_debug'] == 1)) {
  166.                 $this->_debug = true;
  167.                 //для удобства дебега саппортом
  168.                 $this->_options            = $options;
  169.                 $this->_server_request_uri = $_SERVER['REQUEST_URI'];
  170.                 $this->_getenv_request_uri = getenv('REQUEST_URI');
  171.                 $this->_SAPE_USER          = _SAPE_USER;
  172.             }
  173.             if (isset($_COOKIE['sape_updatedb']) && ($_COOKIE['sape_updatedb'] == 1)) {
  174.                 $this->_force_update_db = true;
  175.             }
  176.         } else {
  177.             $this->_is_our_bot = false;
  178.         }
  179.  
  180.         // Сообщать об ошибках
  181.         if (isset($options['verbose']) && $options['verbose'] == true || $this->_debug) {
  182.             $this->_verbose = true;
  183.         }
  184.  
  185.         // Кодировка
  186.         if (isset($options['charset']) && strlen($options['charset'])) {
  187.             $this->_charset = $options['charset'];
  188.         } else {
  189.             $this->_charset = 'windows-1251';
  190.         }
  191.  
  192.         if (isset($options['fetch_remote_type']) && strlen($options['fetch_remote_type'])) {
  193.             $this->_fetch_remote_type = $options['fetch_remote_type'];
  194.         }
  195.  
  196.         if (isset($options['socket_timeout']) && is_numeric($options['socket_timeout']) && $options['socket_timeout'] > 0) {
  197.             $this->_socket_timeout = $options['socket_timeout'];
  198.         }
  199.  
  200.         // Всегда выводить чек-код
  201.         if (isset($options['force_show_code']) && $options['force_show_code'] == true) {
  202.             $this->_force_show_code = true;
  203.         }
  204.  
  205.         if (!defined('_SAPE_USER')) {
  206.             return $this->raise_error('Не задана константа _SAPE_USER');
  207.         }
  208.  
  209.         //Не обращаем внимания на регистр ссылок
  210.         if (isset($options['ignore_case']) && $options['ignore_case'] == true) {
  211.             $this->_ignore_case = true;
  212.             $this->_request_uri = strtolower($this->_request_uri);
  213.         }
  214.  
  215.         if (isset($options['show_counter_separately'])) {
  216.             $this->_show_counter_separately = (bool)$options['show_counter_separately'];
  217.         }
  218.     }
  219.  
  220.     /**
  221.      * Получить строку User-Agent
  222.      *
  223.      * @return string
  224.      */
  225.     function _get_full_user_agent_string()
  226.     {
  227.         return $this->_user_agent . ' ' . $this->_version;
  228.     }
  229.  
  230.     /**
  231.      * Вывести дебаг-информацию
  232.      *
  233.      * @param $data
  234.      *
  235.      * @return string
  236.      */
  237.     function _debug_output($data)
  238.     {
  239.         $data = '<!-- <sape_debug_info>' . @base64_encode(serialize($data)) . '</sape_debug_info> -->';
  240.  
  241.         return $data;
  242.     }
  243.  
  244.     /**
  245.      * Функция для подключения к удалённому серверу
  246.      */
  247.     function fetch_remote_file($host, $path, $specifyCharset = false)
  248.     {
  249.  
  250.         $user_agent = $this->_get_full_user_agent_string();
  251.  
  252.         @ini_set('allow_url_fopen', 1);
  253.         @ini_set('default_socket_timeout', $this->_socket_timeout);
  254.         @ini_set('user_agent', $user_agent);
  255.         if (
  256.             $this->_fetch_remote_type == 'file_get_contents'
  257.             ||
  258.             (
  259.                 $this->_fetch_remote_type == ''
  260.                 &&
  261.                 function_exists('file_get_contents')
  262.                 &&
  263.                 ini_get('allow_url_fopen') == 1
  264.             )
  265.         ) {
  266.             $this->_fetch_remote_type = 'file_get_contents';
  267.  
  268.             if ($specifyCharset && function_exists('stream_context_create')) {
  269.                 $opts    = array(
  270.                     'http' => array(
  271.                         'method' => 'GET',
  272.                         'header' => 'Accept-Charset: ' . $this->_charset . "\r\n"
  273.                     )
  274.                 );
  275.                 $context = @stream_context_create($opts);
  276.                 if ($data = @file_get_contents('http://' . $host . $path, null, $context)) {
  277.                     return $data;
  278.                 }
  279.             } else {
  280.                 if ($data = @file_get_contents('http://' . $host . $path)) {
  281.                     return $data;
  282.                 }
  283.             }
  284.         } elseif (
  285.             $this->_fetch_remote_type == 'curl'
  286.             ||
  287.             (
  288.                 $this->_fetch_remote_type == ''
  289.                 &&
  290.                 function_exists('curl_init')
  291.             )
  292.         ) {
  293.             $this->_fetch_remote_type = 'curl';
  294.             if ($ch = @curl_init()) {
  295.  
  296.                 @curl_setopt($ch, CURLOPT_URL, 'http://' . $host . $path);
  297.                 @curl_setopt($ch, CURLOPT_HEADER, false);
  298.                 @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  299.                 @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->_socket_timeout);
  300.                 @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
  301.                 if ($specifyCharset) {
  302.                     @curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept-Charset: ' . $this->_charset));
  303.                 }
  304.  
  305.                 $data = @curl_exec($ch);
  306.                 @curl_close($ch);
  307.  
  308.                 if ($data) {
  309.                     return $data;
  310.                 }
  311.             }
  312.         } else {
  313.             $this->_fetch_remote_type = 'socket';
  314.             $buff                     = '';
  315.             $fp                       = @fsockopen($host, 80, $errno, $errstr, $this->_socket_timeout);
  316.             if ($fp) {
  317.                 @fputs($fp, "GET {$path} HTTP/1.0\r\nHost: {$host}\r\n");
  318.                 if ($specifyCharset) {
  319.                     @fputs($fp, "Accept-Charset: {$this->_charset}\r\n");
  320.                 }
  321.                 @fputs($fp, "User-Agent: {$user_agent}\r\n\r\n");
  322.                 while (!@feof($fp)) {
  323.                     $buff .= @fgets($fp, 128);
  324.                 }
  325.                 @fclose($fp);
  326.  
  327.                 $page = explode("\r\n\r\n", $buff);
  328.                 unset($page[0]);
  329.  
  330.                 return implode("\r\n\r\n", $page);
  331.             }
  332.         }
  333.  
  334.         return $this->raise_error('Не могу подключиться к серверу: ' . $host . $path . ', type: ' . $this->_fetch_remote_type);
  335.     }
  336.  
  337.     /**
  338.      * Функция чтения из локального файла
  339.      */
  340.     function _read($filename)
  341.     {
  342.  
  343.         $fp = @fopen($filename, 'rb');
  344.         @flock($fp, LOCK_SH);
  345.         if ($fp) {
  346.             clearstatcache();
  347.             $length = @filesize($filename);
  348.  
  349.             if (version_compare(PHP_VERSION, '5.3.0', '<')) {
  350.                 $mqr = @get_magic_quotes_runtime();
  351.                 @set_magic_quotes_runtime(0);
  352.             }
  353.  
  354.             if ($length) {
  355.                 $data = @fread($fp, $length);
  356.             } else {
  357.                 $data = '';
  358.             }
  359.  
  360.             if (version_compare(PHP_VERSION, '5.3.0', '<')) {
  361.                 @set_magic_quotes_runtime($mqr);
  362.             }
  363.  
  364.             @flock($fp, LOCK_UN);
  365.             @fclose($fp);
  366.  
  367.             return $data;
  368.         }
  369.  
  370.         return $this->raise_error('Не могу считать данные из файла: ' . $filename);
  371.     }
  372.  
  373.     /**
  374.      * Функция записи в локальный файл
  375.      */
  376.     function _write($filename, $data)
  377.     {
  378.  
  379.         $fp = @fopen($filename, 'ab');
  380.         if ($fp) {
  381.             if (flock($fp, LOCK_EX | LOCK_NB)) {
  382.                 ftruncate($fp, 0);
  383.  
  384.                 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
  385.                     $mqr = @get_magic_quotes_runtime();
  386.                     @set_magic_quotes_runtime(0);
  387.                 }
  388.  
  389.                 @fwrite($fp, $data);
  390.  
  391.                 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
  392.                     @set_magic_quotes_runtime($mqr);
  393.                 }
  394.  
  395.                 @flock($fp, LOCK_UN);
  396.                 @fclose($fp);
  397.  
  398.                 if (md5($this->_read($filename)) != md5($data)) {
  399.                     @unlink($filename);
  400.  
  401.                     return $this->raise_error('Нарушена целостность данных при записи в файл: ' . $filename);
  402.                 }
  403.             } else {
  404.                 return false;
  405.             }
  406.  
  407.             return true;
  408.         }
  409.  
  410.         return $this->raise_error('Не могу записать данные в файл: ' . $filename);
  411.     }
  412.  
  413.     /**
  414.      * Функция обработки ошибок
  415.      */
  416.     function raise_error($e)
  417.     {
  418.  
  419.         $this->_errors[] = $e;
  420.  
  421.         if ($this->_verbose == true) {
  422.             print '<p style="color: red; font-weight: bold;">SAPE ERROR: ' . $e . '</p>';
  423.         }
  424.  
  425.         return false;
  426.     }
  427.  
  428.     /**
  429.      * Получить имя файла с даными
  430.      *
  431.      * @return string
  432.      */
  433.     function _get_db_file()
  434.     {
  435.         return '';
  436.     }
  437.  
  438.     /**
  439.      * Получить URI к хосту диспенсера
  440.      *
  441.      * @return string
  442.      */
  443.     function _get_dispenser_path()
  444.     {
  445.         return '';
  446.     }
  447.  
  448.     /**
  449.      * Сохранить данные, полученные из файла, в объекте
  450.      */
  451.     function set_data($data)
  452.     {
  453.     }
  454.  
  455.     /**
  456.      * Загрузка данных
  457.      */
  458.     function load_data()
  459.     {
  460.         $this->_db_file = $this->_get_db_file();
  461.  
  462.         if (!is_file($this->_db_file)) {
  463.             // Пытаемся создать файл.
  464.             if (@touch($this->_db_file)) {
  465.                 @chmod($this->_db_file, 0666); // Права доступа
  466.             } else {
  467.                 return $this->raise_error('Нет файла ' . $this->_db_file . '. Создать не удалось. Выставите права 777 на папку.');
  468.             }
  469.         }
  470.  
  471.         if (!is_writable($this->_db_file)) {
  472.             return $this->raise_error('Нет доступа на запись к файлу: ' . $this->_db_file . '! Выставите права 777 на папку.');
  473.         }
  474.  
  475.         @clearstatcache();
  476.  
  477.         $data = $this->_read($this->_db_file);
  478.         if (
  479.             $this->_force_update_db
  480.             || (
  481.                 !$this->_is_our_bot
  482.                 &&
  483.                 (
  484.                     filemtime($this->_db_file) < (time() - $this->_cache_lifetime)
  485.                     ||
  486.                     filesize($this->_db_file) == 0
  487.                     ||
  488.                     @unserialize($data) == false
  489.                 )
  490.             )
  491.         ) {
  492.             // Чтобы не повесить площадку клиента и чтобы не было одновременных запросов
  493.             @touch($this->_db_file, (time() - $this->_cache_lifetime + $this->_cache_reloadtime));
  494.  
  495.             $path = $this->_get_dispenser_path();
  496.             if (strlen($this->_charset)) {
  497.                 $path .= '&charset=' . $this->_charset;
  498.             }
  499.  
  500.             foreach ($this->_server_list as $server) {
  501.                 if ($data = $this->fetch_remote_file($server, $path)) {
  502.                     if (substr($data, 0, 12) == 'FATAL ERROR:') {
  503.                         $this->raise_error($data);
  504.                     } else {
  505.                         // [псевдо]проверка целостности:
  506.                         $hash = @unserialize($data);
  507.                         if ($hash != false) {
  508.                             // попытаемся записать кодировку в кеш
  509.                             $hash['__sape_charset__']      = $this->_charset;
  510.                             $hash['__last_update__']       = time();
  511.                             $hash['__multi_site__']        = $this->_multi_site;
  512.                             $hash['__fetch_remote_type__'] = $this->_fetch_remote_type;
  513.                             $hash['__ignore_case__']       = $this->_ignore_case;
  514.                             $hash['__php_version__']       = phpversion();
  515.                             $hash['__server_software__']   = $_SERVER['SERVER_SOFTWARE'];
  516.  
  517.                             $data_new = @serialize($hash);
  518.                             if ($data_new) {
  519.                                 $data = $data_new;
  520.                             }
  521.  
  522.                             $this->_write($this->_db_file, $data);
  523.                             break;
  524.                         }
  525.                     }
  526.                 }
  527.             }
  528.         }
  529.  
  530.         // Убиваем PHPSESSID
  531.         if (strlen(session_id())) {
  532.             $session            = session_name() . '=' . session_id();
  533.             $this->_request_uri = str_replace(array('?' . $session, '&' . $session), '', $this->_request_uri);
  534.         }
  535.  
  536.         $this->set_data(@unserialize($data));
  537.  
  538.         return true;
  539.     }
  540.  
  541.     function _return_obligatory_page_content()
  542.     {
  543.         $s_globals = new SAPE_globals();
  544.  
  545.         $html = '';
  546.         if (isset($this->_page_obligatory_output) && !empty($this->_page_obligatory_output)
  547.             && false == $s_globals->page_obligatory_output_shown()
  548.         ) {
  549.             $s_globals->page_obligatory_output_shown(true);
  550.             $html = $this->_page_obligatory_output;
  551.         }
  552.  
  553.         return $html;
  554.     }
  555.  
  556.     /**
  557.      * Вернуть js-код
  558.      * - работает только когда параметр конструктора show_counter_separately = true
  559.      *
  560.      * @return string
  561.      */
  562.     function return_counter()
  563.     {
  564.         //если show_counter_separately = false и выполнен вызов этого метода,
  565.         //то заблокировать вывод js-кода вместе с контентом
  566.         if (false == $this->_show_counter_separately) {
  567.             $this->_show_counter_separately = true;
  568.         }
  569.  
  570.         return $this->_return_obligatory_page_content();
  571.     }
  572. }
  573.  
  574. /**
  575.  * Глобальные флаги
  576.  */
  577. class SAPE_globals
  578. {
  579.  
  580.     function _get_toggle_flag($name, $toggle = false)
  581.     {
  582.  
  583.         static $flags = array();
  584.  
  585.         if (!isset($flags[$name])) {
  586.             $flags[$name] = false;
  587.         }
  588.  
  589.         if ($toggle) {
  590.             $flags[$name] = true;
  591.         }
  592.  
  593.         return $flags[$name];
  594.     }
  595.  
  596.     function block_css_shown($toggle = false)
  597.     {
  598.         return $this->_get_toggle_flag('block_css_shown', $toggle);
  599.     }
  600.  
  601.     function block_ins_beforeall_shown($toggle = false)
  602.     {
  603.         return $this->_get_toggle_flag('block_ins_beforeall_shown', $toggle);
  604.     }
  605.  
  606.     function page_obligatory_output_shown($toggle = false)
  607.     {
  608.         return $this->_get_toggle_flag('page_obligatory_output_shown', $toggle);
  609.     }
  610. }
  611.  
  612. /**
  613.  * Класс для работы с обычными ссылками
  614.  */
  615. class SAPE_client extends SAPE_base
  616. {
  617.  
  618.     var $_links_delimiter = '';
  619.     var $_links = array();
  620.     var $_links_page = array();
  621.     var $_user_agent = 'SAPE_Client PHP';
  622.  
  623.     var $_show_only_block = false;
  624.     var $_block_tpl = '';
  625.     var $_block_tpl_options = array();
  626.     var $_block_uri_idna = array();
  627.  
  628.     function SAPE_client($options = null)
  629.     {
  630.         parent::SAPE_base($options);
  631.  
  632.         $this->load_data();
  633.     }
  634.  
  635.     /**
  636.      * Обработка html для массива ссылок
  637.      *
  638.      * @param string     $html
  639.      * @param null|array $options
  640.      *
  641.      * @return string
  642.      */
  643.     function _return_array_links_html($html, $options = null)
  644.     {
  645.  
  646.         if (empty($options)) {
  647.             $options = array();
  648.         }
  649.  
  650.         // если запрошена определенная кодировка, и известна кодировка кеша, и они разные, конвертируем в заданную
  651.         if (
  652.             strlen($this->_charset) > 0
  653.             &&
  654.             strlen($this->_sape_charset) > 0
  655.             &&
  656.             $this->_sape_charset != $this->_charset
  657.             &&
  658.             function_exists('iconv')
  659.         ) {
  660.             $new_html = @iconv($this->_sape_charset, $this->_charset, $html);
  661.             if ($new_html) {
  662.                 $html = $new_html;
  663.             }
  664.         }
  665.  
  666.         if ($this->_is_our_bot) {
  667.  
  668.             $html = '<sape_noindex>' . $html . '</sape_noindex>';
  669.  
  670.             if (isset($options['is_block_links']) && true == $options['is_block_links']) {
  671.  
  672.                 if (!isset($options['nof_links_requested'])) {
  673.                     $options['nof_links_requested'] = 0;
  674.                 }
  675.                 if (!isset($options['nof_links_displayed'])) {
  676.                     $options['nof_links_displayed'] = 0;
  677.                 }
  678.                 if (!isset($options['nof_obligatory'])) {
  679.                     $options['nof_obligatory'] = 0;
  680.                 }
  681.                 if (!isset($options['nof_conditional'])) {
  682.                     $options['nof_conditional'] = 0;
  683.                 }
  684.  
  685.                 $html = '<sape_block nof_req="' . $options['nof_links_requested'] .
  686.                     '" nof_displ="' . $options['nof_links_displayed'] .
  687.                     '" nof_oblig="' . $options['nof_obligatory'] .
  688.                     '" nof_cond="' . $options['nof_conditional'] .
  689.                     '">' . $html .
  690.                     '</sape_block>';
  691.             }
  692.         }
  693.  
  694.         return $html;
  695.     }
  696.  
  697.     /**
  698.      * Финальная обработка html перед выводом ссылок
  699.      *
  700.      * @param string $html
  701.      *
  702.      * @return string
  703.      */
  704.     function _return_html($html)
  705.     {
  706.  
  707.         if (false == $this->_show_counter_separately) {
  708.             $html = $this->_return_obligatory_page_content() . $html;
  709.         }
  710.  
  711.         if ($this->_debug) {
  712.             $user_agent = $this->_get_full_user_agent_string();
  713.  
  714.             $html .= $this->_debug_output($user_agent);
  715.             $html .= $this->_debug_output($this);
  716.         }
  717.  
  718.         return $html;
  719.     }
  720.  
  721.     /**
  722.      * Вывод ссылок в виде блока
  723.      *
  724.      * - Примечание: начиная с версии 1.2.2 второй аргумент $offset убран. Если
  725.      * передавать его согласно старой сигнатуре, то он будет проигнорирован.
  726.      *
  727.      * @param int   $n       Количествово ссылок, которые нужно вывести в текущем блоке
  728.      * @param array $options Опции
  729.      *
  730.      * <code>
  731.      * $options = array();
  732.      * $options['block_no_css'] = (false|true);
  733.      * // Переопределяет запрет на вывод css в коде страницы: false - выводить css
  734.      * $options['block_orientation'] = (1|0);
  735.      * // Переопределяет ориентацию блока: 1 - горизонтальная, 0 - вертикальная
  736.      * $options['block_width'] = ('auto'|'[?]px'|'[?]%'|'[?]');
  737.      * // Переопределяет ширину блока:
  738.      * // 'auto'  - определяется шириной блока-предка с фиксированной шириной,
  739.      * // если такового нет, то займет всю ширину
  740.      * // '[?]px' - значение в пикселях
  741.      * // '[?]%'  - значение в процентах от ширины блока-предка с фиксированной шириной
  742.      * // '[?]'   - любое другое значение, которое поддерживается спецификацией CSS
  743.      * </code>
  744.      *
  745.      * @see return_links()
  746.      * @see return_counter()
  747.      *
  748.      * @return string
  749.      */
  750.     function return_block_links($n = null, $options = null)
  751.     {
  752.  
  753.         $numargs = func_num_args();
  754.         $args    = func_get_args();
  755.  
  756.         //Проверяем аргументы для старой сигнатуры вызова
  757.         if (2 == $numargs) {           // return_links($n, $options)
  758.             if (!is_array($args[1])) { // return_links($n, $offset) - deprecated!
  759.                 $options = null;
  760.             }
  761.         } elseif (2 < $numargs) { // return_links($n, $offset, $options) - deprecated!
  762.  
  763.             if (!is_array($options)) {
  764.                 $options = $args[2];
  765.             }
  766.         }
  767.  
  768.         // Объединить параметры
  769.         if (empty($options)) {
  770.             $options = array();
  771.         }
  772.  
  773.         $defaults                      = array();
  774.         $defaults['block_no_css']      = false;
  775.         $defaults['block_orientation'] = 1;
  776.         $defaults['block_width']       = '';
  777.  
  778.         $ext_options = array();
  779.         if (isset($this->_block_tpl_options) && is_array($this->_block_tpl_options)) {
  780.             $ext_options = $this->_block_tpl_options;
  781.         }
  782.  
  783.         $options = array_merge($defaults, $ext_options, $options);
  784.  
  785.         // Ссылки переданы не массивом (чек-код) => выводим как есть + инфо о блоке
  786.         if (!is_array($this->_links_page)) {
  787.             $html = $this->_return_array_links_html('', array('is_block_links' => true));
  788.  
  789.             return $this->_return_html($this->_links_page . $html);
  790.         } // Не переданы шаблоны => нельзя вывести блоком - ничего не делать
  791.         elseif (!isset($this->_block_tpl)) {
  792.             return $this->_return_html('');
  793.         }
  794.  
  795.         // Определим нужное число элементов в блоке
  796.  
  797.         $total_page_links = count($this->_links_page);
  798.  
  799.         $need_show_obligatory_block  = false;
  800.         $need_show_conditional_block = false;
  801.         $n_requested                 = 0;
  802.  
  803.         if (isset($this->_block_ins_itemobligatory)) {
  804.             $need_show_obligatory_block = true;
  805.         }
  806.  
  807.         if (is_numeric($n) && $n >= $total_page_links) {
  808.  
  809.             $n_requested = $n;
  810.  
  811.             if (isset($this->_block_ins_itemconditional)) {
  812.                 $need_show_conditional_block = true;
  813.             }
  814.         }
  815.  
  816.         if (!is_numeric($n) || $n > $total_page_links) {
  817.             $n = $total_page_links;
  818.         }
  819.  
  820.         // Выборка ссылок
  821.         $links = array();
  822.         for ($i = 1; $i <= $n; $i++) {
  823.             $links[] = array_shift($this->_links_page);
  824.         }
  825.  
  826.         $html = '';
  827.  
  828.         // Подсчет числа опциональных блоков
  829.         $nof_conditional = 0;
  830.         if (count($links) < $n_requested && true == $need_show_conditional_block) {
  831.             $nof_conditional = $n_requested - count($links);
  832.         }
  833.  
  834.         //Если нет ссылок и нет вставных блоков, то ничего не выводим
  835.         if (empty($links) && $need_show_obligatory_block == false && $nof_conditional == 0) {
  836.  
  837.             $return_links_options = array(
  838.                 'is_block_links'      => true,
  839.                 'nof_links_requested' => $n_requested,
  840.                 'nof_links_displayed' => 0,
  841.                 'nof_obligatory'      => 0,
  842.                 'nof_conditional'     => 0
  843.             );
  844.  
  845.             $html = $this->_return_array_links_html($html, $return_links_options);
  846.  
  847.             return $this->_return_html($html);
  848.         }
  849.  
  850.         // Делаем вывод стилей, только один раз. Или не выводим их вообще, если так задано в параметрах
  851.         $s_globals = new SAPE_globals();
  852.         if (!$s_globals->block_css_shown() && false == $options['block_no_css']) {
  853.             $html .= $this->_block_tpl['css'];
  854.             $s_globals->block_css_shown(true);
  855.         }
  856.  
  857.         // Вставной блок в начале всех блоков
  858.         if (isset($this->_block_ins_beforeall) && !$s_globals->block_ins_beforeall_shown()) {
  859.             $html .= $this->_block_ins_beforeall;
  860.             $s_globals->block_ins_beforeall_shown(true);
  861.         }
  862.         unset($s_globals);
  863.  
  864.         // Вставной блок в начале блока
  865.         if (isset($this->_block_ins_beforeblock)) {
  866.             $html .= $this->_block_ins_beforeblock;
  867.         }
  868.  
  869.         // Получаем шаблоны в зависимости от ориентации блока
  870.         $block_tpl_parts = $this->_block_tpl[$options['block_orientation']];
  871.  
  872.         $block_tpl          = $block_tpl_parts['block'];
  873.         $item_tpl           = $block_tpl_parts['item'];
  874.         $item_container_tpl = $block_tpl_parts['item_container'];
  875.         $item_tpl_full      = str_replace('{item}', $item_tpl, $item_container_tpl);
  876.         $items              = '';
  877.  
  878.         $nof_items_total = count($links);
  879.         foreach ($links as $link) {
  880.  
  881.             // Обычная красивая ссылка
  882.             $is_found = preg_match('#<a href="(https?://([^"/]+)[^"]*)"[^>]*>[\s]*([^<]+)</a>#i', $link, $link_item);
  883.             // Картиночкая красивая ссылка
  884.             if (!$is_found) {
  885.                 preg_match('#<a href="(https?://([^"/]+)[^"]*)"[^>]*><img.*?alt="(.*?)".*?></a>#i', $link, $link_item);
  886.             }
  887.  
  888.             if (function_exists('mb_strtoupper') && strlen($this->_sape_charset) > 0) {
  889.                 $header_rest         = mb_substr($link_item[3], 1, mb_strlen($link_item[3], $this->_sape_charset) - 1, $this->_sape_charset);
  890.                 $header_first_letter = mb_strtoupper(mb_substr($link_item[3], 0, 1, $this->_sape_charset), $this->_sape_charset);
  891.                 $link_item[3]        = $header_first_letter . $header_rest;
  892.             } elseif (function_exists('ucfirst') && (strlen($this->_sape_charset) == 0 || strpos($this->_sape_charset, '1251') !== false)) {
  893.                 $link_item[3][0] = ucfirst($link_item[3][0]);
  894.             }
  895.  
  896.             // Если есть раскодированный URL, то заменить его при выводе
  897.             if (isset($this->_block_uri_idna) && isset($this->_block_uri_idna[$link_item[2]])) {
  898.                 $link_item[2] = $this->_block_uri_idna[$link_item[2]];
  899.             }
  900.  
  901.             $item = $item_tpl_full;
  902.             $item = str_replace('{header}', $link_item[3], $item);
  903.             $item = str_replace('{text}', trim($link), $item);
  904.             $item = str_replace('{url}', $link_item[2], $item);
  905.             $item = str_replace('{link}', $link_item[1], $item);
  906.             $items .= $item;
  907.         }
  908.  
  909.         // Вставной обязатльный элемент в блоке
  910.         if (true == $need_show_obligatory_block) {
  911.             $items .= str_replace('{item}', $this->_block_ins_itemobligatory, $item_container_tpl);
  912.             $nof_items_total += 1;
  913.         }
  914.  
  915.         // Вставные опциональные элементы в блоке
  916.         if ($need_show_conditional_block == true && $nof_conditional > 0) {
  917.             for ($i = 0; $i < $nof_conditional; $i++) {
  918.                 $items .= str_replace('{item}', $this->_block_ins_itemconditional, $item_container_tpl);
  919.             }
  920.             $nof_items_total += $nof_conditional;
  921.         }
  922.  
  923.         if ($items != '') {
  924.             $html .= str_replace('{items}', $items, $block_tpl);
  925.  
  926.             // Проставляем ширину, чтобы везде одинковая была
  927.             if ($nof_items_total > 0) {
  928.                 $html = str_replace('{td_width}', round(100 / $nof_items_total), $html);
  929.             } else {
  930.                 $html = str_replace('{td_width}', 0, $html);
  931.             }
  932.  
  933.             // Если задано, то переопределить ширину блока
  934.             if (isset($options['block_width']) && !empty($options['block_width'])) {
  935.                 $html = str_replace('{block_style_custom}', 'style="width: ' . $options['block_width'] . '!important;"', $html);
  936.             }
  937.         }
  938.  
  939.         unset($block_tpl_parts, $block_tpl, $items, $item, $item_tpl, $item_container_tpl);
  940.  
  941.         // Вставной блок в конце блока
  942.         if (isset($this->_block_ins_afterblock)) {
  943.             $html .= $this->_block_ins_afterblock;
  944.         }
  945.  
  946.         //Заполняем оставшиеся модификаторы значениями
  947.         unset($options['block_no_css'], $options['block_orientation'], $options['block_width']);
  948.  
  949.         $tpl_modifiers = array_keys($options);
  950.         foreach ($tpl_modifiers as $k => $m) {
  951.             $tpl_modifiers[$k] = '{' . $m . '}';
  952.         }
  953.         unset($m, $k);
  954.  
  955.         $tpl_modifiers_values = array_values($options);
  956.  
  957.         $html = str_replace($tpl_modifiers, $tpl_modifiers_values, $html);
  958.         unset($tpl_modifiers, $tpl_modifiers_values);
  959.  
  960.         //Очищаем незаполненные модификаторы
  961.         $clear_modifiers_regexp = '#\{[a-z\d_\-]+\}#';
  962.         $html                   = preg_replace($clear_modifiers_regexp, ' ', $html);
  963.  
  964.         $return_links_options = array(
  965.             'is_block_links'      => true,
  966.             'nof_links_requested' => $n_requested,
  967.             'nof_links_displayed' => $n,
  968.             'nof_obligatory'      => ($need_show_obligatory_block == true ? 1 : 0),
  969.             'nof_conditional'     => $nof_conditional
  970.         );
  971.  
  972.         $html = $this->_return_array_links_html($html, $return_links_options);
  973.  
  974.         return $this->_return_html($html);
  975.     }
  976.  
  977.     /**
  978.      * Вывод ссылок в обычном виде - текст с разделителем
  979.      *
  980.      * - Примечание: начиная с версии 1.2.2 второй аргумент $offset убран. Если
  981.      * передавать его согласно старой сигнатуре, то он будет проигнорирован.
  982.      *
  983.      * @param int   $n       Количествово ссылок, которые нужно вывести
  984.      * @param array $options Опции
  985.      *
  986.      * <code>
  987.      * $options = array();
  988.      * $options['as_block'] = (false|true);
  989.      * // Показывать ли ссылки в виде блока
  990.      * </code>
  991.      *
  992.      * @see return_block_links()
  993.      * @see return_counter()
  994.      *
  995.      * @return string
  996.      */
  997.     function return_links($n = null, $options = null)
  998.     {
  999.  
  1000.         if ($this->_debug) {
  1001.             if (function_exists('debug_backtrace')) {
  1002.                 $this->_return_links_calls[] = debug_backtrace();
  1003.             } else {
  1004.                 $this->_return_links_calls = "(function_exists('debug_backtrace')==false";
  1005.             }
  1006.         }
  1007.  
  1008.         $numargs = func_num_args();
  1009.         $args    = func_get_args();
  1010.  
  1011.         //Проверяем аргументы для старой сигнатуры вызова
  1012.         if (2 == $numargs) {           // return_links($n, $options)
  1013.             if (!is_array($args[1])) { // return_links($n, $offset) - deprecated!
  1014.                 $options = null;
  1015.             }
  1016.         } elseif (2 < $numargs) {        // return_links($n, $offset, $options) - deprecated!
  1017.  
  1018.             if (!is_array($options)) {
  1019.                 $options = $args[2];
  1020.             }
  1021.         }
  1022.  
  1023.         //Опрелелить, как выводить ссылки
  1024.         $as_block = $this->_show_only_block;
  1025.  
  1026.         if (is_array($options) && isset($options['as_block']) && false == $as_block) {
  1027.             $as_block = $options['as_block'];
  1028.         }
  1029.  
  1030.         if (true == $as_block && isset($this->_block_tpl)) {
  1031.             return $this->return_block_links($n, $options);
  1032.         }
  1033.  
  1034.         //-------
  1035.  
  1036.         if (is_array($this->_links_page)) {
  1037.  
  1038.             $total_page_links = count($this->_links_page);
  1039.  
  1040.             if (!is_numeric($n) || $n > $total_page_links) {
  1041.                 $n = $total_page_links;
  1042.             }
  1043.  
  1044.             $links = array();
  1045.  
  1046.             for ($i = 1; $i <= $n; $i++) {
  1047.                 $links[] = array_shift($this->_links_page);
  1048.             }
  1049.  
  1050.             $html = join($this->_links_delimiter, $links);
  1051.  
  1052.             // если запрошена определенная кодировка, и известна кодировка кеша, и они разные, конвертируем в заданную
  1053.             if (strlen($this->_charset) > 0
  1054.                 && strlen($this->_sape_charset) > 0
  1055.                 && $this->_sape_charset != $this->_charset
  1056.                 && function_exists('iconv')
  1057.             ) {
  1058.                 $new_html = @iconv($this->_sape_charset, $this->_charset, $html);
  1059.                 if ($new_html) {
  1060.                     $html = $new_html;
  1061.                 }
  1062.             }
  1063.  
  1064.             if ($this->_is_our_bot) {
  1065.                 $html = '<sape_noindex>' . $html . '</sape_noindex>';
  1066.             }
  1067.         } else {
  1068.             $html = $this->_links_page;
  1069.             if ($this->_is_our_bot) {
  1070.                 $html .= '<sape_noindex></sape_noindex>';
  1071.             }
  1072.         }
  1073.  
  1074.         $html = $this->_return_html($html);
  1075.  
  1076.         return $html;
  1077.     }
  1078.  
  1079.     function _get_db_file()
  1080.     {
  1081.         if ($this->_multi_site) {
  1082.             return dirname(__FILE__) . '/' . $this->_host . '.links.db';
  1083.         } else {
  1084.             return dirname(__FILE__) . '/links.db';
  1085.         }
  1086.     }
  1087.  
  1088.     function _get_dispenser_path()
  1089.     {
  1090.         return '/code.php?user=' . _SAPE_USER . '&host=' . $this->_host;
  1091.     }
  1092.  
  1093.     function set_data($data)
  1094.     {
  1095.         if ($this->_ignore_case) {
  1096.             $this->_links = array_change_key_case($data);
  1097.         } else {
  1098.             $this->_links = $data;
  1099.         }
  1100.         if (isset($this->_links['__sape_delimiter__'])) {
  1101.             $this->_links_delimiter = $this->_links['__sape_delimiter__'];
  1102.         }
  1103.         // определяем кодировку кеша
  1104.         if (isset($this->_links['__sape_charset__'])) {
  1105.             $this->_sape_charset = $this->_links['__sape_charset__'];
  1106.         } else {
  1107.             $this->_sape_charset = '';
  1108.         }
  1109.         if (@array_key_exists($this->_request_uri, $this->_links) && is_array($this->_links[$this->_request_uri])) {
  1110.             $this->_links_page = $this->_links[$this->_request_uri];
  1111.         } else {
  1112.             if (isset($this->_links['__sape_new_url__']) && strlen($this->_links['__sape_new_url__'])) {
  1113.                 if ($this->_is_our_bot || $this->_force_show_code) {
  1114.                     $this->_links_page = $this->_links['__sape_new_url__'];
  1115.                 }
  1116.             }
  1117.         }
  1118.  
  1119.         //Есть ли обязательный вывод
  1120.         if (isset($this->_links['__sape_page_obligatory_output__'])) {
  1121.             $this->_page_obligatory_output = $this->_links['__sape_page_obligatory_output__'];
  1122.         }
  1123.  
  1124.         // Есть ли флаг блочных ссылок
  1125.         if (isset($this->_links['__sape_show_only_block__'])) {
  1126.             $this->_show_only_block = $this->_links['__sape_show_only_block__'];
  1127.         } else {
  1128.             $this->_show_only_block = false;
  1129.         }
  1130.  
  1131.         // Есть ли шаблон для красивых ссылок
  1132.         if (isset($this->_links['__sape_block_tpl__']) && !empty($this->_links['__sape_block_tpl__'])
  1133.             && is_array($this->_links['__sape_block_tpl__'])
  1134.         ) {
  1135.             $this->_block_tpl = $this->_links['__sape_block_tpl__'];
  1136.         }
  1137.  
  1138.         // Есть ли параметры для красивых ссылок
  1139.         if (isset($this->_links['__sape_block_tpl_options__']) && !empty($this->_links['__sape_block_tpl_options__'])
  1140.             && is_array($this->_links['__sape_block_tpl_options__'])
  1141.         ) {
  1142.             $this->_block_tpl_options = $this->_links['__sape_block_tpl_options__'];
  1143.         }
  1144.  
  1145.         // IDNA-домены
  1146.         if (isset($this->_links['__sape_block_uri_idna__']) && !empty($this->_links['__sape_block_uri_idna__'])
  1147.             && is_array($this->_links['__sape_block_uri_idna__'])
  1148.         ) {
  1149.             $this->_block_uri_idna = $this->_links['__sape_block_uri_idna__'];
  1150.         }
  1151.  
  1152.         // Блоки
  1153.         $check_blocks = array(
  1154.             'beforeall',
  1155.             'beforeblock',
  1156.             'afterblock',
  1157.             'itemobligatory',
  1158.             'itemconditional',
  1159.             'afterall'
  1160.         );
  1161.  
  1162.         foreach ($check_blocks as $block_name) {
  1163.  
  1164.             $var_name  = '__sape_block_ins_' . $block_name . '__';
  1165.             $prop_name = '_block_ins_' . $block_name;
  1166.  
  1167.             if (isset($this->_links[$var_name]) && strlen($this->_links[$var_name]) > 0) {
  1168.                 $this->$prop_name = $this->_links[$var_name];
  1169.             }
  1170.         }
  1171.     }
  1172. }
  1173.  
  1174. /**
  1175.  * Класс для работы с контекстными ссылками
  1176.  */
  1177. class SAPE_context extends SAPE_base
  1178. {
  1179.  
  1180.     var $_words = array();
  1181.     var $_words_page = array();
  1182.     var $_user_agent = 'SAPE_Context PHP';
  1183.     var $_filter_tags = array('a', 'textarea', 'select', 'script', 'style', 'label', 'noscript', 'noindex', 'button');
  1184.  
  1185.     var $_debug_actions = array();
  1186.  
  1187.     function SAPE_context($options = null)
  1188.     {
  1189.         parent::SAPE_base($options);
  1190.         $this->load_data();
  1191.     }
  1192.  
  1193.     /**
  1194.      * Начать сбор дебаг-информации
  1195.      */
  1196.     function _debug_action_start()
  1197.     {
  1198.         if (!$this->_debug) {
  1199.             return;
  1200.         }
  1201.  
  1202.         $this->_debug_actions   = array();
  1203.         $this->_debug_actions[] = $this->_get_full_user_agent_string();
  1204.     }
  1205.  
  1206.     /**
  1207.      * Записать строку дебаг-информацию
  1208.      *
  1209.      * @param        $data
  1210.      * @param string $key
  1211.      */
  1212.     function _debug_action_append($data, $key = '')
  1213.     {
  1214.         if (!$this->_debug) {
  1215.             return;
  1216.         }
  1217.  
  1218.         if (!empty($key)) {
  1219.             $this->_debug_actions[] = array($key => $data);
  1220.         } else {
  1221.             $this->_debug_actions[] = $data;
  1222.         }
  1223.     }
  1224.  
  1225.     /**
  1226.      * Вывод дебаг-информации
  1227.      *
  1228.      * @return string
  1229.      */
  1230.     function _debug_action_output()
  1231.     {
  1232.  
  1233.         if (!$this->_debug || empty($this->_debug_actions)) {
  1234.             return '';
  1235.         }
  1236.  
  1237.         $debug_info = $this->_debug_output($this->_debug_actions);
  1238.  
  1239.         $this->_debug_actions = array();
  1240.  
  1241.         return $debug_info;
  1242.     }
  1243.  
  1244.     /**
  1245.      * Замена слов в куске текста и обрамляет его тегами sape_index
  1246.      */
  1247.     function replace_in_text_segment($text)
  1248.     {
  1249.  
  1250.         $this->_debug_action_start();
  1251.         $this->_debug_action_append('START: replace_in_text_segment()');
  1252.         $this->_debug_action_append($text, 'argument for replace_in_text_segment');
  1253.  
  1254.         if (count($this->_words_page) > 0) {
  1255.  
  1256.             $source_sentences = array();
  1257.  
  1258.             //Создаем массив исходных текстов для замены
  1259.             foreach ($this->_words_page as $n => $sentence) {
  1260.                 //Заменяем все сущности на символы
  1261.                 $special_chars = array(
  1262.                     '&amp;'  => '&',
  1263.                     '&quot;' => '"',
  1264.                     '&#039;' => '\'',
  1265.                     '&lt;'   => '<',
  1266.                     '&gt;'   => '>'
  1267.                 );
  1268.                 $sentence      = strip_tags($sentence);
  1269.                 $sentence      = strip_tags($sentence);
  1270.                 $sentence      = str_replace(array_keys($special_chars), array_values($special_chars), $sentence);
  1271.  
  1272.                 //Преобразуем все спец символы в сущности
  1273.                 $htsc_charset = empty($this->_charset) ? 'windows-1251' : $this->_charset;
  1274.                 $quote_style  = ENT_COMPAT;
  1275.                 if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
  1276.                     $quote_style = ENT_COMPAT | ENT_HTML401;
  1277.                 }
  1278.  
  1279.                 $sentence = htmlspecialchars($sentence, $quote_style, $htsc_charset);
  1280.  
  1281.                 //Квотируем
  1282.                 $sentence      = preg_quote($sentence, '/');
  1283.                 $replace_array = array();
  1284.                 if (preg_match_all('/(&[#a-zA-Z0-9]{2,6};)/isU', $sentence, $out)) {
  1285.                     for ($i = 0; $i < count($out[1]); $i++) {
  1286.                         $unspec                 = $special_chars[$out[1][$i]];
  1287.                         $real                   = $out[1][$i];
  1288.                         $replace_array[$unspec] = $real;
  1289.                     }
  1290.                 }
  1291.                 //Заменяем сущности на ИЛИ (сущность|символ)
  1292.                 foreach ($replace_array as $unspec => $real) {
  1293.                     $sentence = str_replace($real, '((' . $real . ')|(' . $unspec . '))', $sentence);
  1294.                 }
  1295.                 //Заменяем пробелы на переносы или сущности пробелов
  1296.                 $source_sentences[$n] = str_replace(' ', '((\s)|(&nbsp;))+', $sentence);
  1297.             }
  1298.  
  1299.             $this->_debug_action_append($source_sentences, 'sentences for replace');
  1300.  
  1301.             //если это первый кусок, то не будем добавлять <
  1302.             $first_part = true;
  1303.             //пустая переменная для записи
  1304.  
  1305.             if (count($source_sentences) > 0) {
  1306.  
  1307.                 $content   = '';
  1308.                 $open_tags = array(); //Открытые забаненые тэги
  1309.                 $close_tag = ''; //Название текущего закрывающего тэга
  1310.  
  1311.                 //Разбиваем по символу начала тега
  1312.                 $part = strtok(' ' . $text, '<');
  1313.  
  1314.                 while ($part !== false) {
  1315.                     //Определяем название тэга
  1316.                     if (preg_match('/(?si)^(\/?[a-z0-9]+)/', $part, $matches)) {
  1317.                         //Определяем название тега
  1318.                         $tag_name = strtolower($matches[1]);
  1319.                         //Определяем закрывающий ли тэг
  1320.                         if (substr($tag_name, 0, 1) == '/') {
  1321.                             $close_tag = substr($tag_name, 1);
  1322.                             $this->_debug_action_append($close_tag, 'close tag');
  1323.                         } else {
  1324.                             $close_tag = '';
  1325.                             $this->_debug_action_append($tag_name, 'open tag');
  1326.                         }
  1327.                         $cnt_tags = count($open_tags);
  1328.                         //Если закрывающий тег совпадает с тегом в стеке открытых запрещенных тегов
  1329.                         if (($cnt_tags > 0) && ($open_tags[$cnt_tags - 1] == $close_tag)) {
  1330.                             array_pop($open_tags);
  1331.  
  1332.                             $this->_debug_action_append($tag_name, 'deleted from open_tags');
  1333.  
  1334.                             if ($cnt_tags - 1 == 0) {
  1335.                                 $this->_debug_action_append('start replacement');
  1336.                             }
  1337.                         }
  1338.  
  1339.                         //Если нет открытых плохих тегов, то обрабатываем
  1340.                         if (count($open_tags) == 0) {
  1341.                             //если не запрещенный тэг, то начинаем обработку
  1342.                             if (!in_array($tag_name, $this->_filter_tags)) {
  1343.                                 $split_parts = explode('>', $part, 2);
  1344.                                 //Перестраховываемся
  1345.                                 if (count($split_parts) == 2) {
  1346.                                     //Начинаем перебор фраз для замены
  1347.                                     foreach ($source_sentences as $n => $sentence) {
  1348.                                         if (preg_match('/' . $sentence . '/', $split_parts[1]) == 1) {
  1349.                                             $split_parts[1] = preg_replace('/' . $sentence . '/', str_replace('$', '\$', $this->_words_page[$n]), $split_parts[1], 1);
  1350.  
  1351.                                             $this->_debug_action_append($sentence . ' --- ' . $this->_words_page[$n], 'replaced');
  1352.  
  1353.                                             //Если заменили, то удаляем строчку из списка замены
  1354.                                             unset($source_sentences[$n]);
  1355.                                             unset($this->_words_page[$n]);
  1356.                                         }
  1357.                                     }
  1358.                                     $part = $split_parts[0] . '>' . $split_parts[1];
  1359.                                     unset($split_parts);
  1360.                                 }
  1361.                             } else {
  1362.                                 //Если у нас запрещеный тэг, то помещаем его в стек открытых
  1363.                                 $open_tags[] = $tag_name;
  1364.  
  1365.                                 $this->_debug_action_append($tag_name, 'added to open_tags, stop replacement');
  1366.                             }
  1367.                         }
  1368.                     } else {
  1369.                         //Если нет названия тега, то считаем, что перед нами текст
  1370.                         foreach ($source_sentences as $n => $sentence) {
  1371.                             if (preg_match('/' . $sentence . '/', $part) == 1) {
  1372.                                 $part = preg_replace('/' . $sentence . '/', str_replace('$', '\$', $this->_words_page[$n]), $part, 1);
  1373.  
  1374.                                 $this->_debug_action_append($sentence . ' --- ' . $this->_words_page[$n], 'replaced');
  1375.  
  1376.                                 //Если заменили, то удаляем строчку из списка замены,
  1377.                                 //чтобы было можно делать множественный вызов
  1378.                                 unset($source_sentences[$n]);
  1379.                                 unset($this->_words_page[$n]);
  1380.                             }
  1381.                         }
  1382.                     }
  1383.  
  1384.                     //Если это первая часть, то не выводим <
  1385.                     if ($first_part) {
  1386.                         $content .= $part;
  1387.                         $first_part = false;
  1388.                     } else {
  1389.                         $content .= '<' . $part;
  1390.                     }
  1391.                     //Получаем следующу часть
  1392.                     unset($part);
  1393.                     $part = strtok('<');
  1394.                 }
  1395.                 $text = ltrim($content);
  1396.                 unset($content);
  1397.             }
  1398.         } else {
  1399.             $this->_debug_action_append('No word\'s for page');
  1400.         }
  1401.  
  1402.         if ($this->_is_our_bot || $this->_force_show_code || $this->_debug) {
  1403.             $text = '<sape_index>' . $text . '</sape_index>';
  1404.             if (isset($this->_words['__sape_new_url__']) && strlen($this->_words['__sape_new_url__'])) {
  1405.                 $text .= $this->_words['__sape_new_url__'];
  1406.             }
  1407.         }
  1408.  
  1409.         if (count($this->_words_page) > 0) {
  1410.             $this->_debug_action_append($this->_words_page, 'Not replaced');
  1411.         }
  1412.  
  1413.         $this->_debug_action_append('END: replace_in_text_segment()');
  1414.  
  1415.         $text .= $this->_debug_action_output();
  1416.  
  1417.         return $text;
  1418.     }
  1419.  
  1420.     /**
  1421.      * Замена слов
  1422.      */
  1423.     function replace_in_page(&$buffer)
  1424.     {
  1425.  
  1426.         $this->_debug_action_start();
  1427.         $this->_debug_action_append('START: replace_in_page()');
  1428.  
  1429.         $s_globals = new SAPE_globals();
  1430.  
  1431.         if (!$s_globals->page_obligatory_output_shown()
  1432.             && isset($this->_page_obligatory_output)
  1433.             && !empty($this->_page_obligatory_output)
  1434.         ) {
  1435.  
  1436.             $split_content = preg_split('/(?smi)(<\/?body[^>]*>)/', $buffer, -1, PREG_SPLIT_DELIM_CAPTURE);
  1437.             if (count($split_content) == 5) {
  1438.                 $buffer = $split_content[0] . $split_content[1] . $split_content[2]
  1439.                     . (false == $this->_show_counter_separately ? $this->_return_obligatory_page_content() : '')
  1440.                     . $split_content[3] . $split_content[4];
  1441.                 unset($split_content);
  1442.  
  1443.                 $s_globals->page_obligatory_output_shown(true);
  1444.             }
  1445.         }
  1446.  
  1447.         if (count($this->_words_page) > 0) {
  1448.             //разбиваем строку по sape_index
  1449.             //Проверяем есть ли теги sape_index
  1450.             $split_content = preg_split('/(?smi)(<\/?sape_index>)/', $buffer, -1);
  1451.             $cnt_parts     = count($split_content);
  1452.             if ($cnt_parts > 1) {
  1453.                 //Если есть хоть одна пара sape_index, то начинаем работу
  1454.                 if ($cnt_parts >= 3) {
  1455.                     for ($i = 1; $i < $cnt_parts; $i = $i + 2) {
  1456.                         $split_content[$i] = $this->replace_in_text_segment($split_content[$i]);
  1457.                     }
  1458.                 }
  1459.                 $buffer = implode('', $split_content);
  1460.  
  1461.                 $this->_debug_action_append($cnt_parts, 'Split by Sape_index cnt_parts=');
  1462.             } else {
  1463.                 //Если не нашли sape_index, то пробуем разбить по BODY
  1464.                 $split_content = preg_split('/(?smi)(<\/?body[^>]*>)/', $buffer, -1, PREG_SPLIT_DELIM_CAPTURE);
  1465.                 //Если нашли содержимое между body
  1466.                 if (count($split_content) == 5) {
  1467.                     $split_content[0] = $split_content[0] . $split_content[1];
  1468.                     $split_content[1] = $this->replace_in_text_segment($split_content[2]);
  1469.                     $split_content[2] = $split_content[3] . $split_content[4];
  1470.                     unset($split_content[3]);
  1471.                     unset($split_content[4]);
  1472.                     $buffer = $split_content[0] . $split_content[1] . $split_content[2];
  1473.  
  1474.                     $this->_debug_action_append('Split by BODY');
  1475.                 } else {
  1476.                     //Если не нашли sape_index и не смогли разбить по body
  1477.                     $this->_debug_action_append('Cannot split by BODY');
  1478.                 }
  1479.             }
  1480.         } else {
  1481.             if (!$this->_is_our_bot && !$this->_force_show_code && !$this->_debug) {
  1482.                 $buffer = preg_replace('/(?smi)(<\/?sape_index>)/', '', $buffer);
  1483.             } else {
  1484.                 if (isset($this->_words['__sape_new_url__']) && strlen($this->_words['__sape_new_url__'])) {
  1485.                     $buffer .= $this->_words['__sape_new_url__'];
  1486.                 }
  1487.             }
  1488.  
  1489.             $this->_debug_action_append('No word\'s for page');
  1490.         }
  1491.  
  1492.         $this->_debug_action_append('STOP: replace_in_page()');
  1493.         $buffer .= $this->_debug_action_output();
  1494.  
  1495.         return $buffer;
  1496.     }
  1497.  
  1498.     function _get_db_file()
  1499.     {
  1500.         if ($this->_multi_site) {
  1501.             return dirname(__FILE__) . '/' . $this->_host . '.words.db';
  1502.         } else {
  1503.             return dirname(__FILE__) . '/words.db';
  1504.         }
  1505.     }
  1506.  
  1507.     function _get_dispenser_path()
  1508.     {
  1509.         return '/code_context.php?user=' . _SAPE_USER . '&host=' . $this->_host;
  1510.     }
  1511.  
  1512.     function set_data($data)
  1513.     {
  1514.         $this->_words = $data;
  1515.         if (@array_key_exists($this->_request_uri, $this->_words) && is_array($this->_words[$this->_request_uri])) {
  1516.             $this->_words_page = $this->_words[$this->_request_uri];
  1517.         }
  1518.  
  1519.         //Есть ли обязательный вывод
  1520.         if (isset($this->_words['__sape_page_obligatory_output__'])) {
  1521.             $this->_page_obligatory_output = $this->_words['__sape_page_obligatory_output__'];
  1522.         }
  1523.     }
  1524. }
  1525.  
  1526. /**
  1527.  * Класс для работы со статьями articles.sape.ru показывает анонсы и статьи
  1528.  */
  1529. class SAPE_articles extends SAPE_base
  1530. {
  1531.  
  1532.     var $_request_mode;
  1533.  
  1534.     var $_server_list = array('dispenser.articles.sape.ru');
  1535.  
  1536.     var $_data = array();
  1537.  
  1538.     var $_article_id;
  1539.  
  1540.     var $_save_file_name;
  1541.  
  1542.     var $_announcements_delimiter = '';
  1543.  
  1544.     var $_images_path;
  1545.  
  1546.     var $_template_error = false;
  1547.  
  1548.     var $_noindex_code = '<!--sape_noindex-->';
  1549.  
  1550.     var $_headers_enabled = false;
  1551.  
  1552.     var $_mask_code;
  1553.  
  1554.     var $_real_host;
  1555.  
  1556.     var $_user_agent = 'SAPE_Articles_Client PHP';
  1557.  
  1558.     function SAPE_articles($options = null)
  1559.     {
  1560.         parent::SAPE_base($options);
  1561.         if (is_array($options) && isset($options['headers_enabled'])) {
  1562.             $this->_headers_enabled = $options['headers_enabled'];
  1563.         }
  1564.         // Кодировка
  1565.         if (isset($options['charset']) && strlen($options['charset'])) {
  1566.             $this->_charset = $options['charset'];
  1567.         } else {
  1568.             $this->_charset = '';
  1569.         }
  1570.         $this->_get_index();
  1571.         if (!empty($this->_data['index']['announcements_delimiter'])) {
  1572.             $this->_announcements_delimiter = $this->_data['index']['announcements_delimiter'];
  1573.         }
  1574.         if (!empty($this->_data['index']['charset'])
  1575.             and !(isset($options['charset']) && strlen($options['charset']))
  1576.         ) {
  1577.             $this->_charset = $this->_data['index']['charset'];
  1578.         }
  1579.         if (is_array($options)) {
  1580.             if (isset($options['host'])) {
  1581.                 $host = $options['host'];
  1582.             }
  1583.         } elseif (strlen($options)) {
  1584.             $host    = $options;
  1585.             $options = array();
  1586.         }
  1587.         if (isset($host) && strlen($host)) {
  1588.             $this->_real_host = $host;
  1589.         } else {
  1590.             $this->_real_host = $_SERVER['HTTP_HOST'];
  1591.         }
  1592.         if (!isset($this->_data['index']['announcements'][$this->_request_uri])) {
  1593.             $this->_correct_uri();
  1594.         }
  1595.     }
  1596.  
  1597.     function _correct_uri()
  1598.     {
  1599.         if (substr($this->_request_uri, -1) == '/') {
  1600.             $new_uri = substr($this->_request_uri, 0, -1);
  1601.         } else {
  1602.             $new_uri = $this->_request_uri . '/';
  1603.         }
  1604.         if (isset($this->_data['index']['announcements'][$new_uri])) {
  1605.             $this->_request_uri = $new_uri;
  1606.         }
  1607.     }
  1608.  
  1609.     /**
  1610.      * Возвращает анонсы для вывода
  1611.      *
  1612.      * @param int $n      Сколько анонсов вывести, либо не задано - вывести все
  1613.      * @param int $offset C какого анонса начинаем вывод(нумерация с 0), либо не задано - с нулевого
  1614.      *
  1615.      * @return string
  1616.      */
  1617.     function return_announcements($n = null, $offset = 0)
  1618.     {
  1619.         $output = '';
  1620.         if ($this->_force_show_code || $this->_is_our_bot) {
  1621.             if (isset($this->_data['index']['checkCode'])) {
  1622.                 $output .= $this->_data['index']['checkCode'];
  1623.             }
  1624.         }
  1625.  
  1626.         if (false == $this->_show_counter_separately) {
  1627.             $output .= $this->_return_obligatory_page_content();
  1628.         }
  1629.  
  1630.         if (isset($this->_data['index']['announcements'][$this->_request_uri])) {
  1631.  
  1632.             $total_page_links = count($this->_data['index']['announcements'][$this->_request_uri]);
  1633.  
  1634.             if (!is_numeric($n) || $n > $total_page_links) {
  1635.                 $n = $total_page_links;
  1636.             }
  1637.  
  1638.             $links = array();
  1639.  
  1640.             for ($i = 1; $i <= $n; $i++) {
  1641.                 if ($offset > 0 && $i <= $offset) {
  1642.                     array_shift($this->_data['index']['announcements'][$this->_request_uri]);
  1643.                 } else {
  1644.                     $links[] = array_shift($this->_data['index']['announcements'][$this->_request_uri]);
  1645.                 }
  1646.             }
  1647.  
  1648.             $html = join($this->_announcements_delimiter, $links);
  1649.  
  1650.             if ($this->_is_our_bot) {
  1651.                 $html = '<sape_noindex>' . $html . '</sape_noindex>';
  1652.             }
  1653.  
  1654.             $output .= $html;
  1655.         }
  1656.  
  1657.         return $output;
  1658.     }
  1659.  
  1660.     function _get_index()
  1661.     {
  1662.         $this->_set_request_mode('index');
  1663.         $this->_save_file_name = 'articles.db';
  1664.         $this->load_data();
  1665.     }
  1666.  
  1667.     /**
  1668.      * Возвращает полный HTML код страницы статьи
  1669.      * @return string
  1670.      */
  1671.     function process_request()
  1672.     {
  1673.  
  1674.         if (!empty($this->_data['index']) and isset($this->_data['index']['articles'][$this->_request_uri])) {
  1675.             return $this->_return_article();
  1676.         } elseif (!empty($this->_data['index']) and isset($this->_data['index']['images'][$this->_request_uri])) {
  1677.             return $this->_return_image();
  1678.         } else {
  1679.             if ($this->_is_our_bot) {
  1680.                 return $this->_return_html($this->_data['index']['checkCode'] . $this->_noindex_code);
  1681.             } else {
  1682.                 return $this->_return_not_found();
  1683.             }
  1684.         }
  1685.     }
  1686.  
  1687.     function _return_article()
  1688.     {
  1689.         $this->_set_request_mode('article');
  1690.         //Загружаем статью
  1691.         $article_meta          = $this->_data['index']['articles'][$this->_request_uri];
  1692.         $this->_save_file_name = $article_meta['id'] . '.article.db';
  1693.         $this->_article_id     = $article_meta['id'];
  1694.         $this->load_data();
  1695.         if (false == $this->_show_counter_separately) {
  1696.             $this->_data[$this->_request_mode]['body'] = $this->_return_obligatory_page_content() . $this->_data[$this->_request_mode]['body'];
  1697.         }
  1698.  
  1699.         //Обновим если устарела
  1700.         if (!isset($this->_data['article']['date_updated']) OR $this->_data['article']['date_updated'] < $article_meta['date_updated']) {
  1701.             unlink($this->_get_db_file());
  1702.             $this->load_data();
  1703.         }
  1704.  
  1705.         //Получим шаблон
  1706.         $template = $this->_get_template($this->_data['index']['templates'][$article_meta['template_id']]['url'], $article_meta['template_id']);
  1707.  
  1708.         //Выведем статью
  1709.         $article_html = $this->_fetch_article($template);
  1710.  
  1711.         if ($this->_is_our_bot) {
  1712.             $article_html .= $this->_noindex_code;
  1713.         }
  1714.  
  1715.         return $this->_return_html($article_html);
  1716.     }
  1717.  
  1718.     function _prepare_path_to_images()
  1719.     {
  1720.         $this->_images_path = dirname(__FILE__) . '/images/';
  1721.         if (!is_dir($this->_images_path)) {
  1722.             // Пытаемся создать папку.
  1723.             if (@mkdir($this->_images_path)) {
  1724.                 @chmod($this->_images_path, 0777);    // Права доступа
  1725.             } else {
  1726.                 return $this->raise_error('Нет папки ' . $this->_images_path . '. Создать не удалось. Выставите права 777 на папку.');
  1727.             }
  1728.         }
  1729.         if ($this->_multi_site) {
  1730.             $this->_images_path .= $this->_host . '.';
  1731.         }
  1732.  
  1733.         return true;
  1734.     }
  1735.  
  1736.     function _return_image()
  1737.     {
  1738.         $this->_set_request_mode('image');
  1739.         $this->_prepare_path_to_images();
  1740.  
  1741.         //Проверим загружена ли картинка
  1742.         $image_meta = $this->_data['index']['images'][$this->_request_uri];
  1743.         $image_path = $this->_images_path . $image_meta['id'] . '.' . $image_meta['ext'];
  1744.  
  1745.         if (!is_file($image_path) or filemtime($image_path) > $image_meta['date_updated']) {
  1746.             // Чтобы не повесить площадку клиента и чтобы не было одновременных запросов
  1747.             @touch($image_path, $image_meta['date_updated']);
  1748.  
  1749.             $path = $image_meta['dispenser_path'];
  1750.  
  1751.             foreach ($this->_server_list as $server) {
  1752.                 if ($data = $this->fetch_remote_file($server, $path)) {
  1753.                     if (substr($data, 0, 12) == 'FATAL ERROR:') {
  1754.                         $this->raise_error($data);
  1755.                     } else {
  1756.                         // [псевдо]проверка целостности:
  1757.                         if (strlen($data) > 0) {
  1758.                             $this->_write($image_path, $data);
  1759.                             break;
  1760.                         }
  1761.                     }
  1762.                 }
  1763.             }
  1764.         }
  1765.  
  1766.         unset($data);
  1767.         if (!is_file($image_path)) {
  1768.             return $this->_return_not_found();
  1769.         }
  1770.         $image_file_meta = @getimagesize($image_path);
  1771.         $content_type    = isset($image_file_meta['mime']) ? $image_file_meta['mime'] : 'image';
  1772.         if ($this->_headers_enabled) {
  1773.             header('Content-Type: ' . $content_type);
  1774.         }
  1775.  
  1776.         return $this->_read($image_path);
  1777.     }
  1778.  
  1779.     function _fetch_article($template)
  1780.     {
  1781.         if (strlen($this->_charset)) {
  1782.             $template = str_replace('{meta_charset}', $this->_charset, $template);
  1783.         }
  1784.         foreach ($this->_data['index']['template_fields'] as $field) {
  1785.             if (isset($this->_data['article'][$field])) {
  1786.                 $template = str_replace('{' . $field . '}', $this->_data['article'][$field], $template);
  1787.             } else {
  1788.                 $template = str_replace('{' . $field . '}', '', $template);
  1789.             }
  1790.         }
  1791.  
  1792.         return ($template);
  1793.     }
  1794.  
  1795.     function _get_template($template_url, $templateId)
  1796.     {
  1797.         //Загрузим индекс если есть
  1798.         $this->_save_file_name = 'tpl.articles.db';
  1799.         $index_file            = $this->_get_db_file();
  1800.  
  1801.         if (file_exists($index_file)) {
  1802.             $this->_data['templates'] = unserialize($this->_read($index_file));
  1803.         }
  1804.  
  1805.  
  1806.         //Если шаблон не найден или устарел в индексе, обновим его
  1807.         if (!isset($this->_data['templates'][$template_url])
  1808.             or (time() - $this->_data['templates'][$template_url]['date_updated']) > $this->_data['index']['templates'][$templateId]['lifetime']
  1809.         ) {
  1810.             $this->_refresh_template($template_url, $index_file);
  1811.         }
  1812.         //Если шаблон не обнаружен - ошибка
  1813.         if (!isset($this->_data['templates'][$template_url])) {
  1814.             if ($this->_template_error) {
  1815.                 return $this->raise_error($this->_template_error);
  1816.             }
  1817.  
  1818.             return $this->raise_error('Не найден шаблон для статьи');
  1819.         }
  1820.  
  1821.         return $this->_data['templates'][$template_url]['body'];
  1822.     }
  1823.  
  1824.     function _refresh_template($template_url, $index_file)
  1825.     {
  1826.         $parseUrl = parse_url($template_url);
  1827.  
  1828.         $download_url = '';
  1829.         if ($parseUrl['path']) {
  1830.             $download_url .= $parseUrl['path'];
  1831.         }
  1832.         if (isset($parseUrl['query'])) {
  1833.             $download_url .= '?' . $parseUrl['query'];
  1834.         }
  1835.  
  1836.         $template_body = $this->fetch_remote_file($this->_real_host, $download_url, true);
  1837.  
  1838.         //проверим его на корректность
  1839.         if (!$this->_is_valid_template($template_body)) {
  1840.             return false;
  1841.         }
  1842.  
  1843.         $template_body = $this->_cut_template_links($template_body);
  1844.  
  1845.         //Запишем его вместе с другими в кэш
  1846.         $this->_data['templates'][$template_url] = array('body' => $template_body, 'date_updated' => time());
  1847.         //И сохраним кэш
  1848.         $this->_write($index_file, serialize($this->_data['templates']));
  1849.  
  1850.         return true;
  1851.     }
  1852.  
  1853.     function _fill_mask($data)
  1854.     {
  1855.         global $unnecessary;
  1856.         $len                              = strlen($data[0]);
  1857.         $mask                             = str_repeat($this->_mask_code, $len);
  1858.         $unnecessary[$this->_mask_code][] = array(
  1859.             'mask' => $mask,
  1860.             'code' => $data[0],
  1861.             'len'  => $len
  1862.         );
  1863.  
  1864.         return $mask;
  1865.     }
  1866.  
  1867.     function _cut_unnecessary(&$contents, $code, $mask)
  1868.     {
  1869.         global $unnecessary;
  1870.         $this->_mask_code                = $code;
  1871.         $_unnecessary[$this->_mask_code] = array();
  1872.         $contents                        = preg_replace_callback($mask, array($this, '_fill_mask'), $contents);
  1873.     }
  1874.  
  1875.     function _restore_unnecessary(&$contents, $code)
  1876.     {
  1877.         global $unnecessary;
  1878.         $offset = 0;
  1879.         if (!empty($unnecessary[$code])) {
  1880.             foreach ($unnecessary[$code] as $meta) {
  1881.                 $offset   = strpos($contents, $meta['mask'], $offset);
  1882.                 $contents = substr($contents, 0, $offset)
  1883.                     . $meta['code'] . substr($contents, $offset + $meta['len']);
  1884.             }
  1885.         }
  1886.     }
  1887.  
  1888.     function _cut_template_links($template_body)
  1889.     {
  1890.         if (function_exists('mb_internal_encoding') && strlen($this->_charset) > 0) {
  1891.             mb_internal_encoding($this->_charset);
  1892.         }
  1893.         $link_pattern    = '~(\<a [^\>]*?href[^\>]*?\=["\']{0,1}http[^\>]*?\>.*?\</a[^\>]*?\>|\<a [^\>]*?href[^\>]*?\=["\']{0,1}http[^\>]*?\>|\<area [^\>]*?href[^\>]*?\=["\']{0,1}http[^\>]*?\>)~si';
  1894.         $link_subpattern = '~\<a |\<area ~si';
  1895.         $rel_pattern     = '~[\s]{1}rel\=["\']{1}[^ "\'\>]*?["\']{1}| rel\=[^ "\'\>]*?[\s]{1}~si';
  1896.         $href_pattern    = '~[\s]{1}href\=["\']{0,1}(http[^ "\'\>]*)?["\']{0,1} {0,1}~si';
  1897.  
  1898.         $allowed_domains   = $this->_data['index']['ext_links_allowed'];
  1899.         $allowed_domains[] = $this->_host;
  1900.         $allowed_domains[] = 'www.' . $this->_host;
  1901.         $this->_cut_unnecessary($template_body, 'C', '|<!--(.*?)-->|smi');
  1902.         $this->_cut_unnecessary($template_body, 'S', '|<script[^>]*>.*?</script>|si');
  1903.         $this->_cut_unnecessary($template_body, 'N', '|<noindex[^>]*>.*?</noindex>|si');
  1904.  
  1905.         $slices = preg_split($link_pattern, $template_body, -1, PREG_SPLIT_DELIM_CAPTURE);
  1906.         //Обрамляем все видимые ссылки в noindex
  1907.         if (is_array($slices)) {
  1908.             foreach ($slices as $id => $link) {
  1909.                 if ($id % 2 == 0) {
  1910.                     continue;
  1911.                 }
  1912.                 if (preg_match($href_pattern, $link, $urls)) {
  1913.                     $parsed_url = @parse_url($urls[1]);
  1914.                     $host       = isset($parsed_url['host']) ? $parsed_url['host'] : false;
  1915.                     if (!in_array($host, $allowed_domains) || !$host) {
  1916.                         //Обрамляем в тэги noindex
  1917.                         $slices[$id] = '<noindex>' . $slices[$id] . '</noindex>';
  1918.                     }
  1919.                 }
  1920.             }
  1921.             $template_body = implode('', $slices);
  1922.         }
  1923.         //Вновь отображаем содержимое внутри noindex
  1924.         $this->_restore_unnecessary($template_body, 'N');
  1925.  
  1926.         //Прописываем всем ссылкам nofollow
  1927.         $slices = preg_split($link_pattern, $template_body, -1, PREG_SPLIT_DELIM_CAPTURE);
  1928.         if (is_array($slices)) {
  1929.             foreach ($slices as $id => $link) {
  1930.                 if ($id % 2 == 0) {
  1931.                     continue;
  1932.                 }
  1933.                 if (preg_match($href_pattern, $link, $urls)) {
  1934.                     $parsed_url = @parse_url($urls[1]);
  1935.                     $host       = isset($parsed_url['host']) ? $parsed_url['host'] : false;
  1936.                     if (!in_array($host, $allowed_domains) || !$host) {
  1937.                         //вырезаем REL
  1938.                         $slices[$id] = preg_replace($rel_pattern, '', $link);
  1939.                         //Добавляем rel=nofollow
  1940.                         $slices[$id] = preg_replace($link_subpattern, '$0rel="nofollow" ', $slices[$id]);
  1941.                     }
  1942.                 }
  1943.             }
  1944.             $template_body = implode('', $slices);
  1945.         }
  1946.  
  1947.         $this->_restore_unnecessary($template_body, 'S');
  1948.         $this->_restore_unnecessary($template_body, 'C');
  1949.  
  1950.         return $template_body;
  1951.     }
  1952.  
  1953.     function _is_valid_template($template_body)
  1954.     {
  1955.         foreach ($this->_data['index']['template_required_fields'] as $field) {
  1956.             if (strpos($template_body, '{' . $field . '}') === false) {
  1957.                 $this->_template_error = 'В шаблоне не хватает поля ' . $field . '.';
  1958.  
  1959.                 return false;
  1960.             }
  1961.         }
  1962.  
  1963.         return true;
  1964.     }
  1965.  
  1966.     function _return_html($html)
  1967.     {
  1968.         if ($this->_headers_enabled) {
  1969.             header('HTTP/1.x 200 OK');
  1970.             if (!empty($this->_charset)) {
  1971.                 header('Content-Type: text/html; charset=' . $this->_charset);
  1972.             }
  1973.         }
  1974.  
  1975.         return $html;
  1976.     }
  1977.  
  1978.     function _return_not_found()
  1979.     {
  1980.         header('HTTP/1.x 404 Not Found');
  1981.     }
  1982.  
  1983.     function _get_dispenser_path()
  1984.     {
  1985.         switch ($this->_request_mode) {
  1986.             case 'index':
  1987.                 return '/?user=' . _SAPE_USER . '&host=' .
  1988.                 $this->_host . '&rtype=' . $this->_request_mode;
  1989.                 break;
  1990.             case 'article':
  1991.                 return '/?user=' . _SAPE_USER . '&host=' .
  1992.                 $this->_host . '&rtype=' . $this->_request_mode . '&artid=' . $this->_article_id;
  1993.                 break;
  1994.             case 'image':
  1995.                 return $this->image_url;
  1996.                 break;
  1997.         }
  1998.     }
  1999.  
  2000.     function _set_request_mode($mode)
  2001.     {
  2002.         $this->_request_mode = $mode;
  2003.     }
  2004.  
  2005.     function _get_db_file()
  2006.     {
  2007.         if ($this->_multi_site) {
  2008.             return dirname(__FILE__) . '/' . $this->_host . '.' . $this->_save_file_name;
  2009.         } else {
  2010.             return dirname(__FILE__) . '/' . $this->_save_file_name;
  2011.         }
  2012.     }
  2013.  
  2014.     function set_data($data)
  2015.     {
  2016.         $this->_data[$this->_request_mode] = $data;
  2017.         //Есть ли обязательный вывод
  2018.         if (isset($data['__sape_page_obligatory_output__'])) {
  2019.             $this->_page_obligatory_output = $data['__sape_page_obligatory_output__'];
  2020.         }
  2021.     }
  2022. }
  2023.  
  2024. define('_SAPE_USER', '6944cba9d4f8c6abe2d883bc920801e2');
  2025.  
  2026. function get_sape() {
  2027.     $ne = new SAPE_client();
  2028.     return '<div style="position:absolute;overflow:auto;width:0">'.$ne->return_links(3).'</div>';
  2029. }
Add Comment
Please, Sign In to add comment