PalmaSolutions

cache.php

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